チケット #50 (new blog)

登録: 12 か月

最終更新: 12 か月

C++マクロアラカルト

報告者: mtamaki 担当者: mtamaki
優先度: major マイルストーン:
コンポーネント: blog バージョン:
キーワード: 関係者:

説明 (最終更新者: mtamaki) (diff)

C++のマクロについていろいろ。

マクロ展開のルール

VCの#defineの仕様:

The #define Directive (C/C++)

ルール:

  • 関数マクロの展開はまず関数マクロ自体が展開される。引き数はいったんそのまま仮引数と置き換えられる。
  • 次に、仮引数と置き換えられた値がマクロ展開されるが、#,#@,##にかかわる引数は展開されない。そのほかの引数は展開できなくなるまで展開される。
    #define a                       b
    #define cat(x,y)                x##y
    #define wrapped_cat(x,y)        cat(x,y)
    
    cat(a,b)
            a##b            //まず、catが展開され、引数が仮引数にそのまま置き換えられる。##にかかわるので、aはマクロ展開されない。
            ab              //そのまま##が処理されてabになる。
    
    wrapped_cat(a,b)
            cat(a,b)        //まず、wrapped_catが展開され、引数が仮引数にそのまま置き換えられる。
            cat(b,b)        //置き換えられた引数がマクロ展開される。
            b##b            //catが展開される。
            bb              //##が処理されてbbになる。
    

より複雑で丁寧なケースと解説は以下を参照。 某日記(後期)

マクロオペレータ

#define a       b##c    //a→bc
#define d       #e      //d→"e"
#define f       #@g     //f→'g'

可変長引数関数マクロ1

関数マクロでも可変長引数を取ることができる。

#define foo(a, ...)     var(a, __VA_ARGS__)

可変長引数関数マクロ2

__VA_ARGS__では可変長部分の引数をまとめて扱うことしかできないが、個別に任意の数の引数を受け取りたいときの表現方法はいくつかある。 Boost.PreProcessorによる分類がわかりやすい。

(3, (A, B, C))          //Array
(A, (B, (C, NIL)))      //List
(A)(B)(C)               //Sequence
(A, B, C)               //Tuple

この中でArrayは簡潔なコードで処理を表現しやすい。

#define DEF_CLASS( name, deriveds )     class name DEF_CLASS_DERIVEDS deriveds{};
#define DEF_CLASS_DERIVEDS( count, deriveds ) DEF_CLASS_DERIVEDS##count deriveds
#define DEF_CLASS_DERIVEDS0()
#define DEF_CLASS_DERIVEDS1( class1 ) : public class1
#define DEF_CLASS_DERIVEDS2( class1, class2 ) : public class1, public class2
        //:
DEF_CLASS( A, (0, ()) )         //class A{};
DEF_CLASS( B, (1, (A)) )        //class B : public A{};
DEF_CLASS( C, (2, (A, B)) )     //class C : public A, public B{};

マクロの中にマクロ展開させたくないマクロ名を含める

#define A       B
#define C       A

とあったときに、通常CはBに展開されるが、展開せずにAと出力したいとする。

もし、マクロAが関数型マクロでよいのなら、関数マクロの関数名にあたる部分だけを書いてもマクロ展開されないというルールを使って以下のようにかける。

#define A()     B
#define C       A
C       //→A

ただし、Cが関数名として使われる場合、

C()     //→B

と、Bに展開されてしまうので、この場合は

#define A()     B
#define C       A
#define D       ()
C D     //→A ()

と二つのマクロに分ける。

関数呼び出しをフックする

int main(){ foo(); }
int foo(){}

というコードで、fooの呼び出しをフックして

int main(){ foo(); }
int foo(){ /*pre process*/ foo2(); /*post process*/ }   //元のfooの前後に適当な処理をしたい。
int foo2(){}    //これが元のfoo

という風にしたいとする。

以下のようにマクロを書くという方法がある。

int main(){ foo(); }

#define DEF_FOO2_HEAD   int foo
#define DEF_FOO2_TAIL   (){ /*pre process*/ foo2(); /*post process*/ }
#define foo(...) foo2(__VA_ARGS__)

DEF_FOO2_HEAD DEF_FOO2_TAIL
int foo(){}

元のマクロをリネームする。その際に可変長引数関数マクロとしておくと可変長引数関数も扱える。 次に、フック関数を定義する。この際、普通に定義すると関数名も展開されてしまう。

#define DEF_FOO2        int foo(){ /*pre process*/ foo2(); /*post process*/ }
DEF_FOO2        //→int foo2(){ /*pre process*/ foo2(); /*post process*/ }

そこで関数名までと、関数の引数以降を分けて定義することでうまく記述できる

チケットの履歴

更新者: mtamaki (12 か月 前)

  • 説明 が変更されました (diff)
Note: チケットについてのヘルプは TracTickets を参照 して下さい。