何らかの理由でマクロMACRO(X,Y)
を書く必要があるとしましょう。 (インライン関数を使用できない正当な理由があると仮定しましょう。)このマクロは、戻り値のない関数の呼び出しをエミュレートします。 。
if (x > y)
MACRO(x, y);
do_something();
if (x > y)
MACRO(x, y);
else
MACRO(y - x, x - y);
do_something();
MACRO(x, y)
do_something();
マクロを記述する単純な方法は次のとおりです。
#define MACRO(X,Y) \
cout << "1st arg is:" << (X) << endl; \
cout << "2nd arg is:" << (Y) << endl; \
cout << "Sum is:" << ((X)+(Y)) << endl;
これは3つの例すべてに失敗する非常に悪い解決策であり、理由を説明する必要はありません。
マクロが実際に行うことは無視してください、それはポイントではありません。
今、私が最もよく書かれているマクロを見る方法は、次のように中括弧で囲むことです:
#define MACRO(X,Y) \
{ \
cout << "1st arg is:" << (X) << endl; \
cout << "2nd arg is:" << (Y) << endl; \
cout << "Sum is:" << ((X)+(Y)) << endl; \
}
これは、マクロが1つのステートメントブロックにあるため、例1を解決します。ただし、マクロの呼び出しの後にセミコロンを挿入するため、例2は壊れています。これにより、コンパイラはセミコロンをそれ自体がステートメントであると見なします。つまり、elseステートメントはどのifステートメントにも対応しません。最後に、コードブロックにはセミコロンが必要ないため、例3はセミコロンがなくても問題なくコンパイルできます。
3つの例すべてに合格するようにマクロを記述する方法はありますか?
注: 受け入れられているヒントの共有方法 の一部として自分の回答を提出していますが、より良い解決策があれば、ここに投稿してください。私の方法よりも多くの票を得ることができます。 :)
通常、マクロは使用しないでください。常にインライン関数を優先します。その塩に値するコンパイラーは、マクロであるかのように小さな関数をインライン化できる必要があり、インライン関数は名前空間と他のスコープを尊重し、すべての引数を一度評価します。
マクロである必要がある場合は、whileループ(既に提案されている)が機能するか、コンマ演算子を試すことができます。
_#define MACRO(X,Y) \
( \
(cout << "1st arg is:" << (X) << endl), \
(cout << "2nd arg is:" << (Y) << endl), \
(cout << "3rd arg is:" << ((X) + (Y)) << endl), \
(void)0 \
)
_
_(void)0
_により、ステートメントはvoid
タイプのいずれかに評価されます。セミコロンではなくコンマを使用すると、スタンドアロンとしてのみではなく、ステートメント内で使用できます。理由のホストのために、インライン関数をお勧めしますが、少なくともその理由はMACRO(a++, b++)
がa
とb
を2回インクリメントするという事実です。
かなり賢い解決策があります:
#define MACRO(X,Y) \
do { \
cout << "1st arg is:" << (X) << endl; \
cout << "2nd arg is:" << (Y) << endl; \
cout << "Sum is:" << ((X)+(Y)) << endl; \
} while (0)
これで、単一のブロックレベルのステートメントができました。その後にセミコロンが必要です。これは、3つの例すべてで期待どおりに動作します。
「マクロの機能を無視する」と言ったのは知っていますが、タイトルに基づいて検索することでこの質問を見つけることができるので、マクロで関数をエミュレートするためのさらなるテクニックの議論が必要だと思います。
私が知っている最も近いものは:
#define MACRO(X,Y) \
do { \
auto MACRO_tmp_1 = (X); \
auto MACRO_tmp_2 = (Y); \
using std::cout; \
using std::endl; \
cout << "1st arg is:" << (MACRO_tmp_1) << endl; \
cout << "2nd arg is:" << (MACRO_tmp_2) << endl; \
cout << "Sum is:" << (MACRO_tmp_1 + MACRO_tmp_2) << endl; \
} while(0)
これは次のことを行います。
ただし、次の点で関数とは異なります。
_libc6
_からの回答は次のとおりです。 _/usr/include/x86_64-linux-gnu/bits/byteswap.h
_を見てみると、あなたが探していたトリックが見つかりました。
以前のソリューションに対するいくつかの批評家:
auto
キーワードを使用しますが、これは問題ありませんが、代わりに既知/予期されるタイプを使用してくださいです。秘Theは、_(expr,expr)
_構造と_{}
_スコープの両方を使用することです:
_#define MACRO(X,Y) \
( \
{ \
register int __x = static_cast<int>(X), __y = static_cast<int>(Y); \
std::cout << "1st arg is:" << __x << std::endl; \
std::cout << "2nd arg is:" << __y << std::endl; \
std::cout << "Sum is:" << (__x + __y) << std::endl; \
__x + __y; \
} \
)
_
register
キーワードの使用に注意してください。これはコンパイラーへのヒントにすぎません。 X
およびY
マクロパラメーターは、(既に)括弧で囲まれ、castedで期待されるタイプになっています。このソリューションは、パラメータが一度だけ評価されるため、プリインクリメントとポストインクリメントで適切に機能します。
例として、要求されていなくても、___x + __y;
_ステートメントを追加しました。これは、ブロック全体を正確な式として評価する方法です。
マクロが式に評価されないことを確認したい場合は、void();
を使用する方が安全です。したがって、rvalue
が期待される場所では不正です。
ただし、、解決策はISO C++準拠ではない _g++ -pedantic
_:
_warning: ISO C++ forbids braced-groups within expressions [-pedantic]
_
_g++
_に休息を与えるには、_(__extension__ OLD_WHOLE_MACRO_CONTENT_HERE)
_を使用して、新しい定義を読み取ります。
_#define MACRO(X,Y) \
(__extension__ ( \
{ \
register int __x = static_cast<int>(X), __y = static_cast<int>(Y); \
std::cout << "1st arg is:" << __x << std::endl; \
std::cout << "2nd arg is:" << __y << std::endl; \
std::cout << "Sum is:" << (__x + __y) << std::endl; \
__x + __y; \
} \
))
_
私のソリューションをさらに改善するために、Cの MINおよびMAXに見られるように、___typeof__
_キーワードを使用します :
_#define MACRO(X,Y) \
(__extension__ ( \
{ \
__typeof__(X) __x = (X); \
__typeof__(Y) __y = (Y); \
std::cout << "1st arg is:" << __x << std::endl; \
std::cout << "2nd arg is:" << __y << std::endl; \
std::cout << "Sum is:" << (__x + __y) << std::endl; \
__x + __y; \
} \
))
_
これで、コンパイラは適切な型を決定します。これもgcc
拡張です。
register
キーワードが削除されていることに注意してください。クラスタイプで使用すると、次の警告が表示されます。
_warning: address requested for ‘__x’, which is declared ‘register’ [-Wextra]
_
C++ 11はラムダをもたらしました。これはこの状況で非常に便利です。
#define MACRO(X,Y) \
[&](x_, y_) { \
cout << "1st arg is:" << x_ << endl; \
cout << "2nd arg is:" << y_ << endl; \
cout << "Sum is:" << (x_ + y_) << endl; \
}((X), (Y))
マクロの生成力は維持しますが、必要なもの(void
を含む)を返すことができる快適なスコープがあります。さらに、マクロパラメーターを複数回評価する問題が回避されます。
を使用してブロックを作成する
#define MACRO(...) do { ... } while(false)
;を追加しないでください。 while(false)の後
あなたの答えは、複数評価の問題に苦しんでいるので、(例えば)
macro( read_int(file1), read_int(file2) );
予期しない、おそらく望ましくないことを行います。
他の人が述べたように、可能な限りマクロを避けるべきです。マクロの引数が複数回評価された場合、副作用が存在すると危険です。引数のタイプがわかっている場合(またはC++ 0x auto
機能を使用できる場合)、一時評価を使用して単一の評価を実施できます。
別の問題:複数の評価が発生する順序は、予想どおりではない場合があります!
次のコードを検討してください。
#include <iostream>
using namespace std;
int foo( int & i ) { return i *= 10; }
int bar( int & i ) { return i *= 100; }
#define BADMACRO( X, Y ) do { \
cout << "X=" << (X) << ", Y=" << (Y) << ", X+Y=" << ((X)+(Y)) << endl; \
} while (0)
#define MACRO( X, Y ) do { \
int x = X; int y = Y; \
cout << "X=" << x << ", Y=" << y << ", X+Y=" << ( x + y ) << endl; \
} while (0)
int main() {
int a = 1; int b = 1;
BADMACRO( foo(a), bar(b) );
a = 1; b = 1;
MACRO( foo(a), bar(b) );
return 0;
}
そして、それはコンパイルされて私のマシン上で実行される出力です:
X = 100、Y = 10000、X + Y = 110 X = 10、Y = 100、X + Y = 110