4

ある時点で、Ada と同じように動作する Enum をサポートするクラス/テンプレートを C++ で実装することを検討していました。この問題について考えてからしばらく経ちましたが、誰かがこの問題を解決したことがあるかどうか疑問に思っていましたか?

編集:

申し訳ありませんが、Enum の Ada 実装で役立つと思われる機能を明確にする必要があります。列挙を考えると

type fruit is (apple, banana, cherry, peach, grape);

果物は、リストされている果物の 1 つであることを知っています: リンゴ、バナナ、チェリー、モモ、グレープ。C++ と特に違いはありません。

非常に便利なのは、追加作業なしで Ada のすべての列挙型で取得できる次の機能です。

  • 列挙値を出力すると、文字列バージョンが生成されます
  • 列挙変数をインクリメントできます
  • 列挙された変数を減らすことができます

これで問題がもう少し明確になることを願っています。


コメントから追加されたメモ:

Ada 列挙の便利な機能

  • 列挙の最初の値は、fruit'firstを与えるものappleです。
  • 列挙の最後の値は、fruit'lastを与えるものgrapeです。
  • インクリメント操作はfruit'succ(apple)を与えるものbananaです。
  • デクリメント操作はfruit'pred(cherry)、 も与えbananaます。
  • 列挙から整数への変換は、Ada が 0 から始まる列挙を使用するため、fruit'pos(cherry)これを返します。2
  • 整数から列挙型への変換は、fruit'val(2)を返しますcherry
  • 列挙型から文字列への変換fruit'Image(apple)は、(大文字の) string を返します"APPLE"
  • 文字列から列挙型への変換fruit'Value("apple")は、値を返すものですapple

関連する SO の質問も参照してください。

4

8 に答える 8

3

よし、C++ のことはしばらく脇に置いておこう。C++ は C の単なるスーパーセットです (つまり、C で実行できることはすべて C++ でも実行できます)。それでは、plain-C に集中しましょう (それは私がよく知っている言語だからです)。C には列挙型があります。

enum fruit { apple, banana, cherry, peach, grape };

これは完全に正当な C であり、値は連続しており、apple の値は 0 で、banana の値は apple + 1 です。穴のある列挙型を作成できますが、このようなを明示的に作成する場合に限ります。

enum  fruit { apple = 0, banana, cherry = 20, peach, grape };

りんごは 0、バナナは 1、さくらんぼは 20、桃は 21、ぶどうは 22、1 から 20 までは未定義です。通常、穴は必要ありません。次のことができます。

enum fruit { apple = 0, banana, cherry, peach, grape };
enum fruit myFruit = banana;
myFruit++;
// myFruit is now cherry
printf("My fruit is cherry? %s\n", myFruit == cherry ? "YES" : "NO");

これにより、YES が出力されます。次のこともできます。

enum fruit { apple = 0, banana, cherry = 20, peach, grape };
enum fruit myFruit = banana;
myFruit++;
// myFruit is now cherry
printf("My fruit is cherry? %s\n", myFruit == cherry ? "YES" : "NO");

これは NO を出力し、myFruit の値は列挙定数のいずれとも同じではありません。

ところで、「enum fruit myFruit」と言わなければならないことを避けるために、typedef で enum を避けることができます。「typedef enum fruit fruit;」を使用するだけです。専用線で。これで、enum を前に付けずに「fruit myFruit」と言うことができます。多くの場合、列挙型が定義されているときに直接行われます。

typedef enum fruit { apple = 0, banana, cherry, peach, grape } fruit;

fruit myFruit;

欠点は、果物が列挙型であることをもはや知らないことです。それはオブジェクト、構造体、またはその他のものである可能性があります。私は通常、これらのタイプの typedef を避けます。enum の場合は enum を前に、構造体の場合は struct を前に記述します (見栄えが良いので、ここではそれらを使用します)。

文字列値を取得できません。実行時には、列挙は単なる数値です。つまり、列挙の種類がわからない場合は不可能です (0 はリンゴかもしれませんが、別の列挙セットの別のものである可能性もあります)。ただし、それが果物であることがわかっている場合は、それを実行する関数を簡単に作成できます。プリプロセッサはあなたの友達です:-)

typedef enum fruit {
    apple = 0,
    banana,
    cherry,
    peach,
    grape
} fruit;

#define STR_CASE(x) case x: return #x
const char * enum_fruit_to_string(fruit f) {
    switch (f) {
        STR_CASE(apple); STR_CASE(banana); STR_CASE(cherry);
        STR_CASE(peach); STR_CASE(grape);
    }
    return NULL;
}
#undef STR_CASE

static void testCall(fruit f) {
    // I have no idea what fruit will be passed to me, but I know it is
    // a fruit and I want to print the name at runtime
    printf("I got called with fruit %s\n", enum_fruit_to_string(f));
}

int main(int argc, char ** argv) {
    printf("%s\n", enum_fruit_to_string(banana));
    fruit myFruit = cherry;
    myFruit++; // myFruit is now peach
    printf("%s\n", enum_fruit_to_string(myFruit));
    // I can also pass an enumeration to a function
    testCall(grape);
    return 0;
}

出力:

banana
peach
I got called with fruit grape

これはまさにあなたが望んでいたものですか、それとも私はここで完全に間違った方向に進んでいますか?

于 2008-11-19T23:35:56.170 に答える
2

私の同僚の1人が、必要なことのほとんど(すべてではないにしても)を実行するクラスを生成するツールを実装しました。

http://code.google.com/p/enumgen/

現在の実装はLispにありますが、彼に対してそれを保持しないでください:-)

于 2008-11-19T01:11:48.583 に答える
2

Boost.Preprocessorを使用するマクロenum_iteratorと一緒に、これを行うを作成しました。ENUM

#include <iostream>
#include "enum.hpp"

ENUM(FooEnum, 
  (N)
  (A = 1)
  (B = 2)
  (C = 4)
  (D = 8));

int main() {
  litb::enum_iterator< FooEnum, litb::SparseRange<FooEnum> > i = N, end;
  while(i != end) {
    std::cout << i.to_string() << ": " << *i << std::endl;
    ++i;
  }
}

列挙型を単純な古い列挙型として宣言するため、「通常の」目的で引き続き使用できます。イテレータは、連続した値を持つ他の通常の列挙型にも使用できます。そのため、デフォルトで に設定される 2 番目のテンプレート パラメータがありますlitb::ConsequtiveRange<>。双方向反復子の要件に準拠しています。

愚かなコードはここからダウンロードできます

于 2009-09-17T03:43:24.420 に答える
1

C ++でこれを行う簡単な方法はありません。特に、列挙定数が一意または連続している必要がないためです。値から文字列への変換も簡単ではありません。私が知っているソリューションには、C / C ++プリプロセッサハッカリーが含まれます。これは、ハッカリーという用語の蔑称的な使用法です。

私は「いいえ」と言いたくなります。それが正しいかどうかはわかりませんが、それは間違いなく重要です。

于 2008-11-19T01:05:49.687 に答える
1

Java列挙型(http://madbean.com/2004/mb2004-3/)とこのアイデアをご覧ください:http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern

于 2008-11-19T01:06:20.747 に答える
1

enumgen に興味がある場合は、例を使用して簡単なデモを作成しました。前述のように Common Lisp を使用して実装したため、入力ファイルは Lisp ですが、構文を合理的にするために非常に苦労しました。

ここにあります:

$ cat Fruit.enum
(def-enum "Fruit" (("apple")
                   ("banana")
                   ("cherry")
                   ("peach")
                   ("grape")
                   ("INVALID_")))

$ enumgen Fruit.enum
Using clisp
;; Loading file /tmp/enumgen/enumgen.lisp ...
;; Loaded file /tmp/enumgen/enumgen.lisp
loading def file:
;; Loading file /tmp/enumgen/enumgen.def ...
;; Loaded file /tmp/enumgen/enumgen.def
generating output:
  Fruit.cpp
  Fruit.ipp
  Fruit.hpp
DONE

生成されたコードを表示するには、次の URL にアクセスしてください: http://code.google.com/p/enumgen/source/browse/#svn/trunk/demo

このままでもかなり機能が豊富ですが、入力ファイルに変数を設定したり、列挙子の属性を指定したりして、微調整できることもたくさんあります。

たとえば、デフォルトでは、std::string を使用して文字列名を表しますが、少し努力すれば、char const * または任意のユーザー定義の文字列クラスを使用できます。

複数の名前を同じ列挙値にマップすることができますが、値を文字列にマップすると (他の名前ではなく) この名前になるように、1 つを「プライマリ」にする必要があります。

列挙型には明示的に値を指定できますが、一意である必要はありません。(重複は、同じ値を持つ前の列挙型の暗黙のエイリアスです。)

さらに、すべての一意の値と、すべてのエイリアスの各値を反復処理できます。これは、Ruby を使用する場合のように、これらのスクリプト言語の「ラッパー」を生成する場合に役立ちます。

これを使用することに興味があり、質問がある場合は、メールでお気軽にお問い合わせください。(gmail の cuzdav)。

お役に立てれば。(テスト スイートとデモ コード、および必要に応じてソースを除いて、多くのドキュメントはありません。)

クリス

于 2008-11-19T23:43:48.667 に答える
0

これは未リリースのソフトウェアですが、Frank Laub の BOOST_ENUM が適しているようです。私が気に入っている部分は、クラスのスコープ内で列挙型を定義できることです。これは、ほとんどのマクロベースの列挙型では通常許可されていません。Boost Vault にあり ます: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=&新しい Boost リリースでどれだけうまくコンパイルできるかを知っています。使用例については、libs/test を参照してください。Boost smart_enum もあります (どちらもリリースされていません)。質問のイテレータ部分を実行しますが、文字列への出力は実行しません。http://cryp.to/smart-enum/

于 2009-11-04T16:17:15.833 に答える
0

この記事では、列挙値の文字列バージョンを生成する方法について説明しますが、そのためにはコードを自分で作成する必要があります。また、列挙型が連続している限り、列挙型変数のインクリメントとデクリメントを非常に簡単に許可するプリプロセッサ マクロも提供します。

このライブラリは、より柔軟なインクリメントとデクリメントを提供します。

Boost Vaultの enum_rev4.6.zip ライブラリは、簡単な文字列変換を提供します。イテレータを使用したインクリメントとデクリメントをサポートしているようです (これはおそらく便利ではありませんが、機能します)。libs/test ディレクトリにはいくつかの良いサンプル コードが含まれていますが、基本的に文書化されていません。

于 2008-11-19T01:41:35.873 に答える