17

Cでの#undefの実用化について疑問に思っています。私はK&Rを使用して作業しており、プリプロセッサを使用しています。これのほとんどは私が(多かれ少なかれ)理解した資料でしたが、90ページ(第2版)の何かが私に突き出ていました:

名前は#undef、通常、ルーチンがマクロではなく実際に関数であることを確認するために、で定義されていない場合があります。

#undef getchar

int getchar(void) { ... }

#defineこれは、関数と同じ名前のマクロを誰かから守るための一般的な方法ですか?それとも、これは実際には発生しないサンプルのようなものですか?(例えば、彼の正しい、間違った、または狂気の人は書き直すgetchar()べきではないので、それは思い浮かばないはずです。)あなた自身の関数名で、これを行う必要性を感じますか?他の人が使用できるライブラリを開発している場合、それは変わりますか?

4

8 に答える 8

16

機能

Plauger のThe Standard C Library<stdio.h> (1992) を読むと、ヘッダーが を関数のようなマクロとして提供できるgetchar()ことがわかります (そのファイル ポインター引数を複数回評価するgetc()ための特別な権限があります!)。getc()ただし、マクロを提供する場合でも、実装は、主にgetchar()orgetc()と呼ばれる関数ポインターにアクセスして、それを他の関数に渡すことができるように、同じジョブを実行する実際の関数を提供する義務があります。

つまり、次のようにします。

#include <stdio.h>
#undef getchar

extern int some_function(int (*)(void));

int core_function(void)
{
   int c = some_function(getchar);
   return(c);
}

書かれているように、core_function()はかなり無意味ですが、要点を示しています。たとえば、isxxxx()マクロでも同じことができます。<ctype.h>

通常、それはしたくありません。通常、マクロ定義を削除したくはありません。ただし、実際の機能が必要な場合は、それを手に入れることができます。ライブラリを提供する人は、標準 C ライブラリの機能を効果的にエミュレートできます。

めったに必要ない

また、明示的に使用する必要がほとんどない理由の 1 つは、次のよう#undefに記述してマクロの代わりに関数を呼び出すことができるためです。

int c = (getchar)();

後のトークンは でgetcharはないため(、関数のようなマクロの呼び出しではないため、関数への参照である必要があります。同様に、上記の最初の例は、#undef.

マクロ オーバーライドを使用して独自の関数を実装する場合は、これを使用して良い効果を得ることができますが、説明しないと少し混乱する可能性があります。

/* 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);
}

の後のトークンfunctionが not であるため、関数定義行はマクロを呼び出しません(return行はマクロを呼び出します。

于 2008-10-20T00:35:39.233 に答える
14

マクロは、大量のコードを生成するためによく使用されます。多くの場合、かなりローカライズされた使用法であり、#undef名前の衝突を回避するために特定のヘッダーの末尾にあるヘルパー マクロに対して安全であるため、実際に生成されたコードのみが他の場所にインポートされ、コードの生成に使用されるマクロはインポートされません。

/編集: 例として、これを使用して構造体を生成しました。以下は、実際のプロジェクトからの抜粋です。

#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
于 2008-10-19T20:27:26.503 に答える
6

プリプロセッサ#defineはすべて 1 つのグローバル名前空間にあるため、名前空間の競合が発生しやすく、特にサードパーティ ライブラリを使用している場合はそうです。たとえば、 という名前の関数を作成する場合、OpenFile正しくコンパイルされない可能性があります。これは、ヘッダー ファイルがまたは( が定義されているかどうかに応じて)にマップする<windows.h>トークンを定義しているためです。正しい解決策は、関数を定義する前です。OpenFileOpenFileAOpenFileWUNICODE#undef OpenFile

于 2008-10-19T20:03:28.190 に答える
2

ファイル内のマクロ#includedが私の関数の1つに干渉している場合にのみ使用します(たとえば、同じ名前の場合)。次に#undef、マクロを使用して、独自の関数を使用できるようにします。

于 2008-10-19T19:50:44.597 に答える
2

ジョナサン・レフラーが正しい答えをくれたと思いますが。これは、#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 を付けてください。

于 2008-10-20T10:19:27.650 に答える
1

これは、関数と同じ名前のマクロを #define する誰かに対して防御するための一般的な方法ですか? それとも、これは実際には発生しないサンプルのようなものですか? (例えば、正しい人も、間違った人も、非常識な人も getchar() を書き換えるべきではないので、出てくるべきではありません。)

両方とも少し。良いコードは を使用する必要は#undefありませんが、作業しなければならない悪いコードがたくさんあります。. #undef_#define bool int

于 2008-10-19T20:00:41.050 に答える
1

グローバル名前空間を汚染するマクロの問題を修正することに加えて、 の別の用途は#undef、マクロが異なる場所で異なる動作をする必要がある場合です。これはあまり一般的なシナリオではありませんが、次のようなシナリオが思い浮かびます。

  • マクロは、assertコードの一部でデバッグを実行したいが他の部分でデバッグを実行したくない場合に備えて、コンパイル単位の途中で定義を変更することができます。これを行うにはassertそれ自体を#undef編集する必要があるだけでなく、NDEBUGマクロを再定義して目的の動作を再構成する必要があります。assert

  • マクロを使用して変数を として宣言することにより、グローバルが一度だけ定義されるようにするために使用される手法を見てきました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"

/* ------------------------------------------------------ */
于 2008-10-19T20:25:11.157 に答える
0

マクロを定義できない場合は、定義を解除する機能が必要です。

私が使用するメモリトラッカーは、ファイル/行情報を追跡するために独自の新規/削除マクロを定義します。このマクロはSC++Lを壊します。

#pragma push_macro( "new" )
#undef new
#include <vector>
#pragma pop_macro( "new" )

より具体的な質問について:名前空間は、ライブラリ関数の前に識別子を付けることでCでエミュレートされることがよくあります。

盲目的に定義を解除するマクロは、混乱を招き、保守性を低下させ、元の動作に依存するものを壊す可能性があります。強制された場合は、少なくともプッシュ/ポップを使用して、他のすべての場所で元の動作を維持します。

于 2008-10-20T00:49:09.270 に答える