24

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 と likes を認識しています。この質問Is this possible?Is this the best way to do it?.

どんな助けでも大歓迎です。

ありがとう、

4

4 に答える 4

30

すべての問題を無視して、救助へのインデックス:

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>{});
}

実例。

于 2012-11-09T20:55:53.943 に答える
5

これを行う最善の方法は、生成される小さなセットアップ プログラムを作成tableすることです。その後、セットアップ プログラムを破棄するか、生成されたソース コードと一緒にチェックインします。

この質問のトリッキーな部分は、この他の質問の複製です: Is it possible to create and initialize a array of values using template metaprogramming?

トリックは、次のようなものを書くことは不可能です

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 自体が変更されない限り変更されないのに、なぜそんなことをするのでしょうか? 正しい方法最も単純な方法です。256 個のエントリすべてを事前に計算し、テンプレート、constexpr、またはその他の魔法を使用せずにテーブルを明示的に書き出します。

于 2012-11-09T21:12:11.137 に答える
4

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 に関する私の他の回答を参照してください。これにより、見栄えの悪いハックをいくつか削除できます。

于 2017-06-30T02:00:17.643 に答える