17

次のような構造体がいくつかあるとしましょう。

struct MyStruct1 {

    inline void DoSomething() {
        cout << "I'm number one!" << endl;
    }

};

struct MyStruct2 {

    static int DoSomething() {

        cout << "I'm the runner up." << endl;
        return 1;

    }

};

struct MyStruct3 {

    void (*DoSomething)();

    MyStruct3() {
        DoSomething = &InternalFunction;
    }

    static void InternalFunction() {
        cout << "I'm the tricky loser." << endl;
    }

};

ご覧のとおり、3 つの構造体すべてについて、その構造体のオブジェクトに対して DoSomething() を呼び出して動作させることができます (ただし、これは構造体ごとに異なる方法で実現されます)。

MyStruct1 a;
MyStruct2 b;
MyStruct3 c;

a.DoSomething(); // works, calls Struct1's instance function
b.DoSomething(); // works, calls Struct2's static function, discards return value
c.DoSomething(); // works, calls Struct3's function pointer

ここで、これらの構造体を任意に選択してタプルに入れたとします。

tuple<MyStruct2, MyStruct3, MyStruct2, MyStruct1> collection;

DoSomething()また、これらの要素の 1 つを取得し、実行時に決定されるインデックスに基づいてその関数を実行したいとします。これを実現するには、switch ステートメントを使用できます。

switch(index) {

case 0: get<0>(collection).DoSomething(); break;
case 1: get<1>(collection).DoSomething(); break;
case 2: get<2>(collection).DoSomething(); break;
case 3: get<3>(collection).DoSomething(); break;

}

これは問題なくうまく機能しますが、複数の異なる配置 (および 4 要素よりもはるかに長い可能性がある) タプルで実行する必要がある場合、非常に退屈で反復的でエラーが発生しやすくなります。可変個引数テンプレートの要素数に基づいて switch ステートメントを自動的に生成できれば、非常に便利です。擬似コード:

template <typename... T>
void DoSomethingByIndex(int index, tuple<T...>& collection) {

    switch(index) {

    STATIC_REPEAT(sizeof...(T), X) {
    case X: get<X>(collection).DoSomething(); break;
    }

    }

}

これを実現できる C++11 のメカニズムはありますか? そうでない場合は、テンプレート内の関数ポインターのリストを使用してソリューションをハックできることは間違いありませんが、このようなものが存在するかどうかは興味があります。私の目的により適しているからです。switch ステートメントのコンパイラ生成ジャンプ リストは、自家製の関数ポインター ソリューションよりも効率的であると確信しています。

4

2 に答える 2

17

配列を使用して、コンパイル時とランタイムを橋渡しできます。(ab) 可変個引数テンプレートを使用して配列要素を静的に初期化し、ランタイム パラメーターを使用して配列にインデックスを付けます。トリッキーな部分は、配列の適切な要素の型を見つけることです。さらに、テンプレートをタプル要素ではなくタプルインデックスで可変長にする必要があるため、通常のトリックを使用します。

template<int... Indices>
struct indices {
    typedef indices<Indices..., sizeof...(Indices)> next;
};

template<int N>
struct build_indices {
    typedef typename build_indices<N - 1>::type::next type;
};

template<>
struct build_indices<0> {
    typedef indices<> type;
};

// No need to be variadic on the tuple elements as we don't care about them
// So I'm using perfect forwarding for the tuple
template<typename Tuple, int... Indices>
void
do_something_by_index(Tuple&& tuple, int index, indices<Indices...>)
{
    using std::get;

    typedef void (*element_type)(Tuple&&);
    static constexpr element_type table[] = {
        [](Tuple&& tuple)
        { get<Indices>(std::forward<Tuple>(tuple)).DoSomething(); }
        ...
    };

    table[index](std::forward<Tuple>(tuple));
}

// Proverbial layer of indirection to get the indices
template<typename Tuple>
void
do_something_by_index(Tuple&& tuple, int index)
{
    typedef typename std::decay<Tuple>::type decay_type;
    constexpr auto tuple_size = std::tuple_size<decay_type>::value;
    typedef typename build_indices<tuple_size>::type indices_type;

    do_something_by_index(std::forward<Tuple>(tuple), index, indices_type{});
}
于 2011-09-12T05:03:43.297 に答える
1

うーん、私は次のようなことを試してみたくなりました:

template<int N, typename ...Args>
struct call_N_helper
{
  static void call(const std::tuple<Args...> & t, int i)
  {
    if (i == N) std::get<N>(t).call();
    else call_N_helper<N-1, Args...>(t, i);
  }
};

template<typename ...Args>
struct call_N_helper<0, Args...>
{
  static void call(const std::tuple<Args...> & t, int i)
  {
    if (i == 0) std::get<0>(t).call();
  }
};

template<typename ...Args>
void call_N(const std::tuple<Args...> & t, int i)
{
  call_N_helper<sizeof...(Args), Args...>::call(t, i);
}

これは単なるアイデアであり、テストされていません。

于 2011-09-11T23:50:17.877 に答える