2016-07-23 14 views
1

C99(とりわけ)配列イニシャライザはそうよう、例えば、配列の要素は正の整数指定子($ 6.7.8.6、$ 6.7.8.17)で設定されている指定することができ:デジグネータ

const char *foo[] = {[2] = "foo", [1] = "bar", [0] = "baz"}; 

私は、以前のようなので、列挙型からストリングテーブルを作るために、これを使用しています

enum {THING_FOO = 0, THING_BAR, THING_BAZ}; 
const char *table[] = { 
    [THING_FOO] = "foo", 
    [THING_BAR] = "bar", 
    [THING_BAZ] = "baz" 
} 

しかし、私は今、私のコードはC89に準拠している要件の下で働いています。

私はプリプロセッサの魔法を調べましたが(例えばhereのように)、列挙型シンボルのコピーではなく、任意の文字列が必要です。

私は将来的に列挙型の要素を追加する必要がありますので、ちょうど

enum {THING_FOO = 0, THING_BAR, THING_BAZ}; 
const char *table[] = {"foo", "bar", "baz"}; 

を行うのに十分ではありません。 c99メソッドを使用すると、テーブルにNULLポインタが生成され、問題になるとデバッグが容易になります。この方法で文字列テーブルを更新するのを忘れてしまった場合は、segfaultsをデバッグするのが難しくなります。とにかく私がオフセットを覚えなければならないなら、それはシンボルを持つ点を打ち負かします。宣言が機能していた場合

が、私はこのような望ましい効果を達成できます。少なくともgccで、しかし

enum {THING_FOO = 0, THING_BAR, THING_BAZ, NUM_THINGS}; 
void foo(void) 
{ 
    static const char *table[NUM_THINGS]; 
    table[THING_FOO] = "foo"; 
    table[THING_BAR] = "bar"; 
    table[THING_BAZ] = "baz"; 

    /* ... */ 
} 

を、これはは、最適化されません。

このような文字列テーブルをc89で宣言する方法はありますか? (これは、アセンブリで問題ありません。)

+0

[cで列挙型の名前を文字列に変換する方法](http://stackoverflow.com/questions/9907160/how-to-convert-enum-names-to-string-in-c) – Leandros

+1

いろいろな方法がありますが、最も醜い方法であると思う方法を選びました。多くの方法がありますが、1つは他よりも悪い方法です。 – Leandros

+0

あなたの質問には、C99以降のエレガントなものではなく、enum値の間のギャップを埋めるものはありません。私は少し混乱している。とにかくコードを変更しなければならないのは明らかです。これは、「巨大なスイッチのステートメント」、またはこの質問の投稿、そして*まだ*どこでもコードを変更する必要がありますか? – WhozCraig

答えて

0

、いくつかの異なる技術を試した後、このいずれかを維持するのが最も簡単です。要素を挿入するには、要素を変更するだけです。 quxを挿入する例:

const char *table[] = { 
#define FOO 0 
    "foo", 
#define QUX (FOO + 1) /* new */ 
    "qux",    /* new */ 
#define BAR (QUX + 1) /* modified */ 
    "bar", 
#define BAZ (BAR + 1) 
    "baz" 
} 

それは(?あなたが知っているかわいらしいの種類、)少し醜いですが、それは良い作品。

1

何言い換えれば

const char* table[] = { "foo", "bar", "baz" }; 

昔ながらのシンプルな、古いについて、ちょうど正しい順序でそれらを置きます。スタイルだけでは上記のような単純な列挙型のため、ない列挙型のため

enum { FOO = 13; BAR = 15, BAZ = 312 }; 

に動作します。しかし、そのために、あなたは、少なくとも313個の要素を持つ配列を作成しなければならない、ということ

もちろん
char *foo_string = table[FOO]; 

、いずれにしてもほとんどNULLですが、かなり無駄な構造になります。このような場合、switch構造体を使用すると、コンパイラはこれを最適化できます。


また、S.O.質問@ Leandrosは:How to convert enum names to string in cを指摘した。そこでの答えは、配列を生成するためにマクロを使用します。これにより、エントリが正しい順序で確保されます。

か、その答えが言うように:完全配列を取り除く

#define enum_str(s) #s 

+0

もう1つの答えの問題は、各enum要素に任意の文字列を割り当てることができないということです。私は質問で明確にします。 – nebuch

+0

あなたはソースコード内の名前だけを取得します。 FOOは "foo"にリンクされていないので、自動的に "FOO"になります。次に配列は1つの解決策です。あるいは、ハッシュテーブル。しかし後者はそれほど簡単ではありません。列挙体が何百もの要素を持つことはめったにないので、スイッチ構造体はおそらくより簡単でしょう。 –

+0

私はちょうどあなたがこれらの巨大なenumを持っているのを見ました。そうすれば、処理が複雑または難しくなります。なぜこれらの列挙型はとても大きいのですか? –

2
#define DEF_FOO_ENUM(E0, S0, E1, S1, E2, S2) \ 
    enum foo    { E0, E1, E2 }; \ 
    const char *foo_str = { S0, S1, S2 }; 

DEF_FOO_ENUM(THING_FOO, "foo", 
       THING_BAR, "bar", 
       THING_BAZ, "baz"); 

記号と文字列がペアになっています。文字列のない新しいシンボルを簡単に追加することはできません。逆もまた同様です。要素を追加するには、マクロへの2つの新しい引数— E3, S3 —などが必要です。同期するには何もありません。enumにはすべてE -sがあり、配列にはすべてS -sが含まれています。これはほとんどねじ込むことは不可能です。クラスタ化されたエントリに関するすべての情報はここで

const char *table[] = { 
#define FOO 0 
    "foo", 
#define BAR (FOO + 1) 
    "bar", 
#define BAZ (BAR + 1) 
    "baz" 
} 

+0

テーブルの中には100個以上の要素があります。私はこの解決策が好きですが、それはEsとSsの多くに作用します。 – nebuch

+3

私はコード生成を行うか、あるいは外部ツールを使って検証します。つまり、これらの宣言(ハンド管理されている)を具体的に理解し、その整合性をチェックするテキスト処理ユーティリティーです。 – Kaz

1

あなたはX-マクロを使って、それらを一緒に保つことができます。

#define MYXMACRO(OP) \ 
    OP(ENUM_FOO, "foo") \ 
    OP(ENUM_BAR, " bar") \ 
    OP(ENUM_BAZ, "baz") 

/* use the first parameter to set up your enums*/ 
enum { 
#define AS_ENUMS(x,y) x, 
MYXMACRO(AS_ENUMS) 
#undef AS_ENUMS /*not required, just playing nice*/ 
NUMTHINGS 
}; 

/* use the 2nd parameter to set up your strings*/ 
const char *strings[] = { 
#define AS_STRINGS(x,y) y, 
MYXMACRO(AS_STRINGS) 
#undef AS_STRINGS 
}; 
#undef MYXMACRO 

今すぐあなたの新しいデータが列挙型と文字列のセットとして追加することができます。後で列挙型に基づいて何かを追加することにした場合は、OP()に「z」パラメータを追加したり、複数のパラメータの数を変更して...および__VA_ARGS__にも簡単に拡張できます。