質問
Cでの#undefの実際の使用について疑問に思っています。私はK <!> amp; Rを使って作業しており、プリプロセッサ次第です。これの大部分は私が(多かれ少なかれ)理解した資料でしたが、90ページ(第2版)の何かが私に突き出ました:
#undef
で名前を未定義にすることができます。 通常、ルーチンが 実際にはマクロではなく関数:
#undef getchar
int getchar(void) { ... }
これは、あなたの機能と同じ名前のマクロを誰かが#define
することを防ぐための一般的な慣習ですか?それとも、これは実際には発生しないサンプルの多くですか? (EG、彼の権利、間違った、または狂気の誰もgetchar()
を書き換えるべきではないので、それは出てくるべきではありません。)あなた自身の関数名で、あなたはこれを行う必要を感じますか?他の人が使用するライブラリを開発している場合、それは変わりますか?
解決
機能
Plaugerの標準Cライブラリ(1992)を読むと、 <stdio.h>
ヘッダーは、getchar()
およびgetc()
を関数のようなマクロとして提供できます(core_function()
にファイルポインター引数を複数回評価する特別な許可があります!)。ただし、マクロを提供する場合でも、実装では、主にisxxxx()
または<ctype.h>
という関数ポインターにアクセスして他の関数に渡すことができるように、同じジョブを実行する実際の関数を提供する必要があります。
つまり、次のようにします。
#include <stdio.h>
#undef getchar
extern int some_function(int (*)(void));
int core_function(void)
{
int c = some_function(getchar);
return(c);
}
書かれているように、#undef
はかなり無意味ですが、ポイントを示しています。たとえば、getchar
の(
マクロでも同じことができます。
通常、あなたはそれをしたくない-あなたは通常マクロ定義を削除したくない。しかし、実際の機能が必要な場合は、それを手に入れることができます。ライブラリを提供する人は、標準Cライブラリの機能をエミュレートして、効果を上げることができます。
ほとんど必要ない
また、明示的なfunction
を使用する必要がほとんどない理由の1つは、次のように記述することにより、マクロの代わりに関数を呼び出すことができるためです。
int c = (getchar)();
return
の後のトークンは<=>ではないため、関数のようなマクロの呼び出しではないため、関数への参照でなければなりません。同様に、上記の最初の例は、<=>がなくても正しくコンパイルおよび実行されます。
マクロのオーバーライドを使用して独自の関数を実装する場合、これを使用して効果を発揮できますが、説明しない限り多少混乱する可能性があります。
/* function.h */
…
extern int function(int c);
extern int other_function(int c, FILE *fp);
#define function(c) other_function(c, stdout);
…
/* function.c */
…
/* Provide function despite macro override */
int (function)(int c)
{
return function(c, stdout);
}
<=>の後のトークンが<=>ではないため、関数定義行はマクロを呼び出しません。 <=>行はマクロを呼び出します。
他のヒント
マクロは、大量のコードを生成するためによく使用されます。多くの場合、かなりローカライズされた使用方法であり、名前の衝突を避けるために特定のヘッダーの末尾にあるヘルパーマクロを安全に#undef
するので、実際に生成されたコードのみが他の場所にインポートされ、コードの生成に使用されるマクロはインポートされません。
/ Edit:例として、これを使用して構造体を生成しました。以下は、実際のプロジェクトからの抜粋です。
#define MYLIB_MAKE_PC_PROVIDER(name) \
struct PcApi##name { \
many members …
};
MYLIB_MAKE_PC_PROVIDER(SA)
MYLIB_MAKE_PC_PROVIDER(SSA)
MYLIB_MAKE_PC_PROVIDER(AF)
#undef MYLIB_MAKE_PC_PROVIDER
プリプロセッサ#define
はすべて1つのグローバル名前空間にあるため、特にサードパーティライブラリを使用する場合は、名前空間の競合が発生しやすくなります。たとえば、OpenFile
という名前の関数を作成する場合、ヘッダーファイル<windows.h>
がトークンOpenFileA
を定義してOpenFileW
またはUNICODE
(< =>が定義されているかどうか)。正しい解決策は、関数を定義する前に#undef
<=>にすることです。
#included
ファイル内のマクロが私の機能の1つと干渉している場合にのみ使用します(たとえば、同じ名前です)。次に、マクロを#undef
して、独自の関数を使用できるようにします。
ジョナサン・レフラーはあなたに正しい答えを与えたと思いますが。これは非常にまれなケースで、#undefを使用しています。通常、マクロは多くの関数内で再利用可能でなければなりません。そのため、ファイルの先頭またはヘッダーファイルで定義します。ただし、マクロで短縮できる関数内に繰り返しコードがいくつかある場合があります。
int foo(int x, int y)
{
#define OUT_OF_RANGE(v, vlower, vupper) \
if (v < vlower) {v = vlower; goto EXIT;} \
else if (v > vupper) {v = vupper; goto EXIT;}
/* do some calcs */
x += (x + y)/2;
OUT_OF_RANGE(x, 0, 100);
y += (x - y)/2;
OUT_OF_RANGE(y, -10, 50);
/* do some more calcs and range checks*/
...
EXIT:
/* undefine OUT_OF_RANGE, because we don't need it anymore */
#undef OUT_OF_RANGE
...
return x;
}
このマクロは関数内でのみ有用であることを読者に示すために、最後に定義されていません。このようなハック的なマクロを使用することを誰にも勧めたくありません。ただし、必要な場合は、最後に#undefしてください。
これは、誰かがあなたの関数と同じ名前のマクロを#defineすることを防ぐための一般的な慣習ですか?それとも、これは実際には発生しないサンプルの多くですか? (例えば、彼の右心、間違った心、狂気の心の誰もgetchar()を書き換えるべきではないので、それは現れないはずです。)
両方とも少し。良いコードでは#undef
を使用する必要はありませんが、多くの悪いコードがあります。 #define bool int
は、誰かが<=>のようなトリックを引いたときに非常に貴重であることがわかります。
グローバル名前空間を汚染するマクロの問題を修正することに加えて、#undef
の別の使用法は、マクロが異なる場所で異なる動作をする必要がある場合です。これはあまり一般的なシナリオではありませんが、頭に浮かぶカップルは次のとおりです。
-
assert
マクロでは、コンパイルユニットの途中で定義を変更できます。これにより、コードの一部でデバッグを実行し、他の部分ではデバッグを実行しない場合があります。これを行うにはNDEBUG
自体をextern
する必要があることに加えて、<=> の望ましい動作を再構成するために<=>マクロを再定義する必要があります
-
マクロを使用して変数を<=>として宣言することにより、グローバルが一度だけ定義されることを保証するために使用されるテクニックを見ましたが、ヘッダー/宣言は変数の定義に使用されます。
次のようなもの(これは必ずしも良いテクニックだと言っているのではなく、実際に見たものの1つです):
/* globals.h */
/* ------------------------------------------------------ */
#undef GLOBAL
#ifdef DEFINE_GLOBALS
#define GLOBAL
#else
#define GLOBAL extern
#endif
GLOBAL int g_x;
GLOBAL char* g_name;
/* ------------------------------------------------------ */
/* globals.c */
/* ------------------------------------------------------ */
#include "some_master_header_that_happens_to_include_globals.h"
/* define the globals here (and only here) using globals.h */
#define DEFINE_GLOBALS
#include "globals.h"
/* ------------------------------------------------------ */
マクロを定義解除できる場合は、定義解除する機能が必要です。
使用するメモリトラッカーは、ファイル/行情報を追跡するための独自の新規/削除マクロを定義します。このマクロはSC ++ Lを破壊します。
#pragma push_macro( "new" )
#undef new
#include <vector>
#pragma pop_macro( "new" )
より具体的な質問について:名前空間は多くの場合、ライブラリ関数の前に識別子を付けることでCでエミュレートされます。
明らかにマクロを定義解除すると、混乱が生じ、保守性が低下し、元の動作に依存しているものが壊れる可能性があります。強制された場合は、少なくともプッシュ/ポップを使用して、他のすべての場所で元の動作を維持します。