3

オブジェクトのリストでその関数を呼び出す関数を定義するマクロを作成したいと思います。必ずしもプリプロセッサマクロである必要はありませんが、機能するはずです。

私はこのようなものを書きたいです:

CALL_ON_ALL(doSomething(int arg1, bool arg2))

そして私はそれがこれを生み出すことを望みます:

void doSomething(int arg1, bool arg2) {
    for (int i = 0; i < delegates.size(); i++)
        delegates[i]->doSomething(arg1, arg2);
}

私はうまくいくものがあります:

#define CALL_ON_ALL(defSig, callSig) \
void defSig { \
    for (int i = 0; i < delegates.size(); i++) \
        delegates[i]->callSig; \
}

問題は、次のように定義シグニチャと呼び出しシグニチャを別々に記述しなければならないことです。

CALL_ON_ALL(doSomething(int arg1, bool arg2), doSomething(arg1, arg2))

これを行うためのより良い方法はありますか?

編集

必ずしもプリプロセッサマクロである必要はありません。うまくいくものなら何でもかまいません。

4

5 に答える 5

6

別々の引数から署名を作成してみてください (2 つの引数の場合):

#define CALL_ON_ALL2(name, argType1, argName1, argType2, argName2) \
void name( argType1 argName1 , argType2 argName2 ) { \
    for (int i = 0; i < delegates.size(); i++) \
        delegates[0]->name( argName1 , argName2 ); \
}

他の引数番号に対してこれを複製できます。

于 2013-01-05T18:51:18.740 に答える
5

これは、引数として別の関数を取る関数である高階関数の良い例です。この場合、すべての要素で呼び出される関数。

f以下は、すべての要素で呼び出す高階関数を定義しています。可変個引数テンプレート ( ) には C++11 が必要args...です。C++11 を利用できない場合は、typename ...Args代わりに を削除して、関数シグネチャの固定引数型を使用できます。

template<typename Delegates, typename Function, typename... Args>
void callOnAll(Delegates delegates, Function f, Args... args) {
    for (int i = 0; i < delegates.size(); i++)
        f(delegates[i], args...);
}

これで、次の構文を使用してこれを呼び出すことができます。

callOnAll(delegates, std::mem_fun<void,Delegate>(&Delegate::doSomething), 42);

Thingstd::mem_funは、デリゲートごとに呼び出すメンバー関数の一時関数オブジェクトを作成します。最初の引数としてポインターを使用して、他の関数を適用することもできます。たとえば、この小さなラムダ関数 (これも C++11 以降のみ):

callOnAll(delegates, [](Delegate *d){
    d->doSomething(42);
});

これはほとんど同じで、別の構文です。

ここで例を見てください:


このコードのわずかに異なるバージョンでは、インデックス ベースの for ループの代わりに範囲ベースの for ループを使用しています。

template<typename Delegates, typename Function, typename... Args>
void callOnAll(Delegates delegates, Function f, Args... args) {
    for(auto d : delegates)
        f(d, args...);
}

参考までに、std::for_each必要なことをほとんど実行する がありますが、それ自体では関数パラメーターを使用しないため、もう少し機能的な方法で機能しますが、へのポインターのみを受け取るラムダ関数/ファンクターを提供します実例。次のコードを、ラムダ関数を使用した上記のコードと比較してください。

std::for_each(delegates.begin(), delegates.end(), [](Delegate *d){
    d->doSomething(42);
});

唯一の違い.begin().end()delegates. ただし、標準ライブラリで定義されているその他のアルゴリズムは、一見の価値があります。

于 2013-01-05T18:51:38.617 に答える
2

これがあなたが探しているものだと思います:

#define _NUM_ARGS2(X,X64,X63,X62,X61,X60,X59,X58,X57,X56,X55,X54,X53,X52,X51,X50,X49,X48,X47,X46,X45,X44,X43,X42,X41,X40,X39,X38,X37,X36,X35,X34,X33,X32,X31,X30,X29,X28,X27,X26,X25,X24,X23,X22,X21,X20,X19,X18,X17,X16,X15,X14,X13,X12,X11,X10,X9,X8,X7,X6,X5,X4,X3,X2,X1,N,...) N
#define NUM_ARGS(...) _NUM_ARGS2(0, ##__VA_ARGS__ ,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)

#define MAKE_PARAMS_0()
#define MAKE_PARAMS_1(type) type arg1
#define MAKE_PARAMS_2(type1, type2) type1 arg1, type2 arg2
#define MAKE_PARAMS_3(type1, type2, type3) type1 arg1, type2 arg2, type3 arg3
//.. add as many MAKE_PARAMS_* as you need

#define MAKE_PARAMS_N(N, ...) MAKE_PARAMS_##N(__VA_ARGS__)
#define MAKE_PARAMS_FORCE_N(N, ...) MAKE_PARAMS_N(N, __VA_ARGS__)
#define MAKE_PARAMS(...) MAKE_PARAMS_FORCE_N(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)


#define MAKE_ARGS_0()
#define MAKE_ARGS_1(type) arg1
#define MAKE_ARGS_2(t1, t2) arg1, arg2
#define MAKE_ARGS_3(t1, t2, t3) arg1, arg2, arg3
//.. add as many MAKE_ARGS_* as you have MAKE_PARAMS_*

#define MAKE_ARGS_N(N, ...) MAKE_ARGS_##N(__VA_ARGS__)
#define MAKE_ARGS_FORCE_N(N, ...) MAKE_ARGS_N(N, __VA_ARGS__)
#define MAKE_ARGS(...) MAKE_ARGS_FORCE_N(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)

#define CALL_ON_ALL(fun, ...) \
    void fun(MAKE_PARAMS(__VA_ARGS__)) { \
        for (int i = 0; i < delegates.size(); i++) \
            delegates[i]->fun(MAKE_ARGS(__VA_ARGS__)); \
    }

CALL_ON_ALL(doSomething, int, bool)

これは生成します

void doSomething(int arg1, bool arg2) { for (int i = 0; i < delegates.size(); i++) delegates[i]->doSomething(arg1, arg2); }

このすべての「混乱」がどのように機能するかについての詳細は、次の場所にあります: Variadic recursive preprocessor macros - is it possible?

于 2013-01-05T20:22:18.737 に答える
1

まず、型を括弧で囲み、プリプロセッサが解析できるようにします。したがって、次のように呼び出しますCALL_ON_ALL

CALL_ON_ALL(doSomething, (int) arg1, (bool) arg2)

型を取得して型を削除するマクロを次に示します (それらに名前を付ける必要があります。デモ用に名前空間を省略しています)。

#define EAT(...)
#define REM(...) __VA_ARGS__
#define STRIP(x) EAT x
#define PAIR(x) REM x

これらのマクロは次のように機能します。と書くSTRIP((int) arg1)と展開されarg1ます。そして、あなたが書くとき、PAIR((int) arg1)それはに展開されint arg1ます。次に、渡された各引数にこれらのマクロを適用するAPPLY必要があるため、最大 8 つの引数に対してこれを実行できる単純なマクロを次に示します。

/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)

/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT(x, y) PRIMITIVE_CAT(x, y)

/* This will call a macro on each argument passed in */
#define APPLY(macro, ...) CAT(APPLY_, NARGS(__VA_ARGS__))(macro, __VA_ARGS__)
#define APPLY_1(m, x1) m(x1)
#define APPLY_2(m, x1, x2) m(x1), m(x2)
#define APPLY_3(m, x1, x2, x3) m(x1), m(x2), m(x3)
#define APPLY_4(m, x1, x2, x3, x4) m(x1), m(x2), m(x3), m(x4)
#define APPLY_5(m, x1, x2, x3, x4, x5) m(x1), m(x2), m(x3), m(x4), m(x5)
#define APPLY_6(m, x1, x2, x3, x4, x5, x6) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6)
#define APPLY_7(m, x1, x2, x3, x4, x5, x6, x7) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7)
#define APPLY_8(m, x1, x2, x3, x4, x5, x6, x7, x8) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8)

CALL_ON_ALLマクロを作成する方法は次のとおりです。

#define CALL_ON_ALL(func, ...) \
void func(APPLY(PAIR, __VA_ARGS__)) { \
    for (int i = 0; i < delegates.size(); i++) \
        delegates[i]->func(APPLY(STRIP, __VA_ARGS__)); \
}

注: MSVC にはバグのあるプリプロセッサがあるため (回避策はありますが)、これはおそらく MSVC では機能しません。

于 2013-01-12T01:36:11.477 に答える
1

C には可変引数マクロがあり、ここでは非常に便利だと思います。ただし、それらは通常 C++ の一部ではありませんが (微妙な違い)、それらを実装するほとんどのコンパイラはそれらを C に限定していません。

C++ では、特に C++11 の可変個引数テンプレート (および完全転送) を使用して、テンプレートに到達することをお勧めします。ただし、楽しみと利益のために、プリプロセッサを使用しようとします...

実際の C++03 プリプロセッサ ソリューションが必要な場合は、Boost.Preprocessor を呼び出します。真のキッカーは、プリプロセッサ シーケンスです。これは、自由に操作できる無制限の (理論上*) 要素のリストであり、可変引数マクロの利便性に十分近づけます。

しかし、そこを掘り下げる前に、引数の名前はまったく重要ではなく、その型だけが本当に重要であることに注意する必要があります。

したがって、次の構文を提案します。

CALL_ON_ALL(dosomething, (int)(bool))

// (int)(bool) is a preprocessor sequence in Boost.Preprocessor

内部はもう少し微妙で、最初の試行で正しく理解できるかどうかはわかりませんが、おそらく次のようになるはずです。

#define CALL_ON_ALL(Name_, Args_)                                               \
    void Name_ ( BOOST_PP_ENUM( BOOST_PP_SEQ_SIZE(Args_), ARG_ENUM, Args_) ) {  \
        for (size_t i = 0, max = delegates.size(); i != max; ++i) {             \
            delegates[i]->                                                      \
                Name_ ( BOOST_PP_ENUM_PARAMS( BOOST_PP_SEQ_SIZE(Args_), arg );  \
        }                                                                       \
    }

#define ARG_ENUM(z, n, data)                                                    \
    BOOST_PP_SEQ_ELEM(data, n) BOOST_PP_CAT(arg, n)

注: 複雑さはそれほど大きくありません。N 個の呼び出しがBOOST_PP_SEQ_ELEMあり、それ自体が線形であり、2 次の複雑さにつながります。BOOST_PP_SEQ_*引数列挙してそれらの間にコンマを配置できるマクロが見つかりませんでした。

*実際には、ドキュメントに 100 近くの要素を含むデモがあります。それで十分であることを願っています ;)

于 2013-01-05T19:26:58.467 に答える