web-dev-qa-db-ja.com

C ++マクロを使用したオプションのパラメーター

C++マクロでオプションのパラメーターを取得する方法はありますか?ある種のオーバーロードもニースで​​す。

95
Cenoc

これを行う1つの方法を次に示します。引数のリストを2回使用します。最初にヘルパーマクロの名前を作成し、次にそのヘルパーマクロに引数を渡します。マクロへの引数の数を数えるために、標準のトリックを使用します。

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};

void PrintString(const char* message, int size, int style)
{
}

#define PRINT_STRING_1_ARGS(message)              PrintString(message, 0, 0)
#define PRINT_STRING_2_ARGS(message, size)        PrintString(message, size, 0)
#define PRINT_STRING_3_ARGS(message, size, style) PrintString(message, size, style)

#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define PRINT_STRING_MACRO_CHOOSER(...) \
    GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
                PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )

#define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main(int argc, char * const argv[])
{
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

これにより、マクロの呼び出し元は簡単になりますが、ライターは簡単になりません。

137
Derek Ledbetter

Derek Ledbetterの回答に敬意を表し、古い質問を復活させることをおologiesびします。

それが何をしていたのか理解し、他の場所で__VA_ARGS__ with ##バリエーションを考え出すことができました...

// The multiple macros that you would need anyway [as per: Crazy Eddie]
#define XXX_0()                     <code for no arguments> 
#define XXX_1(A)                    <code for one argument> 
#define XXX_2(A,B)                  <code for two arguments> 
#define XXX_3(A,B,C)                <code for three arguments> 
#define XXX_4(A,B,C,D)              <code for four arguments>  

// The interim macro that simply strips the excess and ends up with the required macro
#define XXX_X(x,A,B,C,D,FUNC, ...)  FUNC  

// The macro that the programmer uses 
#define XXX(...)                    XXX_X(,##__VA_ARGS__,\
                                          XXX_4(__VA_ARGS__),\
                                          XXX_3(__VA_ARGS__),\
                                          XXX_2(__VA_ARGS__),\
                                          XXX_1(__VA_ARGS__),\
                                          XXX_0(__VA_ARGS__)\
                                         ) 

私のように答えを見つけたが、それがどのように機能するのかよくわからない非専門家の場合は、次のコードから始めて実際の処理を実行します...

XXX();
XXX(1); 
XXX(1,2); 
XXX(1,2,3); 
XXX(1,2,3,4); 
XXX(1,2,3,4,5);      // Not actually valid, but included to show the process 

になる...

XXX_X(, XXX_4(), XXX_3(),  XXX_2(),    XXX_1(),      XXX_0()         );
XXX_X(, 1,       XXX_4(1), XXX_3(1),   XXX_2(1),     XXX_1(1),       XXX_0(1)          );
XXX_X(, 1,       2,        XXX_4(1,2), XXX_3(1,2),   XXX_2(1,2),     XXX_1(1,2),       XXX_0(1,2)        );
XXX_X(, 1,       2,        3,          XXX_4(1,2,3), XXX_3(1,2,3),   XXX_2(1,2,3),     XXX_1(1,2,3),     XXX_0(1,2,3)      );
XXX_X(, 1,       2,        3,          4,            XXX_4(1,2,3,4), XXX_3(1,2,3,4),   XXX_2(1,2,3,4),   XXX_1(1,2,3,4),   XXX_0(1,2,3,4)    );
XXX_X(, 1,       2,        3,          4,            5,              XXX_4(1,2,3,4,5), XXX_3(1,2,3,4,5), XXX_2(1,2,3,4,5), XXX_1(1,2,3,4,5), XXX_0(1,2,3,4,5) );

これは6番目の引数になります...

XXX_0(); 
XXX_1(1); 
XXX_2(1,2); 
XXX_3(1,2,3); 
XXX_4(1,2,3,4); 
5; 

PS:XXX_0の#defineを削除して、コンパイルエラーを取得します[引数なしのオプションが許可されていない場合]。

PPS:無効な状況(例:5)がプログラマにとってより明確なコンパイルエラーを与えるものであることは素晴らしいことです!

PPPS:私は専門家ではないので、コメント(良い、悪い、その他)を聞いてとてもうれしいです!

74
David Sorkovsky

C++マクロはCから変更されていません。Cには関数のオーバーロード引数とデフォルト引数がなかったため、マクロ用にはありませんでした。あなたの質問に答えるために:いいえ、それらの機能はマクロには存在しません。唯一のオプションは、異なる名前の複数のマクロを定義することです(または、マクロをまったく使用しません)。

サイドノートとして:C++では、可能な限りマクロから遠ざけることをお勧めします。このような機能が必要な場合は、マクロを使いすぎる可能性があります。

31
sepp2k

Derek LedbetterDavid SorkovskySyphorlateを最大限に尊重して回答、およびJens Gustedtによって空のマクロ引数を検出するための独創的な方法とともに

https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

最後に、すべてのトリックを組み込んだものを思い付きます。

  1. 標準のC99マクロのみを使用して、関数のオーバーロードを実現し、GCC/CLANG/MSVC拡張機能を使用しません(つまり、特定の式_, ##__VA_ARGS___でコンマを飲み込みます) GCC/CLANGの場合、およびMSVCの場合は_##__VA_ARGS___による暗黙の嚥下)。 =)を希望する場合は、欠落している_--std=c99_をコンパイラに自由に渡してください
  2. ゼロ引数、および無制限の数の引数に対応ニーズに合わせてさらに拡張します
  3. 合理的に動作しますcross-platform、少なくともテスト済み

    • GNU/Linux + GCC(CentOS 7.0 x86_64上のGCC 4.9.2)
    • GNU/Linux + CLANG/LLVM、(CentOS 7.0 x86_64上のCLANG/LLVM 3.5.0)
    • OS X + Xcode、(OS X Yosemite 10.10.1のXCode 6.1.1)
    • Windows + Visual Studio、(Windows 7 SP1 64ビットのVisual Studio 2013 Update 4)

怠け者の場合は、この投稿の最後までスキップしてソースをコピーしてください。以下に詳細な説明を示します。これは、私のような一般的な___VA_ARGS___ソリューションを探しているすべての人々を助け、刺激することを願っています。 =)

方法は次のとおりです。最初に、ユーザーに見えるオーバーロードされた「関数」を定義し、create、関連する実際の関数定義realCreate、および異なる引数数を持つマクロ定義_CREATE_2_、_CREATE_1_、_CREATE_0_、以下に示すように:

_#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)
_

MACRO_CHOOSER(__VA_ARGS__)部分は最終的にマクロ定義名に解決され、2番目の_(__VA_ARGS__)_部分はパラメーターリストで構成されます。したがって、ユーザーのcreate(10)への呼び出しはCREATE_1(10)に解決され、_CREATE_1_部分はMACRO_CHOOSER(__VA_ARGS__)から、_(10)_部分は2番目の_(__VA_ARGS__)_。

_MACRO_CHOOSER_は、___VA_ARGS___が空の場合、次の式がプリプロセッサによって有効なマクロ呼び出しに連結されるというトリックを使用します。

_NO_ARG_EXPANDER __VA_ARGS__ ()  // simply shrinks to NO_ARG_EXPANDER()
_

独創的に、この結果のマクロ呼び出しを次のように定義できます。

_#define NO_ARG_EXPANDER() ,,CREATE_0
_

2つのコンマに注意してください、それらはすぐに説明されます。次の便利なマクロは

_#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
_

だからの呼び出し

_create();
create(10);
create(20, 20);
_

実際に展開されます

_CHOOSE_FROM_ARG_COUNT(,,CREATE_0)();
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 10 ())(10);
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 20, 20 ())(20, 20);
_

マクロ名が示すように、後で引数の数を数えます。別のトリックがあります。プリプロセッサは単純なテキスト置換のみを行います。マクロ呼び出しの引数の数は、括弧内にあるカンマの数から推測します。コンマで区切られた実際の「引数」は、有効な構文である必要はありません。任意のテキストを使用できます。つまり、上記の例では、NO_ARG_EXPANDER 10 ()は中間呼び出しの1つの引数としてカウントされます。 _NO_ARG_EXPANDER 20_および20 ()は、それぞれボトムコールの2つの引数としてカウントされます。

次のヘルパーマクロを使用してさらに展開する場合

_##define CHOOSE_FROM_ARG_COUNT(...) \
  FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
  FUNC_CHOOSER argsWithParentheses
_

_,_の後の末尾の_CREATE_1_は、GCC/CLANGの回避策であり、コンパイラーに_ISO C99 requires rest arguments to be used_を渡すときに_-pedantic_という(誤検知)エラーを抑制します。 _FUNC_RECOMPOSER_はMSVCの回避策です。または、マクロ呼び出しの括弧内の引数(つまり、コンマ)の数を正しくカウントできません。結果はさらに解決されます

_FUNC_CHOOSER (,,CREATE_0, CREATE_2, CREATE_1, )();
FUNC_CHOOSER (NO_ARG_EXPANDER 10 (), CREATE_2, CREATE_1, )(10);
FUNC_CHOOSER (NO_ARG_EXPANDER 20, 20 (), CREATE_2, CREATE_1, )(20, 20);
_

お気づきかもしれませんが、最後に必要な手順は、標準の引数カウントトリックを使用して、最終的に必要なマクロバージョン名を選択することです。

_#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
_

結果を解決します

_CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);
_

そして、確かに望ましい実際の関数呼び出しを提供します:

_realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);
_

読みやすくするためにステートメントを再配置して、すべてをまとめると、2引数の例のソース全体がここにあります

_#include <stdio.h>

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define NO_ARG_EXPANDER() ,,CREATE_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main()
{
  create();
  create(10);
  create(20, 20);
  //create(30, 30, 30);  // Compilation error
  return 0;
}
_

複雑で見苦しく、API開発者に負担がかかりますが、C/C++関数のオプションパラメーターをオーバーロードして設定するためのソリューションがあります。今後のオーバーロードAPIの使用は、非常に楽しく快適になります。 =)

このアプローチをさらに単純化できる場合は、次のURLでお知らせください。

https://github.com/jason-deng/C99FunctionOverload

再びこのインスピレーションを得て、この作品を達成するよう導いてくれた素晴らしい人たちに感謝します! =)

22
Jason Deng

Visual C++で動作するVA_NARGSソリューションを苦労して検索する人向け。 Visual C++ Express 2010では、次のマクロが完璧に機能しました(パラメータもゼロです!):

#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N,...) N
#define VA_NUM_ARGS_IMPL_(Tuple) VA_NUM_ARGS_IMPL Tuple
#define VA_NARGS(...)  bool(#__VA_ARGS__) ? (VA_NUM_ARGS_IMPL_((__VA_ARGS__, 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

オプションのパラメーターを持つマクロが必要な場合は、次のことができます。

//macro selection(vc++)
#define SELMACRO_IMPL(_1,_2,_3, N,...) N
#define SELMACRO_IMPL_(Tuple) SELMACRO_IMPL Tuple
#define mymacro1(var1) var1
#define mymacro2(var1,var2) var2*var1
#define mymacro3(var1,var2,var3) var1*var2*var3
#define mymacro(...) SELMACRO_IMPL_((__VA_ARGS__, mymacro3(__VA_ARGS__), mymacro2(__VA_ARGS__), mymacro1(__VA_ARGS__))) 

それはVCでもうまくいきました。ただし、パラメータがゼロの場合は機能しません。

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8
9
Syphorlate

gcc/g++varargsマクロ をサポートしますが、これは標準ではないと思うので、自己責任で使用してください。

6
Paul R
#include <stdio.h>

#define PP_NARG(...) \
    PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
    PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
    _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ 
    _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
    _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
    _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
    _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
    _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
    _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
    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 PP_CONCAT(a,b) PP_CONCAT_(a,b)
#define PP_CONCAT_(a,b) a ## b

#define THINK(...) PP_CONCAT(THINK_, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define THINK_0() THINK_1("sector zz9 plural z alpha")
#define THINK_1(location) THINK_2(location, 42)
#define THINK_2(location,answer) THINK_3(location, answer, "deep thought")
#define THINK_3(location,answer,computer) \
  printf ("The answer is %d. This was calculated by %s, and a computer to figure out what this"
          " actually means will be build in %s\n", (answer), (computer), (location))

int
main (int argc, char *argv[])
{
  THINK (); /* On compilers other than GCC you have to call with least one non-default argument */
}

免責事項:ほとんど無害。

5
Joe D

これは、実際にはプリプロセッサの設計対象ではありません。

とはいえ、読みやすさを重視して真剣に挑戦するマクロプログラミングの分野に参入したいのであれば、 ブーストプリプロセッサライブラリ をご覧ください。結局のところ、3つの完全にチューリング互換のプログラミングレベル(プリプロセッサ、テンプレートメタプログラミング、およびベースレベルC++)がなければ、C++ではありません。

3
Pontus Gagge
#define MY_MACRO_3(X,Y,Z) ...
#define MY_MACRO_2(X,Y) MY_MACRO(X,Y,5)
#define MY_MACRO_1(X) MY_MACRO(X,42,5)

呼び出しの時点で、渡す引数の数がわかっているので、オーバーロードする必要はありません。

3
Edward Strange

Derek Ledbetterのコードのより簡潔なバージョン:

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};


void PrintString(const char* message = NULL, int size = 0, int style = 0)
{
}


#define PRINT_STRING(...) PrintString(__VA_ARGS__)


int main(int argc, char * const argv[])
{ 
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}
2
Megamozg

boostライブラリのBOOST_PP_OVERLOADを使用できます。

official boost doc の例:

#include <boost/preprocessor/facilities/overload.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>

#define MACRO_1(number) MACRO_2(number,10)
#define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2)

#if !BOOST_PP_VARIADICS_MSVC

#define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__)

#else

// or for Visual C++

#define MACRO_ADD_NUMBERS(...) \
  BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())

#endif

MACRO_ADD_NUMBERS(5) // output is 15
MACRO_ADD_NUMBERS(3,6) // output is 9
1
disable13

恐ろしいマクロモンスターの大ファンとして、Jason Dengの答えを拡張して、実際に使用できるようにしたかったのです。 (良くも悪くも。)オリジナルは、新しいマクロを作成するたびに大きなアルファベットのスープを変更する必要があるため、使用するのにあまり良いものではありません。

そこで、これらの機能を備えたバージョンを作成しました。

  • 0引数の場合は動作します
  • 乱雑な部分に変更を加えない1〜16の引数
  • より多くのマクロ関数を簡単に書く
  • Gcc 10、clang 9、Visual Studio 2017でテスト済み

現在、私は最大16個の引数を作成しましたが、さらに必要な場合(本当に?愚かなことになっています...)、FUNC_CHOOSERとCHOOSE_FROM_ARG_COUNTを編集し、NO_ARG_EXPANDERにコンマを追加できます。

実装の詳細については、Jason Dengの優れた回答を参照してください。ただし、ここにコードを配置します。

#include <stdio.h>

void realCreate(int x, int y)
{
    printf("(%d, %d)\n", x, y);
}

// This part you put in some library header:
#define FUNC_CHOOSER(_f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15, _f16, ...) _f16
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(F, ...) FUNC_RECOMPOSER((__VA_ARGS__, \
            F##_16, F##_15, F##_14, F##_13, F##_12, F##_11, F##_10, F##_9, F##_8,\
            F##_7, F##_6, F##_5, F##_4, F##_3, F##_2, F##_1, ))
#define NO_ARG_EXPANDER(FUNC) ,,,,,,,,,,,,,,,,FUNC ## _0
#define MACRO_CHOOSER(FUNC, ...) CHOOSE_FROM_ARG_COUNT(FUNC, NO_ARG_EXPANDER __VA_ARGS__ (FUNC))
#define MULTI_MACRO(FUNC, ...) MACRO_CHOOSER(FUNC, __VA_ARGS__)(__VA_ARGS__)

// When you need to make a macro with default arguments, use this:
#define create(...) MULTI_MACRO(CREATE, __VA_ARGS__)
#define CREATE_0() CREATE_1(0)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_2(x, y) \
    do { \
        /* put whatever code you want in the last macro */ \
        realCreate(x, y); \
    } while(0)


int main()
{
    create();
    create(10);
    create(20, 20);
    //create(30, 30, 30);  // Compilation error
    return 0;
}
0
Kuukunen

必要に応じて、マクロで var args を使用して実行できます。現在、オプションのパラメーターまたはマクロのオーバーロードは、そのようなことはありません。

0
Gianni