5

オブジェクトのセットをインスタンス化して初期化するために使用されるメソッドのセットがあります。Init関数に渡される引数の数を除いて、それらはすべてほとんど同じように見えます。

ObjectType* CreateObjectType(Arg1 a1, Arg2 arg2, ... ArgN aN)
{
    ObjectType* object = new ObjectType();
    [...]
    object->Init(this, a1, a2, ..., aN);
    [...]
    return object;
}

引数は、 Init関数に渡す以外の場所では使用しないことに注意してください。

オブジェクトの種類ごとにコードを複製することなく、これらすべてを実装する方法を見つけたいと思います。


次の(無効な)結果で、可変長マクロを使用してみました:

#define CREATE_OBJECT_IMPL(ObjectType, ...)    \
ObjectType* Create##ObjectType##(__VA_ARGS__)  \
{                                              \
    ObjectType* object = new ObjectType();     \
    [...]
    object->Init(this, ##__VA_ARGS__);         \
    [...]
    return object;                             \
}

// This is the result I am trying to achieve :
CREATE_OBJECT_IMPL(MyFirstObject, bool, float)
CREATE_OBJECT_IMPL(MySecondObject, int)
CREATE_OBJECT_IMPL(MyThirdObject)

さて、この実装では、VA_ARGS を 2 回使用しましたが、どちらも間違っています。

  • 最初のケースでは、指定した型 (Arg1 a1、Arg2 a2...) の引数のリストが必要です。

  • 2 番目のケースでは、これらの引数を名前 ( Init(a1, a2...) ) で呼び出したいと思います。


可変個引数テンプレートを使用してみました:

template< typename ObjectType, typename... Args >
ObjectType* CreateObject(Args args)
{
    ObjectType* object = new ObjectType();
    [...]
    object->Init(this, args);
    [...]
    return object;
}

#define CREATE_OBJECT_IMPL(ObjectType, ...)                     \
ObjectType* Create##ObjectType##(__VA_ARGS__)                   \
{                                                               \
    return CreateObject<ObjectType, __VA_ARGS__>(__VA_ARGS__);  \
}

...しかし、これもうまくいかないようです。テンプレート定義行で次のエラーが発生します。

エラー C2143: 構文エラー: '...' の前に ',' がありません

エラー C2065: 'Args': 宣言されていない識別子

私はVS2012を使用しています。

引数の数ごとに N 個の同様のマクロを記述できますが、コードを複製せずに同じ結果を得る方法があるかどうか疑問に思っていました。

4

2 に答える 2

9

この問題を解決するには、いくつかの方法があります。まず、型を解析できるように、マクロで型付き式を使用できます。したがって、CREATE_OBJECT_IMPLは次のように呼び出されます。

CREATE_OBJECT_IMPL(Object, (Arg1) arg1, (Arg2) arg2)

タイプを取得してタイプを削除するマクロを次に示します。

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

これらのマクロは次のように機能します。と書くSTRIP((Arg1) arg1)と展開されarg1ます。そして、あなたが書くとき、PAIR((Arg1) arg1)それはに展開されArg1 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)

CREATE_OBJECT_IMPL次に、次のように定義できます。

#define CREATE_OBJECT_IMPL(ObjectType, ...) \
ObjectType* Create##ObjectType(APPLY(PAIR, __VA_ARGS__))  \
{ \
    ObjectType* object = new ObjectType(); \
    [...] \
    object->Init(this, APPLY(STRIP, __VA_ARGS__)); \
    [...] \
    return object; \
}

もちろん、これらのマクロを Visual Studio で使用する場合は、いくつかの回避策が必要になる場合があります。もちろん、より良い解決策は、テンプレート化された関数を作成することです。したがって、次のように呼び出しますCreateObject

ObjectType* obj = CreateObject<ObjectType>(arg1, arg2, arg3);

C++11 では、次のように可変長テンプレートを使用できます。

template< typename ObjectType, typename... Args >
ObjectType* CreateObject(Args... args)
{
    ObjectType* object = new ObjectType();
    [...]
    object->Init(this, args...);
    [...]
    return object;
}

ただし、コンパイラが varidiac テンプレートをサポートしていない場合は、Boost.PPを使用して、最大 10 個の引数 (必要に応じてそれ以上) のオーバーロードを生成できます。

#define GENERATE_OBJS_EACH(z, n, data) \
template<class ObjectType, BOOST_PP_ENUM_PARAMS_Z(z, n, class Arg)> \
ObjectType* CreateObject(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, Arg, arg))  \
{ \
    ObjectType* object = new ObjectType(); \
    [...] \
    object->Init(this, BOOST_PP_ENUM_PARAMS_Z(z, n, arg)); \
    [...] \
    return object; \
}
/* Generate CreateObject template for up to 10 arguments */
BOOST_PP_REPEAT_FROM_TO_1(1, 10, GENERATE_OBJS_EACH, ~)

編集: 上記のマクロを msvc で動作させるために必要な回避策は次のとおりです。

/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS_MSVC_WORKAROUND(x) NARGS_SEQ x
#define NARGS(...) NARGS_MSVC_WORKAROUND((__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_MSVC_WORKAROUND(x) PRIMITIVE_CAT x
#define CAT(x, y) CAT_MSVC_WORKAROUND((x, y))

/* This will call a macro on each argument passed in */
#define APPLY(macro, ...) APPLY_MSVC_WORKAROUND(CAT(APPLY_, NARGS(__VA_ARGS__)), (macro, __VA_ARGS__))
#define APPLY_MSVC_WORKAROUND(m, x) m x
...
于 2013-08-18T05:38:22.583 に答える
0

両方Argsargs ここの後に ... を入れる必要があります:

ObjectType* CreateObject(Args args)

そしてここ:

object->Init(this, args);

コードは次のようになります。

template< typename ObjectType, typename... Args >
ObjectType* CreateObject(Args... args)
{
    ObjectType* object = new ObjectType();
    [...]
    object->Init(this, args...);
    [...]
    return object;
}

もう 1 つの問題は、Visual Studio 2012 では可変個引数テンプレートがサポートされていないことですが、2012 年 11 月リリースではサポートされています。コンパイラの最新リリースがあるかどうかを確認してください。

また、新しい関数を再作成するために可変長マクロは必要ありません。次のように ObjectType を指定できます。

ObjectType* obj = CreateObject<ObjectType>(foo, 1, "hi");
于 2013-08-14T16:39:29.467 に答える