12

仮想関数の代わりにkindフィールドを使用して動的ディスパッチを行う従来のコードがあります。仮想関数を使用しないC++動的ディスパッチ

// Base struct shared by all subtypes 
// Plain-old data; can't use virtual functions 
struct POD 
{ 
    int kind; 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 
    int GetXyzzy(); 
}; 

enum Kind { Kind_Derived1, Kind_Derived2, Kind_Derived3 /* , ... */ }; 

struct Derived1: POD 
{ 
    Derived1(): kind(Kind_Derived1) {} 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 
    int GetXyzzy(); 

    // ... plus other type-specific data and function members ... 
}; 

struct Derived2: POD 
{ 
    Derived2(): kind(Kind_Derived2) {} 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 
    int GetXyzzy(); 

    // ... plus other type-specific data and function members ... 
}; 

struct Derived3: POD 
{ 
    Derived3(): kind(Kind_Derived3) {} 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 
    int GetXyzzy(); 

    // ... plus other type-specific data and function members ... 
}; 

// ... and so on for other derived classes ... 

、その後PODクラスの機能のメンバーは次のように実装されています:それはこのようなものになります

int POD::GetFoo() 
{ 
    // Call kind-specific function 
    switch (kind) 
    { 
    case Kind_Derived1: 
     { 
     Derived1 *pDerived1 = static_cast<Derived1*>(this); 
     return pDerived1->GetFoo(); 
     } 
    case Kind_Derived2: 
     { 
     Derived2 *pDerived2 = static_cast<Derived2*>(this); 
     return pDerived2->GetFoo(); 
     } 
    case Kind_Derived3: 
     { 
     Derived3 *pDerived3 = static_cast<Derived3*>(this); 
     return pDerived3->GetFoo(); 
     } 

    // ... and so on for other derived classes ... 

    default: 
     throw UnknownKindException(kind, "GetFoo"); 
    } 
} 

POD::GetBar()POD::GetBaz()POD::GetXyzzy()を、そして他のメンバーも同様に実装されています。

この例は簡略化されています。実際のコードには約12種類の異なるサブタイプPODと数十種類のメソッドがあります。 PODの新しいサブタイプと新しいメソッドがかなり頻繁に追加されるので、そのたびにこれらのすべての文を更新する必要があります。

これを処理する一般的な方法は、関数メンバーvirtualPODクラスに宣言することですが、オブジェクトは共有メモリに存在するため、これを行うことはできません。これらの構造体に依存する多くのコードがプレーン・データであるため、共有メモリ・オブジェクトに仮想関数を持たせるための何らかの方法を見つけることができますが、そのようにしたくありません。

私はサブタイプメソッドを呼び出す方法に関するすべての知識が1つの場所に集約されるように、これをクリーンアップするための最良の方法を提案しています.カップルダースの機能。

私には、PODをラップし、冗長性を最小限に抑えるためにテンプレートを使用するアダプタクラスを作成できます。しかし、私がその道を始める前に、他の人たちがこれをどのように扱っているか知りたいと思います。

+0

あなたは多くがあったと述べましたこのクラスに応じてコードのあなたはそれにフィールドを追加することができますか、または構造は同じままにする必要がありますか? –

+0

構造は基本的に同じでなければなりません。私たちは共有メモリにこれらのものの膨大な配列を持っており、既にメモリサイズの限界にぶつかっています。 –

+0

複数のプロセスがすべて同じバージョンのライブラリを持つかどうか –

答えて

11

ジャンプテーブルを使用できます。これは、ほとんどの仮想ディスパッチがボンネットのように見えるため、手動で構築することができます。

template<typename T> int get_derived_foo(POD*ptr) { 
    return static_cast<T>(ptr)->GetFoo(); 
} 
int (*)(POD*) funcs[] = { 
    get_derived_foo<Derived1>, 
    get_derived_foo<Derived2>, 
    get_derived_foo<Derived3> 
}; 
int POD::GetFoo() { 
    return funcs[kind](this); 
} 

短い例として、

共有メモリに存在することの制限は正確には何ですか?私はここで十分なことを知らないことに気づいた。別のプロセスの誰かがそれらのポインタを使用しようとしているので、私はポインタを使用できないことを意味しますか?

文字列マップを使用すると、各プロセスで独自のマップのコピーが取得できます。 GetFoo()に渡して見つけられるようにする必要があります。

struct POD { 
    int GetFoo(std::map<int, std::function<int()>& ref) { 
     return ref[kind](); 
    } 
}; 

編集:もちろん、ここでは文字列を使用する必要はありません。intを使用できます。私は例として使っただけです。私はそれを元に戻すべきです。事実、このソリューションは非常に柔軟ですが、重要なのはプロセス固有のデータのコピーを作成することです。関数ポインタなどを入力してから渡してください。

+0

共有メモリ内のオブジェクトは、複数のプロセスで使用されます。仮想関数ポインタは、すべてのプロセスに対して有効ではありません。 –

+2

詳細は見ていませんが、一般的に、テーブルは各オブジェクトのポインタによって参照されます。スレッドがアドレスYにタイプXのvtableを持つ場合、スレッドはオブジェクトのvptrフィールドにYを格納します。vtableがシステム内の全く同じハードウェアアドレスに格納されていても、2つの異なるプロセスは異なる論理アドレスでそれを見ることはできません。そうであれば、別のスレッドはvtableを間違ったアドレスで使用しようとし、死ぬでしょう。 –

+1

ニースですが、配列を正しく生成することができたら嬉しいです。プリプロセッサマクロはそれを行うことができます。 –

0

ここでは、Curiously recurringテンプレートパターンを使用した例を示します。コンパイル時に詳細を知っていれば、これはあなたのニーズに合うかもしれません。ここ

template<class DerivedType> 
struct POD 
{ 
    int GetFoo() 
    { 
     return static_cast<DerivedType*>(this)->GetFoo(); 
    } 
    int GetBar() 
    { 
     return static_cast<DerivedType*>(this).GetBar(); 
    } 
    int GetBaz() 
    { 
     return static_cast<DerivedType*>(this).GetBaz(); 
    } 
    int GetXyzzy() 
    { 
     return static_cast<DerivedType*>(this).GetXyzzy(); 
    } 
}; 

struct Derived1 : public POD<Derived1> 
{ 
    int GetFoo() 
    { 
     return 1; 
    } 
    //define all implementations 
}; 

struct Derived2 : public POD<Derived2> 
{ 
    //define all implementations 

}; 

int main() 
{ 
    Derived1 d1; 
    cout << d1.GetFoo() << endl; 
    POD<Derived1> *p = new Derived1; 
    cout << p->GetFoo() << endl; 
    return 0; 
} 
+0

しかし、実際にサブタイプを指している 'POD *'で始まって 'GetFoo()'を呼びたいのであれば、私は何をしますか? –

1

は、実際に仮想関数を有するようにポッドクラスまたは派生クラスを必要とすることなく、ジャンプテーブルを実装する仮想メソッドを使用するアプローチです。

目的は、多くのクラスにわたってメソッドを追加したり削除したりすることを簡単にすることです。

メソッドを追加するには、明確かつ共通のパターンを使用してPodに追加する必要があります.PodInterfaceには純粋な仮想関数を追加する必要があります。また、明確で共通のパターンを使用してPodFuncsに転送関数を追加する必要があります。

派生クラスは、ファイル静的初期化オブジェクトを持つだけで済みます。それ以外の場合は、すでに行われているように見えます。

// Pod header 

#include <boost/shared_ptr.hpp> 
enum Kind { Kind_Derived1, Kind_Derived2, Kind_Derived3 /* , ... */ }; 

struct Pod 
{ 
    int kind; 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 
}; 

struct PodInterface 
{ 
    virtual ~PodInterface(); 

    virtual int GetFoo(Pod* p) const = 0; 
    virtual int GetBar(Pod* p) const = 0; 
    virtual int GetBaz(Pod* p) const = 0; 

    static void 
    do_init(
      boost::shared_ptr<PodInterface const> const& p, 
      int kind); 
}; 

template<class T> struct PodFuncs : public PodInterface 
{ 
    struct Init 
    { 
     Init(int kind) 
     { 
      boost::shared_ptr<PodInterface> t(new PodFuncs); 
      PodInterface::do_init(t, kind); 
     } 
    }; 

    ~PodFuncs() { } 

    int GetFoo(Pod* p) const { return static_cast<T*>(p)->GetFoo(); } 
    int GetBar(Pod* p) const { return static_cast<T*>(p)->GetBar(); } 
    int GetBaz(Pod* p) const { return static_cast<T*>(p)->GetBaz(); } 
}; 


// 
// Pod Implementation 
// 

#include <map> 

typedef std::map<int, boost::shared_ptr<PodInterface const> > FuncMap; 

static FuncMap& get_funcmap() 
{ 
    // Replace with other approach for static initialisation order as appropriate. 
    static FuncMap s_funcmap; 
    return s_funcmap; 
} 

// 
// struct Pod methods 
// 

int Pod::GetFoo() 
{ 
    return get_funcmap()[kind]->GetFoo(this); 
} 

// 
// struct PodInterface methods, in same file as s_funcs 
// 

PodInterface::~PodInterface() 
{ 
} 

void 
PodInterface::do_init(
     boost::shared_ptr<PodInterface const> const& p, 
     int kind) 
{ 
    // Could do checking for duplicates here. 
    get_funcmap()[kind] = p; 
} 

// 
// Derived1 
// 

struct Derived1 : Pod 
{ 
    Derived1() { kind = Kind_Derived1; } 

    int GetFoo(); 
    int GetBar(); 
    int GetBaz(); 

    // Whatever else. 
}; 

// 
// Derived1 implementation 
// 

static const PodFuncs<Derived1>::Init s_interface_init(Kind_Derived1); 

int Derived1::GetFoo() { /* Implement */ } 
int Derived1::GetBar() { /* Implement */ } 
int Derived1::GetBaz() { /* Implement */ } 
1

ここで私は今ダウンしているテンプレートメタプログラミングの経路です。ここで私はそれについて好きなものです:

  • 新しい種類のサポートを追加するだけLAST_KINDを更新し、新しいKindTraitsを追加する必要があります。
  • 新しい機能を追加するための単純なパターンがあります。
  • 関数は、必要に応じて特定の種類に特化できます。
  • 私は何かを壊すと、不思議な実行時の誤動作ではなく、コンパイル時のエラーと警告を期待できます。

懸念がいくつかあります:

  • PODの実装は今、すべての派生クラスのインタフェースに依存しています。 (これは既存の実装では既に当てはまりますが、私は心配していませんが、それはちょっと臭いです。)
  • 私はコンパイラがほぼ同等のコードを生成するほどスマートであると考えていますswitchに基づいています。
  • 多くのC++プログラマは、これを見て頭を傷つけるでしょう。

ここでは、コードです:あなたがになってしまったソリューションを拡張し

// Declare first and last kinds 
const int FIRST_KIND = Kind_Derived1; 
const int LAST_KIND = Kind_Derived3; 

// Provide a compile-time mapping from a kind code to a subtype 
template <int KIND> 
struct KindTraits 
{ 
    typedef void Subtype; 
}; 
template <> KindTraits<Kind_Derived1> { typedef Derived1 Subtype; }; 
template <> KindTraits<Kind_Derived2> { typedef Derived2 Subtype; }; 
template <> KindTraits<Kind_Derived3> { typedef Derived3 Subtype; }; 

// If kind matches, then do the appropriate typecast and return result; 
// otherwise, try the next kind. 
template <int KIND> 
int GetFooForKind(POD *pod) 
{ 
    if (pod->kind == KIND) 
     return static_cast<KindTraits<KIND>::Subtype>(pod)->GetFoo(); 
    else 
     return GetFooForKind<KIND + 1>(); // try the next kind 
} 

// Specialization for LAST_KIND+1 
template <> int GetFooForKind<LAST_KIND + 1>(POD *pod) 
{ 
    // kind didn't match anything in FIRST_KIND..LAST_KIND 
    throw UnknownKindException(kind, "GetFoo"); 
} 

// Now POD's function members can be implemented like this: 

int POD::GetFoo() 
{ 
    return GetFooForKind<FIRST_KIND>(this); 
} 
+1

あなたはUnkindExceptionをスローする機会を逃しました!このアプローチでは、POD実装があらゆる派生型の定義を見たことが必要です。コードの現在の状態を考えると、気にする必要はありませんが、要件はそこにあります。 – janm

+0

"懸念事項"セクションに依存に関する注意を追加しました。 –

0

、以下はプログラムの初期化時に導出関数へのマッピングを解決します

#include <typeinfo> 
#include <iostream> 
#include <functional> 
#include <vector> 

enum Kind 
{ 
    Kind_First, 
    Kind_Derived1 = Kind_First, 
    Kind_Derived2, 
    Kind_Total 
}; 

struct POD 
{ 
    size_t kind; 

    int GetFoo(); 
    int GetBar(); 
}; 

struct VTable 
{ 
    std::function<int(POD*)> GetFoo; 
    std::function<int(POD*)> GetBar; 
}; 

template<int KIND> 
struct KindTraits 
{ 
    typedef POD KindType; 
}; 

template<int KIND> 
void InitRegistry(std::vector<VTable> &t) 
{ 
    typedef typename KindTraits<KIND>::KindType KindType; 

    size_t i = KIND; 
    t[i].GetFoo = [](POD *p) -> int { 
     return static_cast<KindType*>(p)->GetFoo(); 
    }; 
    t[i].GetBar = [](POD *p) -> int { 
     return static_cast<KindType*>(p)->GetBar(); 
    }; 

    InitRegistry<KIND+1>(t); 
} 
template<> 
void InitRegistry<Kind_Total>(std::vector<VTable> &t) 
{ 
} 

struct Registry 
{ 
    std::vector<VTable> table; 

    Registry() 
    { 
     table.resize(Kind_Total); 
     InitRegistry<Kind_First>(table); 
    } 
}; 

Registry reg; 

int POD::GetFoo() { return reg.table[kind].GetFoo(this); } 
int POD::GetBar() { return reg.table[kind].GetBar(this); } 

struct Derived1 : POD 
{ 
    Derived1() { kind = Kind_Derived1; } 

    int GetFoo() { return 0; } 
    int GetBar() { return 1; } 
}; 
template<> struct KindTraits<Kind_Derived1> { typedef Derived1 KindType; }; 

struct Derived2 : POD 
{ 
    Derived2() { kind = Kind_Derived2; } 

    int GetFoo() { return 2; } 
    int GetBar() { return 3; } 
}; 
template<> struct KindTraits<Kind_Derived2> { typedef Derived2 KindType; }; 

int main() 
{ 
    Derived1 d1; 
    Derived2 d2; 
    POD *p; 

    p = static_cast<POD*>(&d1); 
    std::cout << p->GetFoo() << '\n'; 
    p = static_cast<POD*>(&d2); 
    std::cout << p->GetBar() << '\n'; 
} 
関連する問題