हमने टेम्प्लेट पैरामीटर में लाइनें डाल दी हैं

आधुनिक सी ++ ने हमें उन सुविधाओं का एक समूह लाया है जो पहले भाषा में कमी थी। किसी तरह एक समान प्रभाव प्राप्त करने के लिए, लंबे समय तक आश्चर्यजनक बैसाखी का आविष्कार किया गया था, जिसमें मुख्य रूप से पैटर्न और मैक्रोज़ (अक्सर ऑटोजेनस) के बहुत बड़े फुटक्लॉथ शामिल थे। लेकिन अब, समय-समय पर, उन अवसरों के लिए एक आवश्यकता उत्पन्न होती है जो अभी भी भाषा में नहीं हैं। और हम फिर से टेम्पलेट्स और मैक्रोज़ से जटिल डिजाइनों को फिर से बनाना शुरू करते हैं, उन्हें उत्पन्न करते हैं और हमें जिस व्यवहार की आवश्यकता होती है उसे प्राप्त करते हैं। यह सिर्फ एक ऐसी कहानी है।

पिछले आधे साल में, मुझे दो बार उन मूल्यों की ज़रूरत थी जो टेम्पलेट मापदंडों में इस्तेमाल किए जा सकते हैं। उसी समय, मैं इन मूल्यों के लिए मानव-पठनीय नाम रखना चाहता था और इन नामों को पहले से घोषित करने की आवश्यकता को बाहर कर दिया था। विशिष्ट कार्य जो मैंने हल किए थे वे एक अलग मुद्दा थे, शायद बाद में मैं उनके बारे में अलग-अलग पोस्ट लिखूंगा, "असामान्य प्रोग्रामिंग" हब में कहीं। अब मैं उस दृष्टिकोण के बारे में बात करूंगा जिसके द्वारा मैंने इस समस्या को हल किया।

इसलिए, जब टेम्प्लेट मापदंडों की बात आती है, तो हम टाइप या स्टेटिक कास्ट मान का उपयोग कर सकते हैं। अधिकांश कार्यों के लिए यह पर्याप्त से अधिक है। हम मापदंडों में मानव-पठनीय पहचानकर्ताओं का उपयोग करना चाहते हैं - हम संरचना, गणना या निरंतरता की घोषणा करते हैं और उनका उपयोग करते हैं। समस्याएं तब शुरू होती हैं जब हम इस पहचानकर्ता को पहले से निर्धारित नहीं कर सकते हैं और इसे करना चाहते हैं।

सीधे टेम्प्लेट पैरामीटर में संरचना या कक्षा की घोषणा करना संभव होगा। यह तब भी काम करेगा जब टेम्पलेट इस पैरामीटर के साथ कुछ भी नहीं करता है जिसमें संरचना के पूर्ण विवरण की आवश्यकता होती है। इसके अलावा, हम उन नामों को नियंत्रित नहीं कर सकते जिनमें ऐसी संरचना घोषित की गई है। और पूरी तरह से समान दिखने वाले टेम्पलेट प्रतिस्थापन, यदि ये पंक्तियाँ पड़ोसी वर्गों या नामस्थानों में हैं, तो यह पूरी तरह से अलग कोड में बदल जाएगा।

आपको C ++ में सभी शाब्दिक, और सभी शाब्दिक का उपयोग करने की आवश्यकता है, केवल एक अक्षर शाब्दिक और एक स्ट्रिंग शाब्दिक को पठनीय कहा जा सकता है। लेकिन एक वर्ण शाब्दिक चार वर्णों तक सीमित है (जब char32_t का उपयोग करते हुए), और एक स्ट्रिंग शाब्दिक वर्णों का एक सरणी है और इसका मान टेम्पलेट मापदंडों को पारित नहीं किया जा सकता है।

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

चलो स्ट्रिंग के लिए संरचना बनाते हैं


पहले, चलो स्ट्रिंग के लिए आधार बनाते हैं। C ++ 11 में, वैरेडैमिक टेम्पलेट तर्क दिखाई दिए।
हम एक संरचना की घोषणा करते हैं जिसमें तर्कों में स्ट्रिंग वर्ण शामिल हैं:

template <char... Chars> struct String{}; 

GitHub

यह काम करता है। हम भी तुरंत इन लाइनों का उपयोग इस तरह कर सकते हैं:

 template <class T> struct Foo {}; Foo<String<'B', 'a', 'r'>> foo; 

अब इस लाइन को रनटाइम में खींचें


बहुत बढ़िया। इस स्ट्रिंग के मूल्य को रनटाइम में प्राप्त करना बुरा नहीं होगा। आज्ञा देना एक अतिरिक्त टेम्पलेट संरचना है कि इस तरह के एक स्ट्रिंग से तर्क निकालने और उनमें से एक निरंतर बना देगा:

 template <class T> struct Get; template <char... Chars> struct Get<String<Chars...>> { static constexpr char value[] = { Chars... }; }; 

यह भी काम करता है। चूंकि हमारी पंक्तियों में अंत में '\ 0' शामिल नहीं है, इसलिए आपको इस स्थिरांक के साथ सावधानीपूर्वक काम करने की आवश्यकता है (यह बेहतर है, मेरी राय में, कंस्ट्रक्टर तर्कों में निरंतर और इसके आकार का उपयोग करके एक string_view बनाने के लिए)। एक सरणी के अंत में सिर्फ '\ 0' जोड़ सकता है, लेकिन यह मेरे कार्यों के लिए आवश्यक नहीं है।

जांचें कि हम इस तरह के तार में हेरफेर कर सकते हैं


ठीक है, आप इस तरह के तार के साथ और क्या कर सकते हैं? उदाहरण के लिए सहमति:

 template <class A, class B> struct Concatenate; template <char... Chars, char... ExtraChars...> struct Concatenate<String<Chars...>, String<ExtraChars...>> { using type = String<Chars..., ExtraChars...>; }; 

GitHub

सिद्धांत रूप में, आप किसी भी ऑपरेशन को कम या ज्यादा कर सकते हैं (मैंने इसकी कोशिश नहीं की है, क्योंकि मुझे इसकी आवश्यकता नहीं है, लेकिन मैं केवल कल्पना कर सकता हूं कि आप एक विकल्प के लिए कैसे खोज कर सकते हैं या यहां तक ​​कि एक विकल्प की जगह ले सकते हैं)।
अब हमारे पास मुख्य प्रश्न है, संकलन-समय में एक स्ट्रिंग शाब्दिक से वर्णों को कैसे निकालना है और उन्हें टेम्पलेट तर्क में डाल दिया है।

उल्लू को ड्रा करें। मैक्रो लिखें।


आइए एक बार में टेम्प्लेट तर्कों में पात्रों को रखने के तरीके से शुरू करें:

 template <class T, char c> struct PushBackCharacter; template <char... Chars, char c> struct PushBackCharacter<String<Chars...>, c> { using type = String<Chars..., c>; }; template <char... Chars> struct PushBackCharacter<String<Chars...>, '\0'> { using type = String<Chars...>; }; 

GitHub

मैं चरित्र '0' के लिए एक अलग विशेषज्ञता का उपयोग करता हूं, इसलिए इसे उपयोग किए गए स्ट्रिंग में जोड़ने के लिए नहीं। इसके अलावा, यह कुछ हद तक मैक्रो के अन्य भागों को सरल करता है।

अच्छी खबर यह है कि एक स्ट्रिंग शाब्दिक कॉन्स्ट्रेक्स फ़ंक्शन का एक पैरामीटर हो सकता है। हम एक फ़ंक्शन लिखेंगे जो एक स्ट्रिंग या '\ 0' में इंडेक्स द्वारा एक वर्ण लौटाएगा यदि स्ट्रिंग इंडेक्स से कम है (यहां वर्ण '\ 0' के लिए PushBackCharacter विशेषज्ञता काम में आती है)।

 template <size_t N> constexpr char CharAt(const char (&s)[N], size_t i) { return i < N ? s[i] : '\0'; } 

असल में, हम पहले से ही इस तरह से कुछ लिख सकते हैं:

 PushBackCharacter< PushBackCharacter< PushBackCharacter< PushBackCharacter< String<>, CharAt("foo", 0) >::type, CharAt("foo", 1) >::type, CharAt("foo", 2) >::type, CharAt("foo", 3) >::type 

हमने अपने मैक्रो के अंदर इस तरह के एक फुटक्लॉथ, लेकिन अधिक वास्तविक (हम कोड उत्पन्न करने के लिए स्क्रिप्ट लिख सकते हैं) और यह बात है!

एक अति सूक्ष्म अंतर है। यदि लाइन में वर्णों की संख्या मैक्रो में घोंसले के स्तर से अधिक है, तो लाइन को केवल काट दिया जाता है और हम इसे नोटिस भी नहीं करेंगे। गड़बड़।

चलो एक और संरचना बनाते हैं, जो किसी भी तरह से आने वाली स्ट्रिंग को परिवर्तित नहीं करता है, लेकिन static_assert करता है ताकि इसकी लंबाई एक स्थिर से अधिक न हो:

 #define _NUMBER_TO_STR(n) #n #define NUMBER_TO_STR(n) _NUMBER_TO_STR(n) template <class String, size_t size> struct LiteralSizeLimiter { using type = String; static_assert(size <= MAX_META_STRING_LITERAL_SIZE, "at most " NUMBER_TO_STR(MAX_META_STRING_LITERAL_SIZE) " characters allowed for constexpr string literal"); }; #undef NUMBER_TO_STR #undef _NUMBER_TO_STR 

खैर, मैक्रो कुछ इस तरह दिखेगा:

 #define MAX_META_STRING_LITERAL_SIZE 256 #define STR(literal) \ ::LiteralSizeLimiter< \ ::PushBackCharacter< \ ... \ ::PushBackCharacter< \ ::String<> \ , ::CharAt(literal, 0)>::type \ ... \ , ::CharAt(literal, 255)>::type \ , sizeof(literal) - 1>::type 

GitHub

यह निकला


 template <class S> std::string_view GetContent() { return std::string_view(Get<S>::value, sizeof(Get<S>::value)); } std::cout << GetContent<STR("Hello Habr!")>() << std::endl; 

मुझे जो क्रियान्वयन मिला वह गितुब पर पाया जा सकता है

इस तंत्र के संभावित अनुप्रयोगों के बारे में सुनना मेरे लिए बहुत दिलचस्प होगा, उन लोगों से अलग जो मैं लेकर आया था।

Source: https://habr.com/ru/post/hi438146/


All Articles