2009-02-17 10 views
26

मुझे ऐसी लाइब्रेरी से निपटना है जिसमें कई टेम्पलेटेड क्लासेस शामिल हैं, जो निश्चित रूप से हेडर फाइलों में लागू होते हैं। अब मैं असहनीय लंबे संकलन समय को कम करने का एक तरीका खोजने की कोशिश कर रहा हूं जो इस तथ्य से आती है कि मुझे प्रत्येक में और मेरी संकलन इकाइयों में से एक में पूरी लाइब्रेरी शामिल करना है।टेम्पलेट्स: संकलन समय को कम करने के लिए आगे की घोषणाओं का उपयोग करें?

टेम्पलेट्स के बावजूद आगे की घोषणाओं का एक संभावित उपयोग कर रहा है? मैं नीचे दिए गए उदाहरण की तर्ज पर कुछ कोशिश कर रहा हूं, जहां मैंने #include <vector> को एक उदाहरण के रूप में प्राप्त करने का प्रयास किया, लेकिन यह मुझे एक लिंकर त्रुटि दे रहा है क्योंकि push_back अपरिभाषित है।

#include <iostream> 

namespace std { 
    template<class T> 
    class vector { 
    public: 
    void push_back(const T& t); 
    }; 
} 

int main(int argc, char** argv) { 
    std::vector<int>* vec = new std::vector<int>(); 
    vec->push_back(3); 
    delete vec; 
    return EXIT_SUCCESS; 
} 

$ g++ fwddecl.cpp 
ccuqbCmp.o(.text+0x140): In function `main': 
: undefined reference to `std::vector<int>::push_back(int const&)' 
collect2: ld returned 1 exit status 

मैं precompiled हेडर एक बार कोशिश की, लेकिन वह सब पर संकलन बार परिवर्तन नहीं किया (मुझे यकीन है कि वे वास्तव में असली हेडर के बजाय लाद दिए गए कर देते थे)। लेकिन अगर आप सभी कहते हैं कि प्रीकंपिल्ड हेडर को जाने का रास्ता होना चाहिए तो मैं इसे फिर से कोशिश करूंगा।

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

अद्यतन 2: क्या उपर्युक्त उदाहरण वास्तव में संकलित और ठीक से लिंक करने का कोई तरीका है? लोगान -fno-implicit-templates का उपयोग करने का सुझाव देते हैं और template class std::vector<int> कहीं भी डालते हैं, संभावित रूप से .cpp फ़ाइल में जो -fno-implicit-templates के साथ संकलित हो जाता है, लेकिन मुझे अभी भी लिंकर त्रुटियां मिलती हैं। दोबारा, मैं यह समझने की कोशिश कर रहा हूं कि यह std::vector के लिए कैसे काम करता है ताकि मैं इसे उन templated कक्षाओं पर लागू कर सकूं जो मैं वास्तव में उपयोग कर रहा हूं।

+3

, आप आगे कुछ भी घोषित नहीं किया है। आपने जो कुछ किया है वह नामस्थान std में वेक्टर नामक एक टेम्पलेट वर्ग बना था। फिर आप उस पुश_बैक विधि को परिभाषित करने में विफल रहे जो आपने इसमें घोषित किया था। इसलिए लिंकर त्रुटि। –

+0

दूसरा इवान का स्पष्टीकरण क्यों आगे घोषित करना std :: vector काम नहीं कर रहा है (आप कोण ब्रैकेट के अंदर कम से कम एक तर्क खो रहे हैं)। अपने द्वारा लिखे गए टेम्पलेट वर्ग का उपयोग करने का प्रयास करें, जिसे आप जानते हैं कि कोई डिफ़ॉल्ट टेम्पलेट तर्क नहीं है। –

उत्तर

35

आप इस तरह के वर्गों के "भागों" घोषित नहीं कर सकते हैं। यहां तक ​​कि यदि आप कर सकते हैं, तो आपको अभी भी कोड को तुरंत चालू करने की आवश्यकता होगी ताकि आप इसके खिलाफ लिंक कर सकें। इसे संभालने के तरीके हैं, आप अपने आप को सामान्य कंटेनर (जैसे वेक्टर) के तत्कालताओं के साथ एक छोटी लाइब्रेरी बना सकते हैं और उन्हें लिंक कर सकते हैं। फिर आपको केवल संकलन करने की आवश्यकता होगी। वेक्टर <int> एक बार। इसे लागू करने के आप कम से कम यह सोचते हैं आप जी के साथ चिपके हुए कर रहे हैं ++ और स्पष्ट रूप से template class std::vector<int>


तो, एक असली काम कर उदाहरण के साथ अपने lib में टेम्पलेट का दृष्टांत, -fno-implicit-templates की तरह कुछ का उपयोग करना होगा। यहाँ मैं 2 फ़ाइलें, a.cpp और b.cpp है

a.cpp:

#include <vector> // still need to know the interface 
#include <cstdlib> 

int main(int argc, char **argv) { 
    std::vector<int>* vec = new std::vector<int>(); 
    vec->push_back(3); 
    delete vec; 
    return EXIT_SUCCESS; 
} 

तो अब मैं -fno-implicit-templates साथ a.cpp संकलन कर सकते हैं:

g++ -fno-implicit-templates -c a.cpp 

यह मैं दे देंगे Ao अगर मैं तो ए.ओ. को जोड़ने का प्रयास करता हूं तो मुझे मिलता है:

g++ a.o 
/usr/bin/ld: Undefined symbols: 
std::vector<int, std::allocator<int> >::_M_insert_aux(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&) 
void std::_Destroy<int*, std::allocator<int> >(int*, int*, std::allocator<int>) 
collect2: ld returned 1 exit status 

कोई अच्छा नहीं। तो हम b.cpp:

#include <vector> 
template class std::vector<int>; 
template void std::_Destroy(int*,int*, std::allocator<int>); 
template void std::__uninitialized_fill_n_a(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, unsigned long, int const&, std::allocator<int>); 
template void std::__uninitialized_fill_n_a(int*, unsigned long, int const&, std::allocator<int>); 
template void std::fill(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&); 
template __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > std::fill_n(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, unsigned long, int const&); 
template int* std::fill_n(int*, unsigned long, int const&); 
template void std::_Destroy(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, std::allocator<int>); 

अब आप स्वयं से कह रहे हैं, ये सभी अतिरिक्त टेम्पलेट चीज़ें कहां से आईं? मैं template class std::vector<int> देखता हूं और यह ठीक है, लेकिन बाकी के बारे में क्या?खैर संक्षिप्त जवाब यह है कि, इन चीजों के कार्यान्वयन की आवश्यकता थोड़ी गन्दा होती है, और जब आप मैन्युअल रूप से उन्हें तुरंत चालू करते हैं, तो विस्तार से कुछ गड़बड़ी खत्म हो जाती है। आप शायद सोच रहे हैं कि मैंने यह भी पता लगाया कि मुझे तत्काल आवश्यकता के लिए क्या चाहिए। खैर मैंने लिंकर त्रुटियों का उपयोग किया;)।

तो अब हम b.cpp संकलन

g++ -fno-implicit-templates -c b.cpp 

और हम b.o. मिल a.o लिंक करना और b.o हम

g++ a.o b.o 

हुर्रे, कोई लिंकर त्रुटियों मिल सकती है।

तो, अपने अपडेट किए गए प्रश्न के बारे में कुछ विवरण प्राप्त करने के लिए, यदि यह घर बनाने वाली कक्षा है तो यह आवश्यक रूप से यह गन्दा होना आवश्यक नहीं है। उदाहरण के लिए, आप इंटरफ़ेस को कार्यान्वयन से अलग कर सकते हैं, उदा। कहते हैं कि हम ch, c.cpp है, a.cpp और b.cpp

ch

template<typename T> 
class MyExample { 
    T m_t; 
    MyExample(const T& t); 
    T get(); 
    void set(const T& t); 
}; 

c.cpp

template<typename T> 
MyExample<T>::MyExample(const T& t) : m_t(t) {} 
template<typename T> 
T MyExample<T>::get() { return m_t; } 
template<typename T> 
void MyExample<T>::set(const T& t) { m_t = t; } 

a.cpp

#include "c.h" // only need interface 
#include <iostream> 
int main() { 
    MyExample<int> x(10); 
    std::cout << x.get() << std::endl; 
    x.set(9); 
    std::cout << x.get() << std::endl; 
    return EXIT_SUCCESS; 
} 
के अलावा

b.cpp, "लाइब्रेरी":

#include "c.h" // need interface 
#include "c.cpp" // need implementation to actually instantiate it 
template class MyExample<int>; 

अब आप एक बार बी.पी.पी. को संकलित करते हैं। जब a.cpp आपको बदलता है तो उसे पुनः संयोजित करने की आवश्यकता होती है और b.o. में लिंक होता है।

+0

उत्कृष्ट जवाब! चरण-दर-चरण चलने के लिए धन्यवाद :) –

+0

+1 लंबे टेम्पलेट निर्माण समय मुझे पागल कर रहे हैं! टेम्पलेट-बिल्ड-नरक से बाहर निकलने के तरीके के विस्तृत उदाहरण के लिए tyvm ... – kfmfe04

3

<iosfwd> है जो आपको iostream कक्षाओं के लिए कुछ आगे की घोषणा देगा, लेकिन सामान्य रूप से आप उन्हें घोषित करने के संदर्भ में एसटीएल टेम्पलेट्स के बारे में बहुत कुछ नहीं कर सकते हैं।

प्री-कंपाइल हेडर जाने का रास्ता हैं। पहली बार जब आप उन्हें संकलित करते हैं तो आपको कोई भी गति-वृद्धि दिखाई नहीं देगी, लेकिन जब भी आप प्रीकंपील्ड हेडर (या इसमें शामिल कुछ भी) संशोधित करते हैं, तो आपको केवल उस कीमत का भुगतान करना चाहिए। संकलन को तेज करने के बारे में अन्य विचारों के लिए

See this question अन्य विचारों के लिए।

6

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

+0

यदि आप सेमेक का उपयोग कर रहे हैं, तो कोटेयर प्लगइन आपके लिए पीसीएच की देखभाल करने के लिए अद्भुत है। https://github.com/sakra/cotire यह उपयोग करने में बहुत आसान है और "बस काम करता है" – xaxxon

22

आगे घोषणाओं आप ऐसा करते हैं:

template <class T> class vector; 

तो फिर तुम वेक्टर को परिभाषित (vector के हेडर फाइल को शामिल किए बिना) के बिना vector<whatever> करने के लिए संदर्भ और संकेत दिए गए घोषणा कर सकते हैं। यह नियमित (गैर-टेम्पलेट) कक्षाओं की अगली घोषणाओं के समान कार्य करता है।

विशेष रूप से टेम्पलेट्स के साथ समस्या यह है कि आपको आमतौर पर केवल कक्षा घोषणा की आवश्यकता नहीं होती है बल्कि आपकी हेडर फ़ाइल में सभी विधि परिभाषाएं भी होती हैं (ताकि संकलक आवश्यक टेम्पलेट को चालू कर सके)।स्पष्ट टेम्पलेट त्वरण (जिसे आप -fno-implicit-templates के उपयोग के लिए मजबूर कर सकते हैं) इसके लिए एक समाधान है; आप (जो आप शामिल करने के लिए की जरूरत नहीं है एक -inl.h हेडर फाइल में, या, Google Style Guide के उदाहरण का अनुसरण) एक स्रोत फ़ाइल में अपने विधि परिभाषाओं रख सकते हैं तो स्पष्ट रूप से उन्हें इस तरह का दृष्टांत:

template <class int> class vector; 

ध्यान दें कि इससे आपको लाभ उठाने के लिए वास्तव में -fno-implicit-templates की आवश्यकता नहीं है; संकलक चुपचाप किसी भी टेम्पलेट को तत्काल करने से बचने के लिए इसकी परिभाषा नहीं करेगा, इस धारणा पर कि लिंकर इसे बाद में समझ लेगा। और -fno-implicit-templatesसभी टेम्पलेट्स का उपयोग करना कठिन होगा (केवल समय लेने वाले वाले नहीं), इसलिए मैं इसकी अनुशंसा नहीं करता।

आपके उदाहरण कोड के साथ समस्या यह है कि आप सही std::vector कक्षा घोषित करने के लिए आगे नहीं हैं। <vector> सहित, आप अपना खुद का, गैर मानक vector कक्षा बना रहे हैं, और आप कभी भी push_back को परिभाषित नहीं कर रहे हैं, इसलिए संकलक को तत्काल करने के लिए कुछ भी नहीं है।

मैंने प्रीकंपील्ड हेडर का बहुत प्रभावशाली उपयोग किया है; मुझे यकीन नहीं है कि उन्होंने आपकी मदद क्यों नहीं की। आपने अपने सभी गैर-बदलते शीर्षलेखों को एक all.h में रखा है, इसे पहले से संकलित किया है, और strace के साथ सत्यापित किया है या इसी तरह all.h.pch लोड किया गया था और व्यक्तिगत हेडर फाइलें नहीं थीं? (कैसे strace उपयोग करने के लिए:। open( कॉल के लिए खोज जो फ़ाइलों को पढ़ने जा रहा है देखने के लिए बजाय g++ mytest.cc चलाने का, चलाने strace -o strace.out g++ mytest.cc, तो एक पाठ संपादक में strace.out देख सकते हैं और) अपने उदाहरण में

+2

आप एक अच्छी बात wrt -fno-implicit-templates लाते हैं। जब समस्या संकलन गति होती है, तो मुझे "आगे घोषित" टेम्पलेट्स के विचार से विचलित हो गया। –

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