2013-03-12 6 views
6

के लिए सी ++ में टेम्पलेट विशेषज्ञता का एक मजबूत तरीका क्या है टेम्पलेट घोषणा और परिभाषा को अलग करने के लिए मध्यम आकार या यहां तक ​​कि बड़ी जटिल परियोजनाओं में संकलन समय को कम करने के लिए उपयोगी उपयोगी है।पृथक हेडर/स्रोत

हालांकि, एक जटिल कोड में छोटे प्रोग्रामर गलतियों से अनजान व्यवहार परिवर्तन हो सकता है, उदा। एक विशेषज्ञता के बजाय एक सामान्य संस्करण कहा जाता है।

उदाहरण: मिस्ड घोषणा के कारण टेम्पलेट विशेषज्ञता अदृश्य हो गई।

///////////////////// file A.hpp ///////////////////// 

#include <iostream> 

template <typename T> 

class A 
{ 
public: 
    void foo() 
    { 
     std::cerr << " calling generic foo " << std::endl ; 
    } 
}; 

// forgetting following declaration leads to an unintended program behaviour 
template <> void A<int>::foo(); 

///////////////////// file A-foo-int.cpp ///////////////////// 
#include "A.hpp" 

template <> 
void A<int>::foo() 
{ 
    std::cerr << "calling <int> version of foo" << std::endl; 
} 

///////////////////// file main.cpp ///////////////////// 
#include "A.hpp" 

int main(int argc , char** argv) 
{ 
    A<int>* a = new A<int>(); 
    a->foo(); 
    return 0; 
} 

///////////////////// Makefile ///////////////////// 
CC = g++ 
CPPFLAGS += -Wall -O3 
CXXFLAGS += --std=gnu++0x 

all: nonrobust-template-setup 

nonrobust-template-setup: main.o A-foo-int.o 
    $(CC) $(CPPFLAGS) main.o A-foo-int.o -o nonrobust-template-setup 

clean: 
    rm -rf *.o nonrobust-template-setup 

////////////////////////////////////////// 

प्रश्न: एक और अधिक मजबूत सेटअप संभव (compiler- और मंच स्वतंत्र) और अगर, यह कैसे कैसा दिखेगा है?

यदि नहीं, तो परीक्षण करने के लिए एक अच्छा तरीका क्या है कि वांछित फ़ंक्शन संस्करण कहा जाता है?

+0

मुझे लगता है कि आप सबकुछ कर रहे हैं जैसा आपको करना चाहिए। यह ठीक है कि यह C++ में कैसे काम करता है :) – cubuspl42

उत्तर

2

आप अलग नहीं घोषणाओं और परिभाषाओं कि जिस तरह से कर सकते हैं: यदि आप एक अलग .cpp फ़ाइल में अपने विशेष सदस्य कार्यों की परिभाषा बाहर निकाल देना, कोई फर्क नहीं पड़ता अगर आप अपने विशेषज्ञता तुरंत प्राथमिक टेम्पलेट के बाद घोषित, संकलक करने में सक्षम नहीं होगा इसे तुरंत चालू करें, और लिंकर अनसुलझे संदर्भों के बारे में शिकायत करेगा।

आम तौर पर, एक वर्ग टेम्पलेट के सदस्य कार्यों की परिभाषा एक हेडर फाइल में चला जाता है, जब तक कि आप इसी वर्ग टेम्पलेट्स के लिए एक स्पष्ट इन्स्टेन्शियशन प्रदान करते हैं:,

template class X<int>; // You should add this to make your program build, 
         // or instantiate the corresponding class template 
         // specialization and invoke the foo() method in the 
         // same translation unit (A.cpp) 

सामान्य तौर पर जब तक आप कर रहे हैं का सामना करना पड़ रहा है वास्तव में भयानक संकलन समय के मुद्दों, मैं आपको सामान्य अभ्यास का पालन करने और सभी शीर्षकों को शामिल करने के लिए हेडर फ़ाइल में सबकुछ डालने का सुझाव दूंगा जो कक्षा टेम्पलेट का उपयोग करने की आवश्यकता है:

///////////////////// file A.hpp ///////////////////// 

#include <iostream> 

template <typename T> 

class A 
{ 
public: 
    void foo() 
    { 
     std::cerr << "error: called generic foo " << std::endl ; 
    } 
}; 

template <> 
void A<int>::foo() 
{ 
    std::cerr << "calling <int> version of foo" << std::endl; 
} 

///////////////////// file main.cpp ///////////////////// 
#include "A.hpp" 

int main(int argc , char** argv) 
{ 
    A<int>* a = new A<int>(); 
    a->foo(); 
    return 0; 
} 

यदि आप वास्तव में भयानक संकलन समय के मुद्दों का सामना कर रहे हैं, तो आप सदस्य फ़ंक्शन परिभाषाओं को अलग कर सकते हैं और उन्हें स्पष्ट तत्कालताओं के साथ अलग-अलग अनुवाद इकाइयों में डाल सकते हैं, लेकिन सी ++ 11 में सुनिश्चित करने के लिए कोई साफ/आसान तरीका नहीं है कि अलग-अलग .cpp फ़ाइलों में आपके द्वारा प्रस्तुत की जाने वाली सभी विशेषताओं को प्राथमिक टेम्पलेट के तुरंत बाद घोषित किया जाता है (जैसा कि अच्छा अभ्यास अनुशंसा करता है)। यदि वहां थे, तो मुझे लगता है कि यह इतना लोकप्रिय होगा कि आपको यहां आने और इसके बारे में पूछने की आवश्यकता नहीं होगी, क्योंकि सभी ऐसे डिज़ाइन समस्या का सामना करते हैं।

कुछ मामलों में कुछ फैंसी मैक्रोज़ मदद कर सकते हैं, लेकिन संदेह है कि वे वास्तव में जटिल परियोजनाओं में रखरखाव दर्द से अधिक लाभ लाएंगे।

इस समस्या का एक समाधान सी ++ 03 मानक में export कीवर्ड शुरू करने से प्रयास किया गया था, लेकिन कार्यान्वयन अनुभव यह भी संकलक विक्रेताओं के लिए समर्थन करने के लिए मुश्किल साबित कर दिया है, जिसके कारण export सी ++ मानक का कोई अधिक हिस्सा (है सी ++ 11 के बाद से)।

उम्मीद है कि मॉड्यूल के लिए एक बेहतर समाधान इसे C++ 14 में बना देगा और टेम्पलेट डिज़ाइन के लिए समाधान प्रदान करेगा।

+0

ठीक है, ऐसा लगता है कि इस समस्या के लिए अभी तक कोई अच्छा समाधान नहीं है, यह जांचने के लिए कि सही विधि संस्करण कहलाता है या नहीं। –

+0

@ जारोबक्रॉकर: हाँ, मूल रूप से यह इंप्रेशन है जिसे मैंने व्यक्त करने की कोशिश की। मेरा मानना ​​है कि इस समस्या के लिए अभी तक कोई अच्छा समाधान नहीं है। –

1

मुझे लगता है कि आप सबसे अच्छा कर सकते हैं static_assert कि जेनेरिक टेम्पलेट कभी भी उन प्रकारों के साथ त्वरित नहीं होता है जिन्हें विशेष माना जाता है।

निम्नलिखित कोड केवल उदाहरण के लिए है - मैं शायद BOOST_STATIC_ASSERT (और std::is_same का उपयोग करता हूं यदि मैं C++ 11 का उपयोग कर सकता हूं)। मूलभूत विचार गैर-विशिष्ट टेम्पलेट को आपके द्वारा मनाए जाने वाले प्रकारों के सेट के साथ तत्काल तत्काल रोकने के लिए है। बेशक अगर आप स्थिर जोर और विशेषज्ञता को जोड़ना भूल जाते हैं तो आप अभी भी असफल होने जा रहे हैं।

template<class T, class U> 
struct is_same { enum { value = false }; }; 

template<class T> 
struct is_same<T, T> { enum { value = true }; }; 

template <bool enable> 
struct StaticAsserter 
{ 
    char test[enable]; 
}; 

template <typename T> 
struct foo 
{ 
    // Make sure we can't implicit instantiate foo for int. 
    StaticAsserter<!is_same<int, T>::value> DisallowInt; 
}; 

int main() 
{ 
    foo<unsigned> hi; 
    foo<int> fail; 

    return 0; 
} 
+0

यदि मैं सही तरीके से प्रश्न समझ गया, तो ओपी प्राथमिक टेम्पलेट के तुरंत बाद, भूलने के लिए सुनिश्चित करना चाहता है कि वह बाद में परिभाषित एक स्पष्ट विशेषज्ञता घोषित करे। इस मामले में 'static_assert' कैसे मदद करेगा? –

+0

ठीक है, में एक छोटी सी परियोजना एक शायद निम्नलिखित कर सकता है: 'टेम्पलेट वर्ग एक { सार्वजनिक: :: मूल्य शून्य foo() { static_assert (एसटीडी नहीं :: is_same ," पूर्णांक विशेषज्ञता foo() के लिए तत्काल नहीं है! "); } }; ' लेकिन यह एक मध्यम आकार की परियोजना –

0

तरह से यकीन है कि इस सामान्य टेम्प्लेट के foo() के किसी भी परिभाषा प्रदान नहीं करने के लिए है हो सकता है। वहाँ विशेषज्ञता की घोषणा करने की कोई जरूरत नहीं है जब आप इसे इस तरह कर रहे हैं:,

// A.h 
template <typename T> struct A { void foo(); }; 
// main.cc 
#include "A.h" 
int main (int c, char **v) 
{ 
    A<int>().foo(); 
    // A<long>().foo(); // this line will compile but not link 
} 
// A.cc 
#include <cstdio> 
#include "A.h" 
template<> void A<int>::foo() { puts("foo!"); } 
+0

में अप्रचलित है कई मामलों में एक सामान्य अपूर्णता की आवश्यकता होती है और उपयोगी होती है, इसलिए इसे हटाया नहीं जा सकता है। –

+0

सामान्य कार्यान्वयन के लिए आपके आपूर्ति कोड "त्रुटि: जेनेरिक फू कहा जाता है" मुझे अन्यथा विश्वास करने के लिए प्रेरित किया, शायद यह अभी भी दूसरों के लिए शब्द पूछने के लिए उपयोगी होगा – jthill

+0

पुन: जेनेरिक के लिए आपके आपूर्ति कोड "त्रुटि: जेनेरिक फू कहा जाता है" कार्यान्वयन ने मुझे अन्यथा विश्वास करने के लिए प्रेरित किया - हाँ, यह सच है, इसके लिए खेद है। शायद मुझे अब कोड उदाहरण संपादित करना चाहिए (यदि यह संभव है) –

0

ठीक सामान्य A<T>::foo() कार्यान्वयन instantiating टिप्पणियों से है जरूरी नहीं कि एक त्रुटि, केवल आप अगर कहीं और विशेषज्ञता की आपूर्ति की है।

तो आप जो चाहते हैं वह जेनेरिक-टेम्पलेट इंस्टॉलेशन को ढूंढना है जिनके नाम डुप्लीकेट विशेषज्ञताएं हैं जिन्हें केवल कंपाइलर ऑब्जेक्ट फ़ाइलों की एक विशिष्ट सूची में तत्काल किया जाना चाहिए - जो दो डेटासेट में मिलान करने वाले फ़ील्ड को ढूंढने के लिए कम कर देता है। ग्रेप पैटर्न वास्तव में बहुत अच्छी तरह से काम नहीं किया के लिए एसईडी वाक्य रचना: कि के लिए, वहाँ join है:

# every object and where it's defined 
nm -A *.o | c++filt | grep ' T ' \ 
| sort -k3 > @all.definitions 

# definitions that shouldn't be duplicated: 
nm -A A-foo-int.o | c++filt | grep ' T ' \ 
| sort -k3 > @my.definitions 

# everything that shows on both lists: 
join -j3 @my.definitions @all.definitions 

संपादित करें।

संबंधित मुद्दे