2012-11-09 20 views
17

我想使用constexpr填充枚舉數組。 數組的內容遵循一定的模式。在編譯時使用Constexpr填充數組

我有一個枚舉分隔ASCII字符集爲四個類別。

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

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

我想擁有128 Type的數組。他們可以在一個結構中。 該數組的索引將對應於ASCII字符,值將是每個字符的Type

所以我可以查詢這個數組來找出一個ASCII字符屬於哪個類別。喜歡的東西

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 possible?而不是Is this the best way to do it?

任何幫助,將不勝感激。

感謝,

+2

你知道ASCII只能範圍'[0 .. 127]'?那個'char'的簽名是實現定義的嗎?你目前的做法非常危險。哦,最後但並非最不重要的是,C++標準完全不需要ASCII編碼。它也可能是EBCDIC。 – Xeo

+0

好消息是,因爲數組可以通過擴展包進行初始化,所以您要求的確實可行。您只需調用該函數很多次:p –

+0

最有可能不可能,因爲C++不需要字符的ASCII表示。另外,從最嚴格的意義上講,ASCII字符集只包含128個字符。 –

回答

25

忽略ALL問題,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:添加了一個鏈接到Lounge wiki條目。它基本上構建了一個列表[0..127],並展開它,調用'whichCategory(0),whichCategory(1),...,whichCategory(127)'並將它作爲列表初始化參數傳遞給' Table._'(注意用於初始化內部數組的double {{})。 – Xeo

+0

你是一個魔術師...... :-) –

4

恕我直言,這樣做的最好的方法就是寫一個很小的安裝程序,將爲你生成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數據成員。後元編程的一個或兩個級別,我是太糊塗寫出來的,你會在一條線上走出低谷,看起來像

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

其中Indices是一個參數包,看起來像0,1,2,... 254,255。您可以使用遞歸輔助模板構建該參數包,也可以僅使用Boost中的某些內容。然後,你可以寫

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

...但是,爲什麼你會做的一切,當表只有256個條目,將永遠不會,除非ASCII本身的變化而變化?正確的方法最簡單的方法:預先計算所有256個條目並明確寫出表格,沒有模板,constexpr或任何其他魔術。

2

的方式做到這一點在C++ 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(); 

無巧模板兩輪牛車所需要的任何時間。雖然,因爲C++ 14並沒有真正地對標準庫中的內容進行全面檢查,所以必須使用涉及const_cast的可怕黑客攻擊。

而且,當然,MagicFunction最好不要修改任何全局變量或者違反constexpr規則。但是現在這些規則很自由。例如,你可以修改你想要的所有局部變量,雖然通過引用來傳遞它們,或者考慮到它們的地址可能無法很好地工作。

請參閱我對C++ 17的其他答案,它允許您刪除一些醜陋的外觀黑客。

+1

你甚至可以在'MagicFunction'中使用'for'循環。 – aschepler

+0

雖然我得到一個關於'result'被聲明爲未初始化的錯誤。 'std :: array result {};'工作 - 我猜'Type {}'是'Alphabet'。 – aschepler

+0

@aschepler哎呀,我會解決這個問題。 – Omnifarious

6

在C++中17 ::std::array已更新爲更友好,您可以像C++ 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(這意味着修改全局狀態,即堆)或其他類似的東西。