マクロは、プリプロセッサが正規のコードに挿入するテキストのコピー/貼り付け部分です。マクロの作成者は、置き換えによって有効なコードが生成されることを期待しています。
これを成功させるには、3つの「ヒント」があります。
マクロが本物のコードのように動作するのを助けます
通常のコードは通常セミコロンで終わります。ユーザーがコードを必要としない場合は、コードを表示する必要があります...
doSomething(1) ;
DO_SOMETHING_ELSE(2) // <== Hey? What's this?
doSomethingElseAgain(3) ;
これは、セミコロンがない場合、ユーザーはコンパイラーがエラーを生成することを期待することを意味します。
しかし、本当の本当に良い理由は、いつか、マクロの作者がおそらくマクロを本物の関数(おそらくインライン化)に置き換える必要があることです。したがって、マクロは実際のように動作するはずです。
したがって、セミコロンを必要とするマクロが必要です。
有効なコードを生成する
jfm3の回答に示されているように、マクロに複数の命令が含まれている場合があります。マクロがifステートメント内で使用されている場合、これは問題になります。
if(bIsOk)
MY_MACRO(42) ;
このマクロは次のように展開できます。
#define MY_MACRO(x) f(x) ; g(x)
if(bIsOk)
f(42) ; g(42) ; // was MY_MACRO(42) ;
g
関数は、の値に関係なく実行されますbIsOk
。
つまり、マクロにスコープを追加する必要があります。
#define MY_MACRO(x) { f(x) ; g(x) ; }
if(bIsOk)
{ f(42) ; g(42) ; } ; // was MY_MACRO(42) ;
有効なコードを生成する2
マクロが次のようなものである場合:
#define MY_MACRO(x) int i = x + 1 ; f(i) ;
次のコードで別の問題が発生する可能性があります。
void doSomething()
{
int i = 25 ;
MY_MACRO(32) ;
}
それは次のように拡張されるためです:
void doSomething()
{
int i = 25 ;
int i = 32 + 1 ; f(i) ; ; // was MY_MACRO(32) ;
}
もちろん、このコードはコンパイルされません。つまり、ソリューションはスコープを使用しています。
#define MY_MACRO(x) { int i = x + 1 ; f(i) ; }
void doSomething()
{
int i = 25 ;
{ int i = 32 + 1 ; f(i) ; } ; // was MY_MACRO(32) ;
}
コードは再び正しく動作します。
セミコロン+スコープ効果を組み合わせる?
この効果を生み出すC / C ++イディオムが1つあります:do / whileループ:
do
{
// code
}
while(false) ;
do / whileはスコープを作成できるため、マクロのコードをカプセル化し、最後にセミコロンが必要になるため、必要なコードに拡張できます。
おまけ?
C ++コンパイラーは、事後条件がfalseであるという事実がコンパイル時にわかっているため、do / whileループを最適化します。これは、次のようなマクロを意味します。
#define MY_MACRO(x) \
do \
{ \
const int i = x + 1 ; \
f(i) ; g(i) ; \
} \
while(false)
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
MY_MACRO(42) ;
// Etc.
}
正しく拡張されます
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
do
{
const int i = 42 + 1 ; // was MY_MACRO(42) ;
f(i) ; g(i) ;
}
while(false) ;
// Etc.
}
そしてコンパイルされて最適化されます
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
{
f(43) ; g(43) ;
}
// Etc.
}
void
、最後にtypeの式を追加します... ((void)0)のようにします。