web-dev-qa-db-ja.com

C / C ++でマクロ展開を一時的に無効にするにはどうすればよいですか?

何らかの理由で、ヘッダーファイルの一部のマクロを一時的に無効にする必要があります。#undef MACRONAMEはコードをコンパイルしますが、既存のマクロの定義を解除します。

単に無効にする方法はありますか?

あなたはマクロの値を本当に知らないこと、そして私はクロスコンパイラソリューションを探していることを言及する必要があります(少なくともGCCとMSVCで動作するはずです)。

38
sorin

MSVCでは、 Push_macro プラグマ、GCC supports MicrosoftWindowsコンパイラとの互換性のために。

#pragma Push_macro("MACRONAME")
#undef MACRONAME

// some actions

#pragma pop_macro("MACRONAME")
84

標準C(C89、C99、またはC11)で定義されている機能のみを使用する場合、「無効化」メカニズムは_#undef_のみです。

問題は、「再有効化」メカニズムがないことです。


他の人が指摘しているように、マクロ定義を含むヘッダーファイルがtypedefまたはenum宣言を含まないように構造化されている場合(これらを繰り返すことはできません。関数宣言と変数宣言を繰り返すことができます)、_#undef_マクロを実行し、マクロを有効にせずに必要なことを実行してから、おそらく再包含に対する保護を定義解除した後、ヘッダーを再インクルードします。

もちろん、マクロがヘッダーで定義されていない場合は、コードをリファクタリングしてヘッダーに含めるまでスタックします。

もう1つのトリックが利用可能です-マクロが関数のようなマクロであり、オブジェクトのようなマクロではない場合。

_#define nonsense(a, b)   b /\= a

int (nonsense)(int a, int b)
{
    return (a > b) ? a : b;
}
_

関数nonsense()は、その直前のマクロにもかかわらず、正しく定義されています。これは、マクロの呼び出し(関数のようなマクロの場合)の直後に開き括弧を付ける必要があるためです(空白を付けるか、コメントを含めることもできます)。関数定義行では、「ナンセンス」の後のトークンは閉じ括弧であるため、nonsenseマクロの呼び出しではありません。

マクロが引数のないオブジェクトのようなマクロであった場合、トリックは機能しませんでした。

_#define nonsense min

int (nonsense)(int a, int b)
{
    // Think about it - what is the function really called?
    return (a > b) ? a : b;
}
_

このコードは、minと呼ばれ、無意味な偽の関数を定義します。そして、マクロからの保護はありません。

これが、標準が「実装」用に予約されている名前空間を慎重に定義する理由の1つです。実装は、それらの名前が実装に予約されている場合、それが望むまたは必要とする任意のタイプ(関数のようなまたはオブジェクトのような)の任意の目的のためにマクロを定義することができます。実装のサービスの利用者が実装に予約されている名前を使用または定義しようとする場合、コードは遅かれ早かれ壊れることに注意する必要があります。それはあなたのせいであり、のせいではありません。実装。

28

マクロはひざまずきますが、最も一般的な解決策は、同じソースファイルでマクロを再度有効にする必要がないようにコードを再構築することではないでしょうか。一部のコードを別の関数と別のソースファイルに抽出して、問題のあるマクロの定義を解除することはできませんか。

2
UncleBens

マクロはいくつかのヘッダーファイルから取得されるため、それらの値にアクセスできる必要があります。その後、次のようなことを行うことができます

#include <foo.h> // declares macro FOO

// Do things with FOO

#undef FOO

// do things without FOO

#include <foo.h> // reenable FOO

次に、ヘッダーをこれらの線に沿って設計する必要があります

#ifndef FOO
#define FOO do_something(x,y)
#endif
1
Wernsey

編集:

あなたはそれがとても簡単だと思うかもしれません:

#ifdef macro
#define DISABLED_macro macro
#undef macro
#endif

// do what you want with macro

#ifdef DISABLED_macro
#define macro DISABLED_macro
#endif

しかし、そうではありません(次の例が示すように)!

#include <iostream>
#include <limits>

#include <windows.h>

#ifdef max
#define DISABLED_max max
#undef max
#endif

int main()
{
    std::cout << std::numeric_limits<unsigned long>::max() << std::endl;

#ifdef DISABLED_max
#define max DISABLED_max
#endif

    std::cout << max(15,3) << std::endl;  // error C3861: "max": identifier not found
    return 0;
}

マクロで#undefを使用し、元のヘッダーを再度含めることも、ヘッダーガードのため、機能しない可能性があります。したがって、残っているのはPush_macro/pop_macro #pragmaディレクティブを使用することです。

#pragma Push_macro("MACRO")
#undef MACRO
// do what you want
#pragma pop_macro("MACR")
0
fmuecke