2

列挙型に関するプロジェクトで問題が発生しました。EventDef.h では、

enum EventDef {
    EVT1 = 0,
    EVT2,
    EVT3,
    EVT_NUM,
}

このようにして、別のヘッダー UIEventDef.h で EventDef システムを拡張できます。

#include "EventDef.h"
enum UIEventDef {
    UIEVT1 = EVT_NUM,
    UIEVT2,
    UIEVT3,
}

ただし、NetEvent.h で同じようにできないという制限があります。

#include "EventDef.h"
enum NetEventDef {
    NETEVT1 = EVT_NUM,
    NETEVT2,   //wrong: this will have the same value as UIEVT2
    NETEVT3,  
}

役立つテンプレートなど、C++ でより優れたコンパイル時ソリューションはありますか?

4

4 に答える 4

6

拡張可能な列挙型のアイデアは、本質的に「悪い設計」ではありません。他の言語では、c ++が直接サポートしていなくても、それらの履歴があります。拡張性にはさまざまな種類があります。

拡張可能な列挙型が役立つもの

  • エラーコード
  • メッセージタイプ
  • デバイスの識別(OIDはシステムのような階層型列挙型です)

列挙型の拡張性の例

  • Objective Modula Twoには、継承のようなクラスで拡張可能な列挙型があります。
  • JavaのExtensibleEnumパターン。c++で実装できます。
  • Java列挙型は、追加のデータとメソッドを列挙型の一部にすることができるという点で拡張可能です。
  • C ++では、typeid演算子は基本的に、値が付加されたコンパイラー生成の列挙型です。

サンプルコードで示した種類の拡張性には、支援されていないc++での洗練された実装がありません。実際、ご指摘のとおり、問題が発生しやすくなっています。

拡張可能な列挙型をどのように使用したいかを考えてください。おそらく、不変のシングルトンオブジェクトのセット/マップがニーズを満たします。

C ++で拡張可能な列挙型を使用する別の方法は、コードジェネレーターを使用することです。拡張可能な列挙型に追加するすべてのコンパイルユニットは、独自の個別の.enumファイルにIDを記録します。ビルド時に、コンパイル前に、スクリプト(つまり、perl、bash、...)がすべての.enumファイルを検索し、それらを読み取り、各IDに数値を割り当て、他のファイルと同様にインクルードされているヘッダーファイルを書き出します。

于 2012-08-25T04:50:24.023 に答える
2

イベント列挙型をそのように宣言する必要があるのはなぜですか? それらを「リンク」させることで、あなたが説明する方法で何を得ることができますか?

私はそれらを完全に独立した列挙型にします。次に、古いスタイルの列挙型を使用しないことをお勧めします。c++11 はここにあり、gcc で利用できます。enum クラスを使用する必要があります。

enum class EventDef  : unsigned { Evt1 = 0, Evt2, Evt3, ... LastEvt }
enum class NetEvtDef : unsigned { NetEvt1 = 0, NetEvt2, NetEvt3, ... NetLastEvt }

切り替えている場合は、これを行うことができます:

void doSwitch(EventDef evt_def)
{
  switch(evt_def)
  {
    case EventDef::Evt1
    {
     // Do something;
     break;
    }
    default:
    // Do something;
  };
}

void doSwitch(NetEvtDef net_def)
{
  switch(net_def)
  {
    case NetEvtDef::NetEvt1
    {
     // Do something;
     break;
    }
    default:
    // Do something;
  };
}

doSwitch のオーバーロードされた関数を作成することにより、すべての列挙型を分離します。それらを別々のカテゴリに入れることは、問題ではなく利点です。これにより、各イベント列挙型を異なる方法で柔軟に処理できます。

あなたが説明したようにそれらを連鎖させると、問題が不必要に複雑になります。

それが役立つことを願っています。

于 2012-08-25T02:37:13.847 に答える
0

以下は、複雑さ、機能、およびタイプ セーフの間の便利な妥協点だと思います。初期化を容易にするデフォルトのコンストラクターを持つカスタム クラスのグローバル変数を使用します。以下の例は、拡張可能なエラー コードのセットです。名前空間で囲むこともできます (しかし、私は通常気にしません)。

//
//  ErrorCodes.h
//  ExtendableEnum
//
//  Created by Howard Lovatt on 10/01/2014.
//

#ifndef ErrorCodes_h
#define ErrorCodes_h

#include <string>

class ErrorCodes {
public:
    static int nextValue_;
    explicit ErrorCodes(std::string const name) : value_{nextValue_++}, name_{name} {}
    ErrorCodes() : ErrorCodes(std::to_string(nextValue_)) {}
    int value() const { return value_; }
    std::string name() const { return name_; }
private:
    int const value_;
    std::string const name_;
    ErrorCodes(const ErrorCodes &);
    void operator=(const ErrorCodes &);
};

int ErrorCodes::nextValue_ = 0; // Weird syntax, does not declare a variable but rather initialises an existing one!
ErrorCodes first;
ErrorCodes second;
// ...

#endif


//
//  ExtraErrorCodes.h
//  ExtendableEnum
//
//  Created by Howard Lovatt on 10/01/2014.
//

#ifndef ExtraErrorCodes_h
#define ExtraErrorCodes_h

#include "ErrorCodes.h"

ErrorCodes extra{"Extra"};

#endif


//
//  ExtraExtraExtraCodes.h
//  ExtendableEnum
//
//  Created by Howard Lovatt on 10/01/2014.
//

#ifndef ExtendableEnum_ExtraExtraCodes_h
#define ExtendableEnum_ExtraExtraCodes_h

#include "ErrorCodes.h"

ErrorCodes extraExtra{"ExtraExtra"};

#endif


//
//  main.cpp
//  ExtendableEnum
//
//  Created by Howard Lovatt on 10/01/2014.
//

#include <iostream>
#include "ErrorCodes.h"
#include "ExtraErrorCodes.h"
#include "ExtraExtraErrorCodes.h"

// Need even more error codes
ErrorCodes const localExtra;

int main(int const notUsed, const char *const notUsed2[]) {
    std::cout << first.name() << " = " << first.value() << std::endl;
    std::cout << second.name() << " = " << second.value() << std::endl;
    std::cout << extra.name() << " = " << extra.value() << std::endl;
    std::cout << extraExtra.name() << " = " << extraExtra.value() << std::endl;
    std::cout << localExtra.name() << " = " << localExtra.value() << std::endl;
    return 0;
}

出力は次のとおりです。

0 = 0
1 = 1
Extra = 2
ExtraExtra = 3
4 = 4

複数のコンパイル ユニットがある場合は、シングルトン パターンのバリエーションを使用する必要があります。

class ECs {
public:
    static ErrorCode & first() {
        static ErrorCode instance;
        return instance;
    }
    static ErrorCode & second() {
        static ErrorCode instance;
        return instance;
    }
private:
    ECs(ECs const&);
    void operator=(ECs const&);
};
于 2014-01-10T05:51:35.290 に答える