2016-12-08 10 views
5

交差型を手動で作成せずに2つの型の和集合を作成できますか?C++:仮想基底クラス継承のない2つの型の結合

私の文脈での交差クラスは完全に無意味なので、コードを作成するとコードユーザーが混乱することがあります。

私の実用的なケース: 私は多くのモジュールの階層的なツリー状の構造であるデジタルハードウェアシミュレータを説明しています:

class port; 

class module0 { 
    port a,b,c; 
} 

class module1 { 
    port c,d,e; 
} 

私は、この2種類の和集合を作成する必要があります。

class top_level_module { 
    port a,b,c,d,e; 
} 

私は(これは私が求めている問題である)ユニオン型を作成するためのいくつかのtechiqueがなければならないことを想像:

class top_level_module : union_type < module0, module1 > { 
    // port a,b,c,d,e; 
} 

しかし、私は何も見つかりませんでした。

// this is a meaningless type in my context 
class intersection_of_module0_module1 { 
    port c; 
} 

class module0: virtual intersection_of_module0_module1 { 
    port a,b; 
} 

class module1: virtual intersection_of_module0_module1 { 
    port d,e; 
} 

class top_level_module : module0, module1 { 
    // port a,b,c,d,e; 
} 
+0

もう一つのオプションは、 'top_level_module'が' module0'のインスタンスと 'module1'のインスタンスを所有し、内部ポートへのアクセスを制御する構成です。しかし、2つの間で関数が必要な場合は、おそらく仮想基本クラスのほうが良いでしょう。 – AndyG

答えて

4

あなたが宣言を使用して使用し、2つの構造体のいずれかからcフィールドを促進することができます:私は、ウェブ上で見つけた唯一の解決策は、仮想継承です。一例として、

struct port {}; 

struct module0 { port a, b, c; }; 
struct module1 { port c, d, e; }; 


struct top_level_module: module0, module1 { 
    using module0::c; 
}; 

int main() { 
    top_level_module mod; 
    mod.c = port{}; 
} 

それは正確に2種類の労働組合はありませんが、それはtop_level_moduleを通じてcの用途を明確にすることができます。それはabcdeという名前5つの明示的およびアクセスのフィールドを持っているよう、ユーザーの観点から
は、top_level_moduleが見えます。
それは余分データメンバは、完全修飾名によるcとアクセスを指名した。また、:他の用語で

mod.module1::c 

、メンバーは削除されていない、それはで使用して宣言によって隠されていますトップレベルクラス。

このアプローチにはいくつかの欠点があります。例:

  • 実際に余分なフィールドは削除されていません。したがって、デフォルトで初期化され、その結果を意識する必要があります。
  • top_level_moduleのユーザーは、何とか隠しメンバーを使用できます。
  • top_level_moduleの集約初期化は使用できません。
  • ...

あなたは少しさらに行くと、基礎となるクラスへのアクセスを制限したい場合は、明示的に必要なフィールドprivate継承とエクスポートを使用することができます。確かに

struct top_level_module: private module0, private module1 { 
    using module0::a; 
    using module0::b; 
    using module0::c; 
    using module1::d; 
    using module1::e; 
}; 

冗長を。

+0

これはどのようにして問題を解決しますか?あなたのmixinにはまだOPが取り除こうとしているものである 'port c'の2つのコピーが含まれています – Praetorian

+0

@Praetorian答えを更新しました。誤解に気づいてくれてありがとう。 – skypjack

+0

ありがとうございました!しかし残念なことに、あなたが名前をつけた欠点のためにこの解決法を適用することはできません。 – random

4

定型句のないメンバーの名前を検査することはできないため、別の方法で交点を作成する必要があります。

実際、構造体が何であるかを(まだ)調べることはできません。しかし、タプルを使用することができます。そして、タプルのすべてのメンバーが異なる型を持つ場合、あなたは組合を構成することができます。

portタイプのための単純なラッパーで始まるのをしてみましょう:

template<char... names> 
using module = std::tuple<port_wrapper<names>...>; 

using module1 = module<'a', 'b', 'c'>; 
using module2 = module<'c', 'd', 'e'>; 

その後、あなたが計算するためにメタ関数を使用することができます:あなたのモジュールはタプルを使用してから作られているかを定義、そして、

template<char name> 
struct port_wrapper : port {}; 

モジュールの交差点:

template<char...> 
struct name_list {}; 

template<typename> 
struct to_name_list_impl; 

template<char... names> 
struct to_name_list_impl<std::tuple<port_wrapper<names>...>> { 
    using list = name_list<names...>; 
}; 

template<typename module> 
using to_name_list = typename to_name_list_impl<module>::list; 

template <char, typename> 
struct name_list_contains; 

template <char c> 
struct name_list_contains<c, name_list<>> : std::false_type {}; 

template <char c, char head, char... tail> 
struct name_list_contains<c, name_list<head, tail...>> : name_list_contains<c, name_list<tail...>> {}; 

template <char c, char... tail> 
struct name_list_contains<c, name_list<c, tail...>> : std::true_type {}; 

template<typename, typename> 
struct name_list_concat; 

template<char... names1, char... names2> 
struct name_list_concat<name_list<names1...>, name_list<names2...>> { 
    using list = name_list<names1..., names2...>; 
}; 

template<typename...> 
struct all_names; 

template<> 
struct all_names<> { 
    using list = name_list<>; 
}; 

template<char... names, typename... Tail> 
struct all_names<name_list<names...>, Tail...> { 
    using list = typename name_list_concat<name_list<names...>, typename all_names<Tail...>::list>::list; 
}; 

template<typename> 
struct unique_names; 

template<> 
struct unique_names<name_list<>> { 
    using list = name_list<>; 
}; 

template<char first, char... others> 
struct unique_names<name_list<first, others...>> { 
    using uniques = typename unique_names<name_list<others...>>::list; 

    using list = std::conditional_t< 
     name_list_contains<first, uniques>::value, 
     uniques, 
     typename name_list_concat<uniques, name_list<first>>::list 
    >; 
}; 

template<typename> 
struct module_union_impl; 

template<char... names> 
struct module_union_impl<name_list<names...>> { 
    using module_name_union = module<names...>; 
}; 

template<typename... modules> 
using module_union = typename module_union_impl< 
    typename unique_names< 
     typename all_names< 
      to_name_list<modules>... 
     >::list 
    >::list 
>::module_name_union; 

このコードでは、今、このようmodule_unionを使用します。

using top_level_module = module_union<module1, module2>; 

Live at Coliru

Additionnaly、1は、ポートのタプルから伝統的な構造体を構築するユーティリティを作ることができます。

これは楽しいことでした。

+0

素晴らしいアイデア!それはうまくいくように見えますが、私のモジュールは実際にポートの集合以上のものであるため、おそらく別の "port_bundle"クラスを持っています。あなたは言いました:「確かに、構造体が何であるかを(まだ)調べることはできません。」C++ 1z/2xでコンパイル時のリフレクション・メカニズムと交差/結合することは可能でしょうか? – random

+1

@random th static reflexion proposalはTSに向かい、C++ 17(そして、ポッド上にいくつかのトリックがある14)はstructを分解させます。 –

関連する問題