2012-04-15 21 views
7

सी ++ टेम्पलेट आम तौर पर ब्लोट के निर्माता को समेकित होते हैं, और शिम विचार बिल्कुल वैसा ही होता है: टेम्पलेट को नियमित फ़ंक्शन पर केवल एक पतला आवरण बनाते हैं। यह ब्लोट पर कटौती करने का एक बहुत अच्छा तरीका है। मैं यह इसलिए की तरह उपयोग कर सकते हैंविविध टेम्पलेट्स में शिम्स का उपयोग करने के लिए अधिक संक्षिप्त तरीका?

// 
// Shim interface 
// 
struct Interface { 
    virtual void print(std::ostream& out) const = 0; 
}; // struct Interface 

std::ostream& operator<<(std::ostream& out, Interface const& i) { 
    i.print(out); 
    return out; 
} 

template <typename T> 
struct IT: public Interface { 
    IT(T const& t): _t(t) {} 
    virtual void print(std::ostream& out) const { out << _t; } 
    T const& _t; 
}; 

template <typename T> 
IT<T> shim(T const& t) { return IT<T>(t); } 

अब,:

उदाहरण के लिए, एक साधारण शिम का उपयोग करते हैं

void print_impl(Interface const& t); 

template <typename T> 
void print(T const& t) { print_impl(shim(t)); } 

और कोई बात नहीं print_impl कैसे कार्यान्वित किया जाता है, print बहुत हल्के रहता है और होना चाहिए रेखांकित किया जाना चाहिए। बहुत आसान।


सी ++ 11 तथापि variadic टेम्पलेट्स परिचय देता है। तब सामान्य आग्रह सी ++ 11 वैरिएडिक टेम्पलेट्स के साथ सभी असुरक्षित सी-वेरिएडिक्स को पुन: कार्यान्वित करना है, यहां तक ​​कि विकिपीडिया भी a printf implementation के साथ सुझाव देता है। , प्रकार है कि आप वहाँ 3 पैरामीटर प्रिंट, आदि ... यह आसान होगा निर्दिष्ट कर सकते हैं, तो केवल हम इस प्रोटोटाइप के साथ एक समारोह था:

दुर्भाग्य से, विकिपीडिया के कार्यान्वयन स्थितीय तर्क के साथ सौदा नहीं है

void printf_impl(char const* format, Interface const* array, size_t size); 

या इसी तरह के।

अब, कैसे हम मूल इंटरफ़ेस से पाटने है:

template <typename... T> 
void printf(char const* format, T const&... t); 

ऊपर हस्ताक्षर करने के लिए ?

शिम्स के साथ एक कठिनाई यह है कि वे स्थिरांक-रेफरी व्यवहार के लिए बाध्य (गतिशील स्मृति को आबंटित किए बिना सिर्फ पर्याप्त बनाई गई अस्थायी आवरण के जीवन का विस्तार करने के लिए पर भरोसा करते हैं, अगर वे किया था वे नहीं सस्ता होगा)।

यह बाध्यकारी + एक चरण में सरणी परिवर्तन प्राप्त करना मुश्किल लगता है। विशेष रूप से क्योंकि भाषा में संदर्भों (संदर्भों के सूचक) के सरणी की अनुमति नहीं है।


मैं एक समाधान की एक शुरुआत है, उन दिलचस्पी के लिए:

// 
// printf (or it could be!) 
// 
void printf_impl(char const*, Interface const** array, size_t size) { 
    for (size_t i = 0; i != size; ++i) { std::cout << *(array[i]); } 
    std::cout << "\n"; 
} 

template <typename... T> 
void printf_bridge(char const* format, T const&... t) { 
    Interface const* array[sizeof...(t)] = { (&t)... }; 
    printf_impl(format, array, sizeof...(t)); 
} 

template <typename... T> 
void printf(char const* format, T const&... t) { 
    printf_bridge(format, ((Interface const&)shim(t))...); 
} 

हालांकि आप एक अनुपूरक कदम है, जो थोड़ा कष्टप्रद है की शुरूआत नोट करती है। फिर भी, it appears to work

यदि कोई प्रस्ताव देने के लिए बेहतर कार्यान्वयन करता तो मैं बहुत आभारी रहूंगा।


@Potatoswatter जो helps a bit (कोई वहाँ रेंज के लिए) प्रारंभकर्ता सूचियों, का उपयोग करते हुए सुझाव दिया।

void printf_impl(char const*, std::initializer_list<Interface const*> array) { 
    for (Interface const* e: list) { std::cout << *e; } 
    std::cout << "\n"; 
} 

template <typename... T> 
void printf_bridge(char const* format, T const&... t) { 
    printf_impl(format, {(&t)...}); 
} 

लेकिन अभी भी मध्यवर्ती फ़ंक्शन समस्या को हल नहीं करता है।

+2

ओह नोस, तेह ब्लोट्स! मेरे निष्पादन योग्य में कुछ अतिरिक्त केबी मेरी दुनिया का अंत है। – Puppy

+3

@DeadMG इस तरह की चीज वास्तव में मेगाबाइट्स को बचा सकती है। – Potatoswatter

+0

मुझे नहीं लगता कि 'आईटी' में सदस्य चर 'टी कॉन्स & _t;' 'शिम() 'फ़ंक्शन से अस्थायी रूप से लौटाया गया जीवनकाल बढ़ाता है। जब कन्स्ट्रक्टर लौटाता है, तो '_t' विनाशकारी वस्तु को संदर्भित करता है। – Nawaz

उत्तर

3

टाइप पैरामीटरेशन को खत्म करने पर हल्के वजन को बनाना। आपकी शिम संभावित रूप से out << _t अभिव्यक्ति के साथ कुछ भारी कर्तव्य को तुरंत चालू करती है, इसलिए यह वास्तव में एक अच्छा उदाहरण नहीं हो सकता है।

सी varargs intptr_t पर सब कुछ कास्टिंग करके समस्या को संभालती है। यदि आप केवल सी printf कार्यक्षमता को दोहराना चाहते हैं, तो आप reinterpret_cast और initializer_list के साथ ऐसा ही कर सकते हैं।

template <typename... T> 
void printf(char const* format, T const&... t) { 
    printf_impl(format, { reinterpret_cast<std::intptr_t>(t) ... }); 
} 

यह स्पष्ट रूप से उप-स्थानिक है, लेकिन शिम्स स्वाभाविक रूप से सीमित हैं। यदि आप चाहें तो initializer_list में आप पॉलिमॉर्फिक प्रकारों के साथ कुछ और कर सकते हैं।

किसी भी मामले में, यह वास्तव में initializer_list के लिए है। इसे केवल एक ब्रसेड-इनिट-लिस्ट से बनाया जा सकता है, जिससे इसका आकार संकलन-समय स्थिर हो जाता है। लेकिन आकार को केवल रनटाइम निरंतर के रूप में पढ़ा जा सकता है। तो इसका एकमात्र व्यावहारिक उपयोग केवल सामान्य लंबाई, परिवर्तनीय-लंबाई कार्यान्वयन के लिए सूची लंबाई में भिन्न टेम्पलेट्स को फ़नल करना है।

में जोड़े कि initializer_list तर्क के जीवनकाल अर्थ विज्ञान - वस्तुओं ढेर पर एक सन्निहित सरणी में बनाया है और मर जाते हैं जब समारोह कॉल बयान समाप्त हो जाती है कर रहे हैं - और initializer_list<varargs> की तरह एक बहुत लग रहा है! (संपादित करें: या अपने समाधान है, जो मैं अब वास्तव में वापस चला गया और पढ़ा है: वीपी)

संपादित करें: के बाद से कंटेनर सीधे बहुरूपी वस्तुओं नहीं स्टोर कर सकते हैं, और स्मार्ट संकेत को लागू करने, अस्थायी तर्क वस्तुओं के लिए उपयुक्त नहीं हैं बहुरूपता को अस्थायी लोगों को पॉइंटर्स लेने की आवश्यकता होगी।

template <typename... T> 
void printf(char const* format, T const&... t) { 
    printf_impl(format, std::initializer_list< Interface const * > 
     { & static_cast< Interface const & >(shim(t))... }); 
} 
+0

'startizer_list' अच्छा है, दुर्भाग्यवश यह आंतरिक टाइपिफ के कारण संदर्भों का समर्थन नहीं करता है। कोड को हल्का बनाने में थोड़ा सा मदद करता है। क्या आपको लगता है कि '{(और (इंटरफ़ेस कॉन्स और) शिम (टी)) ...}' 'printf' में मान्य होगा? इस पर विचारधारा त्रुटियां: "त्रुटि:' brace-संलग्न प्रारंभिक सूची> 'से गैर-स्केलर प्रकार' std :: startizer_list 'अनुरोध किया गया" –

+0

@MatthieuM से रूपांतरण। यह संदर्भों का समर्थन नहीं करता है क्योंकि यह एक कंटेनर है और एक सरणी बनाता है। मुझे नहीं लगता कि समस्या का कोई समाधान है जिसमें एक सरणी (या अन्य कंटेनर) शामिल नहीं है, क्योंकि गैर-टेम्पलेट कार्यान्वयन का अन्य इंटरफ़ेस क्या स्वीकार करेगा? – Potatoswatter

+0

@MatthieuM। खैर, यह शायद G ++ में एक बग है, क्योंकि वह ब्रेस-संलग्न-प्रारंभकर्ता-सूची * उस 'प्रारंभकर्ता_सूची' विशेषज्ञता के लिए परिवर्तनीय है। हम्म, मैंने पॉलिमॉर्फिज्म और कंटेनर के साथ समस्या के बारे में सोचा नहीं था। हां, हालांकि यह 'static_cast' होना चाहिए, मुझे लगता है कि यह सबसे अच्छा समाधान हो सकता है ... – Potatoswatter

0

आप समरूप (स्मृति में एक ही आकार और संरेखण) का उपयोग कर सकते हैं प्रकार एक नज़र at that ले: संपादन के बाद

// thin template layer over regular class/methods 
template< typename T, typename... Contracts> 
inline void Container::bindSingleAs(){ 

isMultiBase< T, Contracts...>(); //compile time test 

    priv::TypeInfoP   types[ sizeof...(Contracts)] 
          { &typeid(Contracts)... }; 

    priv::SharedUpcastSignature upcasts[ sizeof...(Contracts)] 
          { &priv::shared_upcast< T, Contracts>... }; 

    // dispatch over non-template method. 
    container->bindSingleAs(&typeid(T), types, upcasts, sizeof...( Contracts)); 
} 

अब बदसूरत, लेकिन कानूनी जीवन अस्थायी वस्तुओं के लिए गारंटी की वजह से टिप्पणियों के कारण, मुझे लगता है कि 2 विरोधाभासी आवश्यकताएं हैं।

  1. एक सरणी पैरामीटर
  2. कोई नकल भूमि के ऊपर चाहते हैं चाहते हैं

printf_impl समारोह पैरामीटर के रूप में एक सरणी की आवश्यकता होती है, तो इसका मतलब यह है कि सरणी तत्वों स्मृति में एक ही स्वभाव होना चाहिए (कि इसका मतलब है कि यदि 1 तत्व 64 बाइट्स है जो अन्य सभी तत्वों को 64 बाइट्स के रूप में गठबंधन करता है, भले ही वे 1 बाइट हैं ..) इसलिए एक प्रति आवश्यक है, या कम से कम एक निश्चित स्थान पर एक सूचक को कॉपी करें, इसलिए यह निश्चित रूप से POSS नहीं है आईबीएलई क्या ओपी चाहता था।

हम अभी भी है कि सरणी का निर्माण कर सकते हैं, लेकिन हम विवश कर रहे हैं:

  1. हम नकल बिल्कुल नहीं करना चाहते, तो हम स्थिर सरणी के प्रकार की घोषणा करना चाहिए, इस बल हमें एक निर्माण करने के लिए तीसरा प्रकार

    auto Array = MakeArray(/* values*/);

    printf(Array);

  2. हम नकल स्वीकार करते हैं, तो हम समारोह के अंदर सरणी का निर्माण, के बाद से मान नहीं जाना जाता है हम उपयोगकर्ता से सरणी छिपा कर सकते हैं, लेकिन हम तय स्मृति स्थल के लिए मानकों को कॉपी करने के लिए है , हालांकि हम अभी भी हुड के नीचे छिपा हुआ सरणी है।

  3. हीप आवंटन, जो बहुत कॉम्पैक्ट सरणी में पैरामीटर पास करने की अनुमति देता है, हालांकि पैरामीटर को कहीं और रहना पड़ता है और आवंटन ढेर महंगा हो सकता है।

पहला समाधान है कि इनकी है के बाद से उस वस्तु जो का आकार बढ़ाने के लिए, एक स्थिर टाइप किया सरणी जो तत्वों संबोधित किया जा सकता है (सभी सबसे बड़ी तत्व को गठबंधन) बनाने के द्वारा कोडिंग में एक अतिरिक्त जटिलता को स्वीकार करना है लेकिन वैसे भी प्रदर्शन कर सकते हैं (यदि वह सरणी फ़ंक्शन कॉल के बाद भी रहता है)

दूसरा समाधान टेम्पलेट इंटरफ़ेस के पीछे जटिलता को छुपाता है, हालांकि यह अस्थायी रूप से किसी एक के समान मानों की प्रतिलिपि बनाने की प्रदर्शन लागत से बच नहीं सकता है पहला समाधान

तो, ऐसा करना संभव नहीं है, क्षमा करें। दूसरा उत्तर संख्या 2 और 3 के बीच आता है। अन्य सभी संभावित उत्तर 3 श्रेणियों में से एक के भीतर होंगे।

+0

भ्रम के लिए खेद है शिम एक व्यक्ति का नाम नहीं है, यह एक नियमित [अंग्रेजी शब्द] (http://www.merriam-webster.com/dictionary/shim) है। 2012 के प्रश्न की तारीख आपको बताएगी कि शिम विचार आपके भंडार से बहुत पुराना है, लेकिन ईमानदार होने के लिए यह पूरी तरह से अप्रासंगिक है। कोई भी परवाह नहीं करता जिसने पहले आविष्कार किया (जब तक पेटेंट शामिल नहीं होते) दुनिया फिर से खोज से भरा हुआ है। मैं आपके उत्तरों को प्रासंगिक भागों में ट्रिम करने की सलाह दूंगा ... –

+0

आह :) धन्यवाद, मैंने इसे छंटनी और इसे थोड़ा सा सुधार दिया, किसी अस्पष्ट भागों या क्या यह आपके लिए अच्छा है? – GameDeveloper

+0

... हालांकि पहले जवाब को फिर से ध्यान देना होगा। सवाल यह है कि कैसे 'टेम्पलेट <टाइपनाम ... टी> शून्य प्रिंटफ (टी कॉन्स * प्रारूप, टी कॉन्स और ... तर्क)' to 'void print_impl (char const *, इंटरफेस कॉन्स *, size_t) 'या' 'void print_impl (char const *, std :: startizer_list <इंटरफेस कॉन्स *>) 'मध्यस्थ कदम के बिना (जो शिम समावेशन के कारण मुश्किल है), हालांकि इस उत्तर में शुरू करने के लिए कोई शिम नहीं है, इसलिए जब तक यह समझा न जाए यह एक शिम के बिना क्यों काम कर सकता है (यानी, यह 'इंटरफेस' वर्ग) या कि शिम एक गैर-मुद्दा है, यह सवाल को संबोधित नहीं कर रहा है। –