2012-11-09 7 views
17

पर कॉन्स्टेक्सप्र का उपयोग कर एक ऐरे पॉप्युलेट करें मैं constexpr का उपयोग करके enum की एक सरणी पॉप्युलेट करना चाहता हूं। सरणी की सामग्री एक निश्चित पैटर्न का पालन करती है।कंपाइल-टाइम

मेरे पास एएससीआईआई चरित्र को चार श्रेणियों में अलग करने वाला एक enum है।

enum Type { 
    Alphabet, 
    Number, 
    Symbol, 
    Other, 
}; 

constexpr Type table[128] = /* blah blah */; 

मैं 128 Type की एक सरणी रखना चाहता हूं। वे एक संरचना में हो सकते हैं। सरणी का सूचकांक ASCII वर्णों के अनुरूप होगा और मान प्रत्येक वर्ण के Type होगा।

तो मैं इस सरणी से पूछ सकता हूं कि एएससीआईआई चरित्र किस श्रेणी से संबंधित है। कुछ

char c = RandomFunction(); 
if (table[c] == Alphabet) 
    DoSomething(); 

मैं जानना चाहता हूं कि यह कुछ लंबा मैक्रो हैक्स के बिना संभव है या नहीं।

वर्तमान में, मैं निम्न कार्य करके तालिका आरंभ करता हूं।

constexpr bool IsAlphabet (char c) { 
    return ((c >= 0x41 && c <= 0x5A) || 
      (c >= 0x61 && c <= 0x7A)); 
} 

constexpr bool IsNumber (char c) { /* blah blah */ } 

constexpr bool IsSymbol (char c) { /* blah blah */ } 

constexpr Type whichCategory (char c) { /* blah blah */ } 

constexpr Type table[128] = { INITIALIZE }; 

जहां INITIALIZE कुछ बहुत ही लंबा मैक्रो हैक्स के प्रवेश बिंदु है। कुछ की तरह

#define INITIALIZE INIT(0) 
#define INIT(N) INIT_##N 
#define INIT_0 whichCategory(0), INIT_1 
#define INIT_1 whichCategory(1), INIT_2 
//... 
#define INIT_127 whichCategory(127) 

मैं इस सरणी या एक संरचना इस मैक्रो हैक की आवश्यकता के बिना सरणी युक्त पॉप्युलेट करने के लिए एक तरह से चाहते हैं ...

हो सकता है कि जैसे

struct Table { 
    Type _[128]; 
}; 

constexpr Table table = MagicFunction(); 

तो कुछ सवाल यह है कि MagicFunction कैसे लिखना है?

नोट: मैं cctype के बारे में पता कर रहा हूँ और पसंद करता है, इस सवाल का Is this the best way to do it? बजाय एक Is this possible? की अधिक है।

किसी भी मदद की सराहना की जाएगी।

धन्यवाद,

+2

आप जानते हैं कि ASCII केवल '[0 .. 127] 'है? और 'char' की हस्ताक्षर कार्यान्वयन परिभाषित किया गया है? आपका वर्तमान दृष्टिकोण बहुत खतरनाक है। ओह, और आखिरी लेकिन कम से कम नहीं, सी ++ मानक ASCII एन्कोडिंग की बिल्कुल मांग नहीं करता है। यह भी ईबीसीडीआईसी हो सकता है। – Xeo

+0

अच्छी खबर यह है कि चूंकि पैक के विस्तार के साथ सरणी शुरू की जा सकती है, जो आप पूछते हैं वह वास्तव में व्यवहार्य है। आपको केवल फ़ंक्शन को कई बार आह्वान करने की आवश्यकता है: पी –

+0

अधिकतर संभव नहीं है क्योंकि C++ को वर्णों के लिए ASCII प्रतिनिधित्व की आवश्यकता नहीं है। साथ ही, सख्ती से, ASCII चरित्र सेट में केवल 128 वर्ण शामिल हैं। –

उत्तर

25

सभी मुद्दों, indices की उपेक्षा कर बचाव के लिए:

template<unsigned... Is> struct seq{}; 
template<unsigned N, unsigned... Is> 
struct gen_seq : gen_seq<N-1, N-1, Is...>{}; 
template<unsigned... Is> 
struct gen_seq<0, Is...> : seq<Is...>{}; 

template<unsigned... Is> 
constexpr Table MagicFunction(seq<Is...>){ 
    return {{ whichCategory(Is)... }}; 
} 

constexpr Table MagicFunction(){ 
    return MagicFunction(gen_seq<128>{}); 
} 

Live example.

+1

क्या आप कृपया बता सकते हैं कि यह क्यों काम करता है? –

+2

@Steven: लाउंज विकी प्रविष्टि के लिए एक लिंक जोड़ा गया। यह मूल रूप से एक सूची '[0 .. 127]' बनाता है और विस्तार करता है, 'कौन सी श्रेणी (0), कौन सी श्रेणी (1), ..., कौन सी श्रेणी (127)' कहता है और सूची-प्रारंभिकरण के लिए आरंभकर्ता तर्क के रूप में गुजरता है ' Table._' (आंतरिक सरणी को प्रारंभ करने के लिए डबल '{}' नोटिस करें)। – Xeo

+0

आप एक जादूगर हैं ... :-) –

4

IMHO यह करने के लिए सबसे अच्छा तरीका है बस एक छोटे से सेटअप प्रोग्राम लिखने की जाती है कि होगा आपके लिए table उत्पन्न करें। और फिर आप या तो सेटअप प्रोग्राम फेंक सकते हैं, या जेनरेट किए गए स्रोत कोड के साथ इसे जांच सकते हैं। , Is it possible to create and initialize an array of values using template metaprogramming?

चाल है, ऐसा लगता है

Type table[256] = some_expression(); 
फ़ाइल दायरे में

कुछ भी लिखने के लिए असंभव है क्योंकि वैश्विक सरणियों हो सकता है:

इस सवाल का मुश्किल हिस्सा सिर्फ इस एक दूसरे का डुप्लिकेट है केवल शाब्दिक (स्रोत-स्तर) प्रारंभकर्ता-सूचियों के साथ प्रारंभ किया गया।constexpr फ़ंक्शन के परिणामस्वरूप आप किसी वैश्विक सरणी को प्रारंभ नहीं कर सकते हैं, भले ही आप std::initializer_list को वापस करने के लिए उस फ़ंक्शन को प्राप्त कर सकें, जिसे आप नहीं कर सकते क्योंकि इसके निर्माता को constexpr घोषित नहीं किया गया है।

तो आपको टेम्पलेट क्लास के static const डेटा सदस्य बनाकर, आपके लिए सरणी उत्पन्न करने के लिए कंपाइलर प्राप्त करना है। metaprogramming के एक या दो स्तरों है कि मैं भी लिखने के लिए उलझन में हूँ के बाद, आप एक लाइन में बाहर नीचे जाएगा की तरह

template <int... Indices> 
Type DummyStruct<Indices...>::table[] = { whichCategory(Indices)... }; 

जहां Indices एक पैरामीटर पैक कि 0,1,2,... 254,255 की तरह लग रहा है कुछ दिखता है। आप एक पुनरावर्ती सहायक टेम्पलेट का उपयोग करके उस पैरामीटर-पैक का निर्माण करते हैं, या शायद बस बूस्ट से कुछ का उपयोग कर सकते हैं। और फिर आप

constexpr Type (&table)[] = IndexHelperTemplate<256>::table; 

लिख सकते हैं ... लेकिन आप सभी ऐसा क्यों होता है, जब तालिका केवल 256 प्रविष्टियों कि जब तक ASCII ही बदल जाता है कभी नहीं बदलेगा है? सही तरीका सबसे आसान तरीका है: सभी 256 प्रविष्टियों को प्रीकंप्यूट करें और तालिका को स्पष्ट रूप से लिखें, बिना किसी टेम्पलेट, कॉन्टेक्सप्र या किसी अन्य जादू के।

2

तरह से इस तरह इस में सी ++ 14 दिखता करने के लिए:

#include <array> 

enum Type { 
    Alphabet, 
    Number, 
    Symbol, 
    Other, 
}; 

constexpr ::std::array<Type, 128> MagicFunction() 
{ 
    using result_t = ::std::array<Type, 128>; 
    result_t result = {Other}; 
    const result_t &fake_const_result = result; 
    const_cast<result_t::reference>(fake_const_result[65]) = Alphabet; 
    //.... 
    return result; 
} 

const ::std::array<Type, 128> table = MagicFunction(); 

कोई चतुर टेम्पलेट hackery अब किसी भी आवश्यकता है। हालांकि, क्योंकि सी ++ 14 ने वास्तव में मानक लाइब्रेरी में constexpr होने की पूरी समीक्षा नहीं की थी, इसलिए const_cast से जुड़े एक भयानक हैक का उपयोग किया जाना चाहिए।

और, ज़ाहिर है, MagicFunction किसी भी वैश्विक चर को बेहतर ढंग से संशोधित नहीं करता था या अन्यथा constexpr नियमों का उल्लंघन नहीं करता था। लेकिन आजकल वे नियम बहुत उदार हैं। उदाहरण के लिए, आप अपने इच्छित सभी स्थानीय चर को संशोधित कर सकते हैं, हालांकि संदर्भ द्वारा उन्हें पास करना या उनके पते लेना शायद इतना अच्छा काम नहीं कर सकता है।

सी ++ 17 के लिए मेरा दूसरा उत्तर देखें, जो आपको कुछ बदसूरत दिखने वाले हैक्स को छोड़ने की अनुमति देता है।

+1

आप 'MagicFunction' के अंदर' for' लूप का भी उपयोग कर सकते हैं। – aschepler

+0

मुझे 'परिणाम' के बारे में एक त्रुटि मिली है, हालांकि इसे अनियमित घोषित किया जा रहा है। 'std :: array परिणाम {};' काम करता है - मुझे लगता है कि 'टाइप {} 'वर्णमाला' है। – aschepler

+0

@aschepler ओह, मैं इसे ठीक कर दूंगा। – Omnifarious

6

में सी ++ 17 ::std::array अधिक constexpr अनुकूल होने के लिए अद्यतन किया गया है और आप सी ++ 14 में के रूप में ही कर सकते हैं, लेकिन डरावना लग रही हैक्स के कुछ महत्वपूर्ण स्थानों में constexpr की कमी से बचने के लिए बिना। यहाँ कोड वहाँ कैसा दिखेगा है:

#include <array> 

enum Type { 
    Alphabet, 
    Number, 
    Symbol, 
    Other, 
}; 

constexpr ::std::array<Type, 128> MagicFunction() 
{ 
    using result_t = ::std::array<Type, 128>; 
    result_t result = {Other}; 
    result[65] = Alphabet; 
    //.... 
    return result; 
} 

const ::std::array<Type, 128> table = MagicFunction(); 

फिर MagicFunction अभी भी बल्कि ढीला constexpr नियमों का पालन करने की जरूरत है। मुख्य रूप से, यह किसी भी वैश्विक चर को संशोधित नहीं कर सकता है या new (जो वैश्विक स्थिति, अर्थात् ढेर को संशोधित करने का तात्पर्य है) या अन्य ऐसी चीजों का उपयोग नहीं कर सकता है।