Constexprを使用して列挙型の配列にデータを入力したいと思います。配列の内容は特定のパターンに従います。
ASCII文字セットを4つのカテゴリに分ける列挙型があります。
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?
。
どんな助けでもいただければ幸いです。
ありがとう、
[〜#〜] all [〜#〜]の問題を無視すると、 インデックス 救助に:
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>{});
}
C++ 17では::std::array
がよりconstexpr
フレンドリーになるように更新され、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
(グローバル状態、つまりヒープの変更を意味する)などを使用したりすることはできません。
これを行うための最良の方法は、table
を生成する小さなセットアッププログラムを作成することです。そして、セットアッププログラムを破棄するか、生成されたソースコードと一緒にチェックインすることができます。
この質問のトリッキーな部分は、この他の質問の複製です: テンプレートメタプログラミングを使用して値の配列を作成および初期化することは可能ですか?
秘訣は、次のようなものを書くことは不可能です
Type table[256] = some_expression();
グローバル配列はリテラル(ソースレベル)初期化子リストでのみ初期化できるため、ファイルスコープで。 constexpr
関数の結果でグローバル配列を初期化することはできません。たとえその関数がstd::initializer_list
を返すように取得できたとしても、コンストラクターが宣言されていないために初期化できません。 constexpr
。
したがって、あなたがしなければならないことは、それをテンプレートクラスのstatic const
データメンバーにすることによって、あなたのために配列を生成するようにコンパイラを取得することです。私が書き出すのに混乱しすぎているメタプログラミングの1つまたは2つのレベルの後、あなたは次のような行で底を打ちます
template <int... Indices>
Type DummyStruct<Indices...>::table[] = { whichCategory(Indices)... };
ここで、Indices
は、0,1,2,... 254,255
のようなパラメータパックです。そのパラメーターパックは、再帰的なヘルパーテンプレートを使用して作成するか、Boostから何かを使用して作成します。そして、あなたは書くことができます
constexpr Type (&table)[] = IndexHelperTemplate<256>::table;
...しかし、テーブルが256エントリしかないのに、ASCII自体が変更されない限り変更されないのに、なぜそうするのでしょうか?正しい方法is最も簡単な方法:256エントリすべてを事前計算し、テンプレート、constexpr、またはその他の魔法を使用せずに、テーブルを明示的に書き出します。
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は、標準ライブラリでconstexpr
である必要がなく、何が行われたかについて十分なレビューを受けていなかったため、const_cast
を含む恐ろしいハックを使用する必要があります。 。
そしてもちろん、MagicFunction
はグローバル変数を変更したり、constexpr
ルールに違反したりしない方がよいでしょう。しかし、これらのルールは最近かなりリベラルです。たとえば、必要なすべてのローカル変数を変更できますが、参照で渡すか、アドレスを取得すると、うまく機能しない場合があります。
あなたが醜い見た目のハックのいくつかを落とすことができるC++ 17についての私の他の答えを見てください。