6

ネットワーク経由で送信できるタイプのリストがあります。次の例を見てください。

enum types {
    E_T1,
    E_T2,
    E_T3,
    E_T4
};

class E_T1 {...}これで、各タイプに対応するクラスのリストができました。たとえば、それぞれが、class E_T2 {...}などとして宣言されているとします。

それらは共通の基本クラスから派生しておらず、そうすることはできません。各クラスには、ネットワーク経由で送信されるデータを使用して呼び出す必要のある検証メソッドがあります。クライアントは、メッセージタイプに対応するデータDとIDを送信します。タイプに対応するオブジェクトを取得する必要があります。必要に応じてC++0x機能を使用できます。

私がこれまでに試したことはtypes、に関連するオブジェクトのtypedefを保持する、に特化したテンプレートを使用することです。テンプレートパラメータはコンパイル時定数である必要があるため、これは明らかにばかげた考えでした。そのため、何かを実行することgetType<data.id()>::typeはできません。

次に、Boost.Variantを使用して、次のような一般的なリターナブルタイプを取得しようとしました(デバグのために実行時に登録されたタイプを反復処理するためにmplベクトルを使用):

template <typename C>
struct getType() {
    typedef C type;
}

typedef boost::mpl::vector<
    getType<E_T1>,
    getType<E_T2>,
    getType<E_TX>...
> _types;

typedef boost::make_variant_over<_types>::type _type;

//use a map to store each type <-> id
boost::unorderd_map<types, _type> m;
m[E_T1] = getType<E_T1>();

m[data.id()]::type x; //<- access type, can now call x.validate(data)

これに伴う問題は、デフォルトでバリアントごとに20エントリに制限されていることです。これは上書きできますが、私が理解したことから、タイプごとのオーバーヘッドを考慮する必要があり、ここでは数千のタイプについて話します。

また、boost.anyを試しましたが、タイプ情報が保持されていないため、これも問題外です。これをエレガントに解決する方法について、誰か良いアイデアはありますか?型を処理するときはいつでも1kのswitchステートメントを書く必要がないものを探しています。

すべてのタイプがコンパイルタイプになりました。対応するIDについても同じです。Id->タイプの解決は実行時に行う必要があります。

よろしくお願いします、ロビン。

4

3 に答える 3

9

外部ポリモーフィズム(*)

これは広く知られているイディオムですが、広く使用されていshared_ptrます。実装で最初に遭遇し、ツールボックスで非常に役立ちました。

アイデアは、これらすべてのタイプの基本クラスを実際に作成することです。しかし、それらを直接派生させないでください。

class Holder {
public:
    virtual ~Holder() {}

    virtual void verify(unsigned char const* bytes, size_t size) const = 0;

}; // class Holder

template <typename T>
class HolderT: public Holder {
public:
     HolderT(): _t() {}

     virtual void verify(unsigned char const* bytes, size_t size) const {
         _t.verify();
     }

private:
    T _t;
}; // class HolderT

template <typename T>
std::unique_ptr<Holder> make_holder() {
    return std::unique_ptr<Holder>(new HolderT<T>());
}

つまり、これは新しいレベルの間接参照を追加するという古典的な戦略です。

さて、値からクラスに移動するには、明らかにスイッチが必要です。または多分...地図?

using maker = std::unique_ptr<Holder> (&)();
using maker_map = std::unordered_map<types, maker>;

std::unique_ptr<Holder> select(types const E) {
    static maker_map mm;
    if (mm.empty()) {
        mm.insert(std::make_pair(E_T1, make_holder<EC_T1>));
        // ...
    }

    maker_map::const_iterator it = mm.find(E);

    if (it == mm.end()) { return std::unique_ptr<Holder>(); }

    return (*it->second)();
 }

そして今、あなたはそれらを多形的に扱うことができます:

void verify(types const E, unsigned char const* bytes, size_t size) {
    std::unique_ptr<Holder> holder = select(E);
    if (not holder) { std::cerr << "Unknown type " << (int)E << "\n"; return; }

    holder->verify(bytes, size);
}

もちろん、ニーズに応じて戦略を変えることもできます。たとえば、マップを移動してselect、タイプを動的に登録できるようにします(プラグインの場合など)。

(*)少なくともそれは私が持っている名前です、私はそれがすでに名前が付けられていることを知ってとてもうれしいです。

于 2012-09-29T14:32:43.783 に答える
2

たとえば、オーバーロードされた関数など、メッセージを処理する一般的な方法があると仮定します。

void handle_message(const E_T1& msg);
void handle_message(const E_T2& msg);
//...

これで、オブジェクトのタイプを実際に取得する必要はありません。必要なのは、デコードされていないメッセージが与えられた場合に、そのタイプのメッセージを処理する方法だけです。

したがって、ファクトリ関数のマップを作成することをお勧めします。

std::unordered_map<types, std::function<void (unsigned char const* bytes, size_t size)> handlers;
handlers[E_E1] = [](unsigned char const* bytes, size_t size) { handle_message(E_T1(bytes, size)); };
// ...

次に、タイプをデコードしたら、を使用handlers[type](bytes, size)してメッセージをデコードおよび処理できます。

于 2012-09-29T14:36:51.863 に答える
2

可変個引数テンプレートと定義済みのgetTypeクラスを試してください。

enum types { T1_ID, T2_ID, .... };
class T1; class T2; class T3; ....

template <types t> struct getType;
template <> struct getType<T1_ID> { typedef T1 type; };  
template <> struct getType<T2_ID> { typedef T2 type; };
...  

そして、操作は次のことを確認します。

template <types...>
struct type_operation;

template <types t1, types... rest> 
struct type_operation<t1, rest...>
{
   void verify(types t)
   {
      if (t == t1) 
      { 
         typename getType<t1>::type a; 
         a.verify(); // read from network and verify the rest of data....
      }
      else type_operation<rest...>::verify(t, data);
   }
};

template <> 
struct type_operation<>
{
   void verify(types t)
   {
      ostringstream log; log << "not suppoted: " << t;
      throw std::runtime_error(log.str()); // 
   }
};

使用法:

typedef type_operation<T1_ID, T2_ID, T3_ID, ,,.., TN_ID> type_mapping;
types id;
readFromNetwork(id);
type_mapping::verify(id);
于 2012-09-29T14:52:01.903 に答える