4

私は C++11 バリアダック マクロを試していました。

リスト内の各引数に別のマクロを適用しようとしていました。
これは私の最初の試みです:

#define  APPLY_CHAIN(first, ...)    APPLY_ACT(first) APPLY_CHAIN( __VA_ARGS__ )

残念ながら、これはうまくいきませんでした。

私は最終的にそれを機能させました。しかし、それは少し複雑で、'n' という制限があります ('n' は、私がマクロを入力する最大サイズです)。

#define COUNT_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...)    N
#define COUNT(...)   COUNT_N( __VA_ARGS__, 10, 9, 8, 7, 6, 5 ,4, 3, 2, 1)

#define BUILD_CHAIN_1(_1)       APPLY_ACT(_1)
#define BUILD_CHAIN_2(_1, ...)  APPLY_ACT(_1) BUILD_CHAIN_1(__VA_ARGS__)
#define BUILD_CHAIN_3(_1, ...)  APPLY_ACT(_1) BUILD_CHAIN_2(__VA_ARGS__)
#define BUILD_CHAIN_4(_1, ...)  APPLY_ACT(_1) BUILD_CHAIN_3(__VA_ARGS__)
#define BUILD_CHAIN_5(_1, ...)  APPLY_ACT(_1) BUILD_CHAIN_4(__VA_ARGS__)
#define BUILD_CHAIN_6(_1, ...)  APPLY_ACT(_1) BUILD_CHAIN_5(__VA_ARGS__)
#define BUILD_CHAIN_7(_1, ...)  APPLY_ACT(_1) BUILD_CHAIN_6(__VA_ARGS__)
#define BUILD_CHAIN_8(_1, ...)  APPLY_ACT(_1) BUILD_CHAIN_7(__VA_ARGS__)
#define BUILD_CHAIN_9(_1, ...)  APPLY_ACT(_1) BUILD_CHAIN_8(__VA_ARGS__)
#define BUILD_CHAIN_10(_1,...)  APPLY_ACT(_1) BUILD_CHAIN_9(__VA_ARGS__)


#define BUILD_CHAIN_INC( CT, ...)   BUILD_CHAIN_ ## CT (__VA_ARGS__)
#define BUILD_CHAIN( CT, ...)       BUILD_CHAIN_INC(CT, __VA_ARGS__)
#define APPLY_CHAIN(...)            BUILD_CHAIN( COUNT(__VA_ARGS__), __VA_ARGS__)

#define APPLY_ACT(X)                #X

#include <iostream>

int main()
{
    std::cout << APPLY_CHAIN(1,2,3,4,5) << "\n";
}

iterateしたがって、私の質問は次のとおりです。引数チェーンに沿って私を可能にする単純なものが欠けていますか?

4

3 に答える 3

1

引数カウントの問題に対するあなたの解決策は、2006 年 1 月に Laurent Deniau によって [comp.lang.c] (IIRC) に投稿されたものと同じです。

Visual C++ でも動作するように変更すると、引数のカウントは次のようになります。

#pragma once

/*
* The PP_NARG macro evaluates to the number of arguments that have been
* passed to it.
*
* Laurent Deniau, "__VA_NARG__," 17 January 2006, <comp.std.c> (29 November 2007).
* https://groups.google.com/forum/?fromgroups=#!topic/comp.std.c/d-6Mj5Lko_s
*/

// Added workaround for silly MSVC bug that yields "too few parameters" warning.
// - Alf
#include <progrock/cppx/macro/invoke.h>         // CPPX_INVOKE
#include <progrock/cppx/macro/concat.h>         // CPPX_CONCAT

#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

#if 0
    #define PP_NARG_(...) PP_ARG_N( __VA_ARGS__ )
    #define PP_NARG( ...) PP_NARG_( __VA_ARGS__, PP_RSEQ_N() )
#else
    #define PP_NARG_(...) CPPX_INVOKE( PP_ARG_N, (__VA_ARGS__) )
    #define PP_NARG(...) PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#endif

CPPX_CONCATマクロ:

#pragma once

#define CPPX_CONCAT__( a, b )       a ## b
#define CPPX_CONCAT_( a, b )        CPPX_CONCAT__( a, b )
#define CPPX_CONCAT( a, b )         CPPX_CONCAT_( a, b )

CPPX_INVOKEマクロ:

#pragma once

#define CPPX_INVOKE( macro, args ) macro args

使用例:

#pragma once
#include <progrock/cppx/macro/nargs.h>          // CPPX_NARGS, CPPX_CONCAT

#define CPPX_IS_UNUSED_( name ) \
    (void) name; struct name

#define CPPX_IS_UNUSED_1( a ) \
    CPPX_IS_UNUSED_( a )

#define CPPX_IS_UNUSED_2( a, b ) \
    CPPX_IS_UNUSED_1( a ); CPPX_IS_UNUSED_( b )

#define CPPX_IS_UNUSED_3( a, b, c ) \
    CPPX_IS_UNUSED_2( a, b ); CPPX_IS_UNUSED_( c )

#define CPPX_IS_UNUSED_4( a, b, c, d ) \
    CPPX_IS_UNUSED_3( a, b, c ); CPPX_IS_UNUSED_( d )

#define CPPX_IS_UNUSED_5( a, b, c, d, e ) \
    CPPX_IS_UNUSED_4( a, b, c, d ); CPPX_IS_UNUSED_( e )

#define CPPX_IS_UNUSED( ... )      \
    CPPX_INVOKE( CPPX_CONCAT( CPPX_IS_UNUSED_, CPPX_NARGS( __VA_ARGS__ ) ), ( __VA_ARGS__ ) )

#define CPPX_IS_INTENTIONALLY_UNUSED    CPPX_IS_UNUSED

これが例証するように、主な手がかりは再帰を (少なくとも直接ではなく) 使用せず、代わりに引数の数ごとに手動でコードを繰り返すことです。

再帰的なマクロは正式には不可能だと思います。ただし、この分野に準拠しているコンパイラはほとんどなく、少なくとも数年前には、Windows コンパイラに再帰的なマクロ置換を実行させることができました。私が思い出したように、これは最終的に Boost Preprocessor Library になったコードで最初に使用された手法でした (現在では、コンパイラ固有の癖の回避策を除いて、引数の数に固有のハンドラーの大きなリストを一元化することにより、正式に有効なコードを使用しています) .

そのコードの繰り返しは、ほとんどの場合、Boost Preprocessor ライブラリを介して自動化できますが、その後、Boost を他のものにも使用します (おそらく、ポイントは、Boost のような大きなライブラリへの依存を避けることです。少なくとも、それが私が書いたときの私の動機でした)上記)。

免責事項: 投稿する前にこのコードを再試行しなかったため、変更された状態だったのか、それとも何だったのか思い出せません。しかし、それは一般原則を示しています。

于 2013-04-06T09:08:46.363 に答える
0

Boost.Preprocessorを見てください。特にBOOST_PP_SEQ_FOLD_LEFTBOOST_PP_VARIADIC_TO_SEQが必要です。

何かのようなもの

#include <boost/preprocessor.hpp>
#include <iostream>

#define OP(d, state, x) state BOOST_PP_STRINGIZE(x)
#define APPLY_CHAIN(...) BOOST_PP_SEQ_FOLD_LEFT( OP, BOOST_PP_EMPTY() , BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) ) 

int main() {
  std::cout <<  APPLY_CHAIN(1,2,3,4,5) << std::endl;
}
//expands to   std::cout << "1" "2" "3" "4" "5" << std::endl;
于 2013-04-06T09:40:10.817 に答える
0

プリプロセッサはソースを 1 回しか処理しないため、再帰的なマクロはまだ使用できないようです

条件付きガードを使用してダミーを使用して明示的なループ構成を作成できるに違い#includeありません (これはおそらく、boost.preprocessorが最終的に行うことです)。

于 2013-04-06T07:02:49.280 に答える