क्या रन-टाइम में तय करना संभव है कि कौन से टेम्पलेट फ़ंक्शन कॉल करें? कुछ की तरह:टेम्पलेट कार्यों की गतिशील प्रेषण?
template<int I>
struct A {
static void foo() {/*...*/}
};
void bar(int i) {
A<i>::f(); // <-- ???
}
क्या रन-टाइम में तय करना संभव है कि कौन से टेम्पलेट फ़ंक्शन कॉल करें? कुछ की तरह:टेम्पलेट कार्यों की गतिशील प्रेषण?
template<int I>
struct A {
static void foo() {/*...*/}
};
void bar(int i) {
A<i>::f(); // <-- ???
}
टेम्पलेट्स से निपटने के दौरान संकलन समय और रनटाइम को पुल करने के लिए एक सामान्य 'चाल' एक प्रकार का प्रकार जा रहा है। उदाहरण के लिए जेनेरिक इमेज लाइब्रेरी (बूस्ट.जीआईएल या स्टैंडअलोन के रूप में उपलब्ध) यही है। यह आमतौर पर का रूप ले लेता:
typedef boost::variant<T, U, V> variant_type;
variant_type variant = /* type is picked at runtime */
boost::apply_visitor(visitor(), variant);
जहां visitor
एक बहुरूपी functor है कि बस आगे टेम्पलेट के लिए:
struct visitor: boost::static_visitor<> {
template<typename T>
void
operator()(T const& t) const
{ foo(t); } // the real work is in template<typename T> void foo(T const&);
};
यह अच्छा डिजाइन किया गया है कि प्रकार की सूची है कि टेम्पलेट होगा/कर सकते हैं तत्काल (यहां, variant_type
प्रकार समानार्थी) के साथ तत्काल होना चाहिए शेष कोड के साथ नहीं है। boost::make_variant_over
जैसे मेटाफंक्शन भी उपयोग की जाने वाली प्रकारों की सूची पर गणना की अनुमति देता है।
चूंकि यह तकनीक गैर-प्रकार के पैरामीटर के लिए उपलब्ध नहीं है, इसलिए आपको हाथ से विज़िट को 'अनलॉक' करने की आवश्यकता है, दुर्भाग्य से इसका मतलब है कि कोड पठनीय/रखरखाव योग्य नहीं है।
void
bar(int i) {
switch(i) {
case 0: A<0>::f(); break;
case 1: A<1>::f(); break;
case 2: A<2>::f(); break;
default:
// handle
}
}
ऊपर स्विच में पुनरावृत्ति से निपटने के लिए हमेशा की तरह करने के लिए (ab) है पूर्वप्रक्रमक का उपयोग करें। एक (untested) Boost.Preprocessor का उपयोग कर उदाहरण:
#ifndef LIMIT
#define LIMIT 20 // 'reasonable' default if nothing is supplied at build time
#endif
#define PASTE(rep, n, _) case n: A<n>::f(); break;
void
bar(int i) {
switch(i) {
BOOST_PP_REPEAT(LIMIT, PASTE, _)
default:
// handle
}
}
#undef PASTE
#undef LIMIT
बेहतर अच्छा, स्वयं का दस्तावेजीकरण नाम खोजने के LIMIT
के लिए (PASTE
या तो के लिए चोट नहीं होगा), और सिर्फ एक साइट के लिए ऊपर दिए गए कोड पीढ़ी की सीमा। डेविड के समाधान और अपनी टिप्पणी से
भवन:
template<int... Indices>
struct indices {
typedef indices<Indices..., sizeof...(Indices)> next;
};
template<int N>
struct build_indices {
typedef typename build_indices<N - 1>::type::next type;
};
template<>
struct build_indices<0> {
typedef indices<> type;
};
template<int... Indices>
void
bar(int i, indices<Indices...>)
{
static void (*lookup[])() = { &A<Indices>::f... };
lookup[i]();
}
तो bar
कॉल करने के लिए: bar(i, typename build_indices<N>::type())
जहां N
अपने निरंतर समय निरंतर होगा, sizeof...(something)
। आपको लगता है कि कॉल की 'बदसूरती' को छिपाने के लिए एक परत जोड़ सकते हैं:
template<int N>
void
bar(int i)
{ bar(i, typename build_indices<N>::type()); }
जो bar<N>(i)
के रूप में कहा जाता है।
नहीं
टेम्पलेट्स समय बहुरूपता संकलन समय बहुरूपता नहीं चला लागू।
नहीं, टेम्पलेट संकलन-समय सुविधा हैं, और i
संकलन समय पर ज्ञात नहीं है, इसलिए यह असंभव है। A<I>::foo()
को A::foo(i)
जैसे कुछ के लिए अनुकूलित किया जाना चाहिए।
मुझे इसके बारे में पता है। अगर मैं कुछ कामकाज कर रहा हूं तो मैं घूमता हूं। – Predrag
वर्कअराउंड या तो संकलन-समय (जैसे @ iammilind के समाधान) पर जाना जाता है, या 'ए :: foo' को संकलन-समय तर्क (मेरा समाधान) की आवश्यकता नहीं है। – tenfour
एक तीसरा तरीका है (यदि सीमित संख्या में विकल्प हैं): संकलन समय पर सभी कार्यों को तुरंत चालू करके लुकअप टेबल बनाएं, और फिर रनटाइम पर उनमें से किसी एक को प्रेषित करें। मैंने पहले इसका इस्तेमाल किया है और यह दर्दनाक है, लेकिन व्यवहार्य है। –
टेम्पलेट तर्क संकलन समय पर जाना जाना चाहिए। इसलिए A<i>::foo()
पास करने के लिए कंपाइलर का कोई तरीका नहीं है।
आप तो आस-पास काम चाहते हैं तो आप bar()
भी एक template
करना है:
template<int i>
void bar() {
A<i>::f(); // ok
}
के लिए, आप संकलन समय पर bar()
को तर्क पता होना चाहिए।
आप जो करना चाहते हैं उसके आधार पर (यानी सीमित सीमित तात्कालिकताएं हैं जिन्हें आप उपयोग करना चाहते हैं?) आप एक लुकअप टेबल बना सकते हैं और फिर उस गतिशील रूप से उपयोग कर सकते हैं। एक पूरी तरह से मैनुअल दृष्टिकोण के लिए, विकल्प 0, 1, 2, और 3 के साथ, आप कर सकता है:
void bar(int i) {
static void (*lookup[])(void) = { &A<0>::foo, &A<1>::foo, &A<2>::foo, &A<3>::foo };
lookup[i]();
}
बेशक
, मैं उदाहरण के लिए सबसे आसान विकल्प चुना है। यदि आपको जिस नंबर की आवश्यकता है वह निरंतर या शून्य आधारित नहीं है, तो आप किसी सरणी के बजाय std::map<int, void (*)(void) >
पसंद कर सकते हैं। यदि आप जिन विभिन्न विकल्पों का उपयोग करना चाहते हैं, वे बड़ी संख्या में हैं, तो आप मैन्युअल रूप से उन सभी को टाइप करने के बजाय टेम्पलेट्स को स्वचालित रूप से intantiate करने के लिए कोड जोड़ना चाहेंगे ... लेकिन आपको यह विचार करना होगा कि टेम्पलेट के प्रत्येक तत्कालता ने एक नया बनाया है फ़ंक्शन, और आप यह जांचना चाहेंगे कि आपको वास्तव में इसकी आवश्यकता है या नहीं।
EDIT: मैंने post को केवल सी ++ 03 सुविधाओं का उपयोग करके उसी प्रारंभिक क्रियान्वयन को लिखा है, यह एक उत्तर के लिए बहुत लंबा लग रहा था।
ल्यूक डैंटन ने एक दिलचस्प उत्तर here लिखा है जिसमें सी ++ 0x संरचनाओं का उपयोग करके लुकअप टेबल की अन्य चीजों को प्रारंभ करना शामिल है। मुझे उस समाधान से काफी पसंद नहीं है कि यह एक अतिरिक्त तर्क की आवश्यकता के लिए इंटरफ़ेस को बदलता है, लेकिन इसे आसानी से मध्यवर्ती प्रेषक के माध्यम से हल किया जा सकता है।
यही वह है जिसे मैं ढूंढ रहा था। क्या आप मुझे दिखा सकते हैं कि सरणी या मानचित्र को स्वचालित रूप से कैसे प्रारंभ करें (या मुझे कहीं भी निर्देशित करें)? – Predrag
@Predrag: आप कितने अलग मूल्य चाहते हैं? क्या वे लगातार हैं? शून्य आधारित? यह भी ध्यान रखें कि प्रत्येक तात्कालिकता एक नया कार्य करेगा, और बदले में इसका मतलब है कि यह आपकी बाइनरी बढ़ेगा ... –
वे शून्य-आधारित हैं और मैं संकलित समय पर जानता हूं कि उनमें से कितने मौजूद हैं। – Predrag
क्या मैं संकलक को मेरे लिए अनलोल कर सकता हूं अगर मुझे पता है (संकलन समय पर) कितने मामले हैं? – Predrag
दर्द होने के लिए खेद है ... लेकिन मामलों की संख्या का मेरा ज्ञान 'आकार ...()' ऑपरेटर से आता है। मुझे डर है कि प्रीप्रोसेसर उस मामले में मदद नहीं करेगा। क्या आप ऐसा कुछ सोच सकते हैं जो उस बाधा से मेरी मदद कर सके? – Predrag
@Predrag एक ऊपरी सीमा को काफी बड़ा उठाएं और आप इसके साथ सहज हैं। –