18

1 つのマクロを、1 つの特定の引数値と他のすべての引数に対して異なる方法で展開することは可能ですか?

現在のユーザーを定義するとします。

#define CURRENT_USER john_smith

私ができるようにしたいのは、ユーザーが一致を渡した場合に異なる方法で展開CURRENT_USERされるマクロを持つことです。すべての可能なユーザーをアプリオリに知っているわけではないことに注意してください。最も基本的なケース:

#define IS_CURRENT_USER(user)                   \
    /* this is not valid preprocessor macro */  \
    #if user == CURRENT_USER                    \
        1                                       \
    #else                                       \
        0                                       \
    #endif                                      

そのようなマクロを使用すると、ユーザー名に依存する他のすべてのマクロは次の方法で実行できます。

#define SOME_USER_SPECIFIC_MACRO(user) SOME_USER_SPECIFIC_MACRO_SWITCH_1(IS_CURRENT_USER(user))

#define SOME_USER_SPECIFIC_MACRO_SWITCH_1(switch)   SOME_USER_SPECIFIC_MACRO_SWITCH_2(switch) // expand switch ...
#define SOME_USER_SPECIFIC_MACRO_SWITCH_2(switch)   SOME_USER_SPECIFIC_MACRO_##switch         // ... and select specific case

#define SOME_USER_SPECIFIC_MACRO_0  ... // not current user
#define SOME_USER_SPECIFIC_MACRO_1  ... // current user

これは可能ですか?

編集:明確にさせてください。各プログラマーCURRENT_USERが構成ヘッダーで異なる定義をしているとします。user引数が一致する場合にのみ、ユーザー固有のマクロを意味のあるものに拡張したいと考えていますCURRENT_USER。これらのマクロに_pragmas を含めたいので、ランタイム チェックにすることはできません (以下のいくつかの anwser で提案されているように)。

編集: 繰り返しますが、明確化。コードの一部のセクションの最適化を無効にするマクロがあるとします。

#define TURN_OPTIMISATION_OFF __pragma optimize("", off)

一部のプログラマーは、一度にすべてではなく、コードのさまざまなセクションの最適化をオフにしたいと考えています。私が欲しいのはマクロを持っていることです:

#define TURN_OPTIMISATION_OFF(user) /* magic */

これは、プログラマごとの構成ファイルから取得したマクロuserに対する引数と一致します。CURRENT_USERユーザーが一致する場合、マクロはプラグマに展開されます。そうでない場合は、何もありません。

4

7 に答える 7

26

まず、を使用してプリプロセッサとのパターンマッチングを行うことができます##IIFマクロの定義方法は次のとおりです。

#define IIF(cond) IIF_ ## cond
#define IIF_0(t, f) f
#define IIF_1(t, f) t

ただし、このアプローチには1つの問題があります。オペレーターの微妙な副作用##は、それが拡張を阻害することです。ここに例があります:

#define A() 1
//This correctly expands to true
IIF(1)(true, false) 
// This will however expand to IIF_A()(true, false)
// This is because A() doesn't expand to 1,
// because its inhibited by the ## operator
IIF(A())(true, false) 

これを回避する方法は、別の間接参照を使用することです。CATこれは一般的に行われるため、抑制せずに連結するというマクロを記述できます。

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

これで、IIFマクロを記述できます。

#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t

#define A() 1
//This correctly expands to true
IIF(1)(true, false) 
// And this will also now correctly expand to true
IIF(A())(true, false)

パターンマッチングを使用すると、次のような他の操作を定義できますCOMPL

// A complement operator
#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0
// An and operator
#define BITAND(x) PRIMITIVE_CAT(BITAND_, x)
#define BITAND_0(y) 0
#define BITAND_1(y) y

次に、検出手法を使用して、パラメーターが特定の値であるか、または括弧であるかを検出できます。これは、さまざまな数のパラメーターに展開される可変個引数に依存しています。検出の中核となるのは、次のようなCHECKマクロを持つマクロです。PROBE

#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define PROBE(x) x, 1,

これは非常に簡単です。プローブが次のようにCHECKマクロに与えられた場合:

CHECK(PROBE(~)) // Expands to 1

しかし、それに単一のトークンを与えると:

CHECK(xxx) // Expands to 0

これで、いくつかの検出マクロを作成できます。たとえば、括弧を検出する場合は、次のようにします。

#define IS_PAREN(x) CHECK(IS_PAREN_PROBE x)
#define IS_PAREN_PROBE(...) PROBE(~)
IS_PAREN(()) // Expands to 1
IS_PAREN(xxx) // Expands to 0

次に、2つのトークンを比較する必要があります。これは、マクロが再帰的に展開されないという事実に依存できます。マクロを他のマクロ内で再帰的に展開するように強制します。2つのトークンが同じである場合、マクロは再帰的に展開されます。これは、それらが括弧に展開されているかどうかを検出しようとすることで検出されます。COMPAREマクロは次のとおりです。

#define COMPARE(a, b) PRIMITIVE_COMPARE(a, b)
#define PRIMITIVE_COMPARE(a, b) \
    IIF( \
        BITAND \
            (IS_PAREN(COMPARE_ ## a(()))) \
            (IS_PAREN(COMPARE_ ## b(()))) \
    )( \
        COMPL(IS_PAREN( \
            COMPARE_ ## a( \
                COMPARE_ ## b \
            )(()) \
        )), \
        0 \
    ) \

比較する各トークンは、次のように定義します。

// So you would define one for each user
#define COMPARE_john_smith(x) x
#define COMPARE_another_user_name(x) x

生成したい最終出力が完全には理解できていないので、現在のユーザー用と他のユーザー用のコードを生成するためのマクロがあるとします。

#define MACRO_CURRENT_USER(user) ...
#define MACRO_OTHER_USER(user) ...

次に、次のように書くことができます。

// Detects if its the current user
#define IS_CURRENT_USER(user) COMPARE(user, CURRENT_USER)
// Your macro
#define MACRO(user) IIF(IS_CURRENT_USER(user))(MACRO_CURRENT_USER, MACRO_OTHER_USER)(user)
于 2012-07-24T23:30:11.860 に答える
11

それは可能です。この anwser は Pauls マクロに基づいていますが、はるかに単純であり、各ユーザーの定義は必要ありません。

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t

#define PROBE(x) x, 1 

さて、MSVC のバグCHECKのため、マクロを少し変更する必要がありました。

#define MSVC_VA_ARGS_WORKAROUND(define, args) define args
#define CHECK(...) MSVC_VA_ARGS_WORKAROUND(CHECK_N, (__VA_ARGS__, 0))
#define CHECK_N(x, n, ...) n

定義する代わりにCURRENT_USER、次のマクロに切り替えました。

#define ENABLE_USER_gwiazdorrr () // gwiazdorrr is now enabled
#define ENABLE_USER_foo ()        // foo is also enabled
// #define ENABLE_USER_bar ()     // bar is NOT enabled

同時に複数のユーザーを有効にできるため、実際にはより柔軟になります。括弧は必須です。以下のマクロは、ENABLE_USER_<user>が括弧内に展開されているかどうかを実際に検出します。

#define USER_ENABLED_PROBE(user)            USER_ENABLED_PROBE_PROXY( ENABLE_USER_##user ) // concatenate prefix with user name
#define USER_ENABLED_PROBE_PROXY(...)       USER_ENABLED_PROBE_PRIMIVIE(__VA_ARGS__)       // expand arguments
#define USER_ENABLED_PROBE_PRIMIVIE(x)      USER_ENABLED_PROBE_COMBINE_##x                 // merge
#define USER_ENABLED_PROBE_COMBINE_(...)    PROBE(~)                                       // if merge successful, expand to probe

USER_ENABLED_PROBE(gwiazdorrr) // expands to ~, 1
USER_ENABLED_PROBE(bar)        // expands to USER_ENABLED_PROBE_COMBINE_bar

これからは子供の遊びです:

#define IS_USER_ENABLED(user) CHECK(USER_ENABLED_PROBE(user))

IS_USER_ENABLED(gwiazdorrr)   // expands to 1
IS_USER_ENABLED(bar)          // expands to 0

このマクロを使用してIIF(Paul に感謝します!) 元の質問に記載されている最適化マクロを実装することにしました。

#define TURN_OPTIMISATION_OFF(user) IIF( IS_USER_ENABLED(user) ) \
    (\
        __pragma optimize("", off),\
        /* nothing */ \
    )

TURN_OPTIMISATION_OFF(gwiazdorrr) // expands into __pragma optimize("", off)
TURN_OPTIMISATION_OFF(foo)        // expands into __pragma optimize("", off)
TURN_OPTIMISATION_OFF(bar)        // nothing emitted

ご意見ありがとうございます。

編集: ここに GCC バージョンがあります: http://ideone.com/129eo

于 2012-07-25T14:54:09.443 に答える
2

以下のコードは、MSVC バグの影響を受けません。... 引数は分離されていません。

#define IF_USER_ENABLED(x,...) IF_USER_ARGS_GT2 (ENABLE_USER_ ## x,__VA_ARGS__) 
#define IF_USER_ARGS_GT2(x,...) ARGS_ARG2 (x,GT4,3,,__VA_ARGS__)
#define ARGS_ARG2(x,y,z,...) ARGS_ ## z (x,y,z,__VA_ARGS__)
#define ARGS_3(x,y,z,w,...) w
#define ARGS_GT4(x,y,z,w,v,...) __VA_ARGS__

#define IF_USER_DISABLED(x,...) IF_NOT_USER_ARGS_GT2 (ENABLE_USER_ ## x,__VA_ARGS__) 
#define IF_NOT_USER_ARGS_GT2(x,...) ARGS_ARG2 (x,4,GT3,,__VA_ARGS__)
#define ARGS_4(x,y,z,w,v,...) v
#define ARGS_GT3(x,y,z,w,...) __VA_ARGS__

#define ENABLE_USER_foo ,
//#define ENABLE_USER_bar ,
于 2014-04-26T23:22:17.590 に答える
2

マクロへの引数が常に非常に一定である場合 (文字通りにも語彙的にも)、concatenationを使用してトリックを実行できます。

#define SOME_MACRO(T) SOME_MACRO_FOR_##T
#define SOME_MACRO_FOR_0 somethinghere()
#define SOME_MACRO_FOR_1 somethingelse()

そうでなければ、あなたは持っている可能性があります

#define CURRENT_USER ((user == THE_USER)?(something()):(somethingelse()))

または、static inline 小さな関数を使用します。

static inline int current_user(int user)
{
   return (user==THE_USER)?(something()):(somethingelse());
}

( ifuserが定数であることに注意してください。おそらく以前のコンパイラの最適化の後、コンパイラはそれをより単純なものに最適化し、コンパイルされたバイナリは実行時にテストされません。 でコンパイルする場合は__builtin_constant_puserも参照してください)。gcc

しかし、プリプロセッサのトリックにより、コードが読みにくくなる可能性があると思います。それらを作成するときはよく考えてください。

また、正確なマクロの使用法を教えてくれませんでした。左辺値として使用しますか?

あなたが言うように、プリプロセッサは前処理ディレクティブに展開できないため、例:

#define IS_CURRENT_USER(user)                   \
  /* this is not valid preprocessor macro */  \
  #if user == CURRENT_USER                    \
      1                                       \
  #else                                       \
      0                                       \
  #endif       

(あなたが言うように)間違っています。

次のようなことのみが許可されています。

 #if user == CURRENT_USER
 #define IS_CURRENT_USER(U) 1
 #else
 #define IS_CURRENT_USER(u) 0
 #endif

読みやすくするために、マクロの正式な引数としてunotを意図的に使用しています(正式な引数は展開されず、マクロでの出現のみが展開されます)。userIS_CURRENT_USER

前処理がコンパイルの「前」に行われることを認識していますか? たとえば、前処理された出力を取得するために実行しましたgcc -C -Eか? それは有益であるべきです!

C プリプロセッサの詳細を読む

ところで、スクリプト (または独自のジェネレーター、またはautotools 、またはautogenm4などの汎用プリプロセッサー) を使用してCコード (おそらく#includeどこかに -dされる) を生成することを検討しましたか? たとえば、 Linux、NIS/YP、LDAP、または...などのユーザーベースから-dを次のように生成できます/etc/passwdgetpwent(3)#includemyoptim.h

#if CURRENT_USER_ID==1234
#define OPTIMIZATION_FOR_PAUL _pragma(GCC(optimize,"-O1"))
#else
#define OPTIMIZATION_FOR_PAUL /*nothing*/
#endif
#if CURRENT_USER_ID==3456
#define OPTIMIZATION_FOR_ALICE _pragma(GCC(optimize,"-O1"))
#else
#define OPTIMIZATION_FOR_ALICE /*nothing*/
#endif

そして、Paul に (彼の uid が 1234 であると仮定して) 彼の関数にプレフィックスを付けて、あなたの;OPTIMIZATION_FOR_PAULを入れるように依頼します。私はそれが醜いと思います(そして、最適化が不適切にコード化されたプログラムの動作をグローバルに変更する可能性があるという事実に対処していません)。CFLAGS=-DCURRENT_USER_ID=$(shell id -u)Makefile

たとえば、必要に応じてカスタム プラグマまたはビルトインを提供するMELT拡張機能を使用して GCC をカスタマイズできますが、特定のケースでは奇妙だと思います。

注意。は歴史的に、cppチューリング完全ではなく、高速なテキスト プロセッサとして設計されました。昔(1980年代のUnix)では、それは別のプロセスとして実行され、実際のコンパイルは によって行われcc1ccコンパイラはそれらを駆動する単なるシェルスクリプトでした(asおよびld)。現在、gcc小さなドライバー プログラムですがcc1、パフォーマンス上の理由からプリプロセッサが組み込まれています。それでも、前処理をコンパイラ本体とは別のプログラムにすることができるように、 C標準が指定されています。

于 2012-07-24T13:53:59.947 に答える
2

コンパイルの前に前処理が行われます。

ユーザーがプリプロセッサに認識されている場合は、はい:

#define user 4
#define CURRENT_USER 4
#define IS_CURRENT_USER 1

#if user == CURRENT_USER
#define IS_CURRENT_USER(user) 1
#else
#define IS_CURRENT_USER(user) 0
#endif

しかし、これはまったく役に立たず、あなたが実際に持っているものとは思えません。

そうでなければ、いいえ。mecro とプリプロセッサを悪用しないでください。

編集後:

いいえ、あなたが望むことは絶対に不可能です (ユーザーに応じて最適化をオフにします)。

于 2012-07-24T14:00:01.043 に答える
0

ifなぜ簡単な文 を使わなかったのですか?#ifマクロでは使用できません。

次に例を示します。

// Return 1 if `user' is the current user, 0 else.
#define IS_CURRENT_USER(user) ((user) == CURRENT_USER)

またはUSER、コンパイル時に設定すると、条件分岐の数を減らすことができます。

#if USER == CURRENT_USER
# define IS_CURRENT_USER (1)
#else
# define IS_CURRENT_USER (0)
#endif
于 2012-07-24T13:51:34.003 に答える
0

そのようなことの何が問題になっていますか?

#if CURRENT_USER == john_smith
    #define SOME_USER_SPECIFIC_MACRO_SWITCH  WHATEVER ## CURRENT_USER
    #define SOME_USER_SPECIFIC_MACRO_1  ... 
#else
    #define SOME_USER_SPECIFIC_MACRO_0  ... 
    #define SOME_USER_SPECIFIC_MACRO_SWITCH  WHATEVER ## Somethingelse
#endif// not current user
于 2012-07-24T14:04:20.943 に答える