web-dev-qa-db-ja.com

マクロは引数の数によってオーバーロードできますか?

this はどのように機能しますか? C99/C++ 11可変長マクロを実装して、引数がいくつ与えられたかに基づいてさまざまなものに拡張するにはどうすればよいですか?

41
Potatoswatter

(編集:既製のソリューションについては最後を参照してください。)

オーバーロードされたマクロを取得するには、最初にいくつかの実装から選択するマクロが必要です。この部分は可変長マクロを使用しません。次に、その引数を一般的にカウントする可変個のマクロがセレクターを生成します。引数countをディスパッチャーに差し込むと、オーバーロードされたマクロが生成されます。

警告:is引数なしと単一の引数の間に違いがないため、このシステムは引数0と1の違いを区別できません空の引数。どちらもMACRO()のように見えます。


実装を選択するには、一連の関数のようなマクロでマクロ連結演算子を使用します。

#define select( selector, ... ) impl ## _ ## selector( __VA_ARGS__ )
#define impl_1() meh
#define impl_2( abc, xyz ) # abc "wizza" xyz()
//etc

// usage: select( 1 ) => impl_1() => meh
//        select( 2, huz, bar ) => impl_2( huzza, bar ) => "huz" "wizza" bar()

##演算子は、引数のマクロ展開を抑制するため、別のマクロでラップすることをお勧めします。

#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )

引数を数えるには、__VA_ARGS__を使用して、そのように引数をシフトします(これは賢い部分です):

#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )

ライブラリコード:

#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )

#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )

#define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)

使用法:

#define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ )
#define MY_OVERLOADED_1( X ) foo< X >
#define MY_OVERLOADED_2( X, Y ) bar< X >( Y )
#define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z()
58
Potatoswatter

以下は Potatoswatterの回答 の改善です。これはcanが0と1の引数を区別します。

簡単に言えば、___VA_ARGS___が空の場合、_VA_SIZE_マクロ内のEXPAND __VA_ARGS__ ()EXPAND ()になり、6つのコンマで置き換えられます。つまり、_VA_SIZE..._はCOMPOSE( GET_COUNT, (,,,,,, , 0, 6, 5, 4, 3, 2, 1) )になり、GET_COUNT (,,,,,, , 0, 6, 5, 4, 3, 2, 1)になって0を返します。

一方、___VA_ARGS___が_int, 5_の場合、EXPAND __VA_ARGS__ ()EXPAND int, 5 ()になります。したがって、Potatoswatterの回答に記載されているように、_VA_SIZE..._はCOMPOSE( GET_COUNT, (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) )になり、GET_COUNT (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1)になり、2を返します。

私は Jason Dangの答え からEXPANDのアイデアを得ました。

ライブラリコード:

_#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )
#define COMPOSE( NAME, ARGS ) NAME ARGS

#define GET_COUNT( _0, _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define EXPAND() ,,,,,, // 6 commas (or 7 empty tokens)
#define VA_SIZE( ... ) COMPOSE( GET_COUNT, (EXPAND __VA_ARGS__ (), 0, 6, 5, 4, 3, 2, 1) )

#define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)
_

使用法:

_#define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ )

#define MY_OVERLOADED_0( ) meh()
#define MY_OVERLOADED_1( X ) foo< X >
#define MY_OVERLOADED_2( X, Y ) bar< X >( Y )
#define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z()

MY_OVERLOADED()                // meh()
MY_OVERLOADED(bool)            // foo< bool >
MY_OVERLOADED(int, 5)          // bar< int >( 5 )
MY_OVERLOADED(me, double, now) // bang_me< double >.now()
_
5

既に回答済みですが、非常に短いバージョンを用意しました。お役に立てれば幸いです。

実装

// Variable Argument Macro (VA_MACRO) upto 6 arguments
#define NUM_ARGS_(_1, _2, _3, _4, _5, _6, TOTAL, ...) TOTAL
#define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 6, 5, 4, 3, 2, 1)

#define CONCATE_(X, Y) X##Y  // Fixed the double '_' from previous code
#define CONCATE(MACRO, NUMBER) CONCATE_(MACRO, NUMBER)
#define VA_MACRO(MACRO, ...) CONCATE(MACRO, NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)

カスタマイズ

// This is how user may define own set of variadic macros
#define MY_MACRO(...) VA_MACRO(MY_MACRO, __VA_ARGS__)
#define MY_MACRO1(_1) "One"
#define MY_MACRO2(_1, _2) "Two"
#define MY_MACRO3(_1, _2, _3) "Three"

使用法

// While using those, user needs to use only the main macro
int main ()
{
  auto one = MY_MACRO(1);
  auto two = MY_MACRO(1, 2); 
  auto three = MY_MACRO(1, 2, 3); 
}
2
iammilind

Potatowatterからソリューションを拡張してiso c99 requires rest arguments to be used gccのコンパイラ切り替え時の問題-pedanticは使用中です。

ライブラリ

#define NUM_ARGS_(_1, _2, _3, _4, _5, _6, _7, _8, TOTAL, ...) TOTAL
#define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define CONCATE_(X, Y) X##Y
#define CONCATE(MACRO, NUMBER) CONCATE_(MACRO, NUMBER)
#define VA_MACRO(MACRO, ...) CONCATE(MACRO, NUM_ARGS (__VA_ARGS__))(__VA_ARGS__)

カスタマイズ

#define MY_OVERLOADED(...) VA_MACRO(MY_OVERLOADED, void, void, __VA_ARGS__)
#define MY_OVERLOADED0(s, t) MacroTest()
#define MY_OVERLOADED1(s, t, a) MacroTest( a)
#define MY_OVERLOADED2(s, t, a, b) MacroTest(a, b)
#define MY_OVERLOADED3(s, t, a, b, c) MacroTest(a, b, c)

使用方法

MY_OVERLOADED();
MY_OVERLOADED(1);
MY_OVERLOADED(11, 22);
MY_OVERLOADED(111, 222, 333);
1
Th. Thielemann