web-dev-qa-db-ja.com

__VA_OPT__サポートを移植可能に検出しますか?

C++ 20では、プリプロセッサは、引数の数がゼロより大きい場合にオプションで可変マクロのトークンを展開する方法として __VA_OPT__ をサポートします。 (これにより、移植性のないいハックである##__VA_ARGS__ GCC拡張機能が不要になります。)

Clang SVNはこの機能を実装していますが、機能テストマクロは追加していません。賢いプリプロセッサハッカーは、ハードエラーや移植性の警告を発生させることなく、__VA_OPT__サポートの有無を検出する方法を見つけることができますか?

24
Eric Niebler

クリスの答え に触発されました。

_#define PP_THIRD_ARG(a,b,c,...) c
#define VA_OPT_SUPPORTED_I(...) PP_THIRD_ARG(__VA_OPT__(,),true,false,)
#define VA_OPT_SUPPORTED VA_OPT_SUPPORTED_I(?)
_

___VA_OPT___がサポートされている場合、VA_OPT_SUPPORTED_I(?)PP_THIRD_ARG(,,true,false,)に展開されるため、3番目の引数はtrueです。それ以外の場合、VA_OPT_SUPPORTED_I(?)PP_THIRD_ARG(__VA_OPT__(,),true,false,)に展開され、3番目の引数はfalseです。

30
cpplearner

次のようなものが動作するはずですが、改善できるかもしれません。

_#include <boost/preprocessor.hpp>

#define VA_OPT_SUPPORTED_II_1(_) 0
#define VA_OPT_SUPPORTED_II_2(_1, _2) 1

#define VA_OPT_SUPPORTED_I(...) BOOST_PP_OVERLOAD(VA_OPT_SUPPORTED_II_, __VA_OPT__(,))(__VA_OPT__(,))

#define VA_OPT_SUPPORTED VA_OPT_SUPPORTED_I(?)
_

Clangトランクでは、これはC++ 2aモードでは1、C++ 17モードでは0と評価されます。 GCCトランクは実際にはC++ 17でこれを1と評価しますが、そのモードで___VA_OPT___も処理します。

これは、_BOOST_PP_OVERLOAD_を使用して、引数の数に基づいて__1_または__2_バージョンの__II_バージョンを呼び出します。 __VA_OPT__(,)が_,_に展開される場合、2つの空の引数があります。そうでない場合、1つの空の引数があります。このマクロは常に引数リストで呼び出すため、___VA_OPT___をサポートするコンパイラーは常に_,_に展開する必要があります。

当然、Boost.PP依存関係は必須ではありません。単純な1-or-2-arg OVERLOADマクロは、簡単に置き換えられるはずです。より簡単にするために少し一般性を失います:

_#define OVERLOAD2_I(_1, _2, NAME, ...) NAME
#define OVERLOAD2(NAME1, NAME2, ...) OVERLOAD2_I(__VA_ARGS__, NAME2, NAME1)

#define VA_OPT_SUPPORTED_I(...) OVERLOAD2(VA_OPT_SUPPORTED_II_1, VA_OPT_SUPPORTED_II_2, __VA_OPT__(,))(__VA_OPT__(,))
_

Clangから移植性に関する警告が1つあります。

警告:可変長マクロはC++ 98と互換性がありません[-Wc ++ 98-compat-pedantic]

この検出がC++ 11の可変長マクロのサポートなしでも可能かどうかはわかりません。 C++ 11よりも低い___cplusplus_値のサポートはないと想定することもできますが、そのようなチェックでラップされた場合でもClangは警告を表示します。

6
chris

他の回答で述べたように、独自のOVERLOADマクロを書くことができます。 BOOST_PP_OVERLOADは2つの部分で構成され、BOOST_PP_CATおよびBOOST_PP_VARIADIC_SIZE。ただし、Boostとは異なり、2つの引数のみを考慮します。そう:

#define OVERLOAD(prefix, ...) CAT(prefix, VARIADIC(__VA_ARGS__))

CATは次のようになります。

#define CAT(a, b) KITTY((a, b))
#define KITTY(par) MEOW ## par
#define MEOW(a, b) a ## b

VARIADIC

#define VARIADIC(...) _VARIADIC_(__VA_ARGS__, 2, 1,)
#define _VARIADIC_(e0, e1, size, ...) size
2
OwO