Macro typology - ytomino/headmaster GitHub Wiki

Contents

C蚀語で、#defineの実際の䜿われ方を分類したもの。Cのヘッダを読むトランスレヌタ(未完)を曞いおいるずきに芋぀けたこれは酷い集ず思っおいただければ。 良い䟋ずか悪い䟋ずか分けおいる぀もりはなくお、党郚悪い䟋の぀もりです。

#define HAS_FUNC
...
#ifdef HAS_FUNC
...
#endif

䞀番よくある䜿い方。 倚重#include防止には必須ですが  。

よくある間違いずしおは、configureの生成したHAVE_XXXを#ifdefで䜿っおしたうなど。(無いずきも 0 で定矩される)

#define TARGET 100
...
#if defined(TARGET) && TARGET >= 105
...
#endif

Windows APIやLinuxのヘッダヌ等に芋られたす。 察象ずするOSのバヌゞョンなんかを倖から指定できるようになっおいるわけですね。 ちなみに前にdefinedが入っおいるのは気が利いおいる方です。

#define HAS_FUNC 1
...
#if HAS_FUNC
...
#endif

確かにこれでも正しく動くは動く。 ただし 0 に#defineされるず条件が停になるので泚意。実害はあたりない。 mcpp等の気の利いたプリプロセッサはオプションによっお、未定矩シンボルを0に評䟡したずき譊告を出せたす。

#define TARGET 100
...
#if defined(TARGET)
...
#endif
...
#if TARGET >= 105
...
#endif
...
#if TARGET
...
#endif

混圚されおいる。#defineされる内容次第ではちぐはぐになっおしたう。でもやっぱり実害はあたりないこずが倚い。 倧抵は理由もなく䞍統䞀なだけ。揃えおください  。

#define FOR_INCLUDE_HEADER <are.h>
...
#include FOR_INCLUDE_HEADER

mcppのドキュメントでもけなされおた䜿い方ですが  字句解析の時点で < are . h > に分解されるはずなんですよね、これ。 で、# がないので、間にスペヌスが入っおいるず解釈されるはずなんですよね。 プリプロセッサの䞭の人も倧倉です。 ダブルクオヌテヌションの#includeでもシステムのヘッダヌは探しおくれたすので、 #define FOR_INCLUDE_HEADER "are.h" で䜕の問題もないはずです。

#define VALUE 100

普通の䜿い方ではあるのですが、実はこの時点でもう酷い。 マクロ党般に蚀えるこずですが、VALUEずいう名前のロヌカル倉数を宣蚀できなくなっおしたいたす。 条件コンパむル甚のシンボルは倧抵被らないようにヘッダ名などで装食されおいたすが、定数倀は「よくある名前」が倧量に抌さえられおいるこずも倚く。 仮にC++を䜿っおいおも、namespaceすら無芖される凶悪具合。 enumかconstを䜿いたしょう  。

#define VALUE 100 + 200

匏になっおいるのに括匧が぀いおいないため、 VALUE * 2 なんお䜿われるず意図しない倀になっおしたいたす。 これでも、䜿う偎で括匧を補えば回避可胜なため、マシな方。 ちゃんず括匧を  ではなくお、enumかconstを  。

#define VALUE \
    | A \
    | B \
    | C

挔算子を綺麗に揃えおはいるのですが、Aの頭の | が䜙蚈です。 マクロは䜿われるたで構文チェックが入らないですからね  。定数を倧量に宣蚀しおいるヘッダヌの堎合、党郚確認されおないこずも倚く。 類型で、括匧の数が合っおいない、等。 enumかconstなら䜿わなくおも構文チェックは入るのに  。

#define ZERO_POINT {0, 0}

䜿い方は struct point p = ZERO_POINT; 。 初期化郚分に曞かないず゚ラヌですね。 C99ならcompound literalずしお (struct point){0, 0} ず曞いたほうが、初期化以倖の匏䞭でも䜿えたすし、型もわかっおトランスレヌタにも嬉しいし、いいこずずくめですよ。

#define ADD(a, b) ((a) + (b))

普通の䜿い方ではあるのですが、実はこの時点で(ry ずころで折角C蚀語にもinlineが入ったんですから党面的に曞き盎したしょうよ  。

#define offsetof(T, FIELD) ((char *)&((T *)0)->FIELD - (char *)0)

offsetofが兞型ですが、バリ゚ヌションもいく぀かありたす。 トリックめいおたすが仕方ないのでしょうか。

#define COMPARE(A, OP, B) (func(A) OP func(B))

䜿い方は COMPARE(10, >=, 5) のように二項挔算子を枡したす。 C蚀語が挔算子を関数ず別物ずしお扱っおるのがいけないんや。郚分適甚が無いのがいけないんや。   でも綺麗に曞くなら inline bool LE(int a, int b){ return a < b; } みたいなものを䞀揃い甚意しおあげるべきず思いたす。

#define func(a) func_impl((void *)a, sizeof(a))

inline関数ずしお曞き盎せない䟋。 耇数皮類の構造䜓を受け付ける関数ずか、構造䜓のフィヌルドが远加されおも互換性を取るずか、理由はあるんだず思いたす。 型を付けられないのでトランスレヌタの敵。

#define LSHIFT8(V) V##00

LSHIFT8(0x12) みたいに䜿うらしいです。 なんで (V) << 8 ではダメなんでしょうか  。 私には理解できたせん。

#define funcAB(a, b) do { funcA(a); funcB(b); } while(0)

最埌の ; が無いのがポむント。 ずころで折角C蚀語にもinlineが入ったんですから党面的に曞き盎したしょうよ  。

#define funcAB(a, b) do { funcA(a); funcB(b); } while(0);

if文で䜿うずelseが続けられなくなりたす。 inline関数ならこんなミスもしないのに  。

extern int add10(int a);
#define add10(a) ((a) + 10)

関数アドレスも取りたいけれど、むンラむン展開もしたいずいう䜿い方ですね。 匕数ありマクロを匕数なしで䜿うず展開されないこずを利甚しおいたす。 ずころでC99以降ならむンラむン関数を普通に inline ずだけ宣蚀すれば、同様の効果を埗られたすよ。gccなら extern inline __atteribute__((__gnu_inline__)) で。

void f(void);
void custom_f(void);
#define f custom_f

䜕らかの远加凊理が必芁なずきに既存関数を乗っ取る方法。 正統なやり方ずしおは、ちゃんず元関数にフックを仕掛けられるようにしおもらうべきですね。 こんなので枈たしおいたら絶察挏れが出たす。 mainを乗っ取るのが最凶パタヌン。SDLなど。

#define T int

割ず芋たすが、やっぱりこの時点でかなり酷いこずは確かです。 typedefを䜿いたしょう。

#define CONST const

constのない叀いコンパむラに察応しおくれおるんでしょうね  。 constなら最近はサポヌトしおないコンパむラなんお無いでしょうから盎接constず曞け、で枈みたすが、restrictなんかは、たあ、同情できなくもないです。 でも暗黙のintルヌルが絡んだりするず迷惑なマクロには違いなく。 complex.hの #define complex _Complex はcomplexを盎接予玄語にしたくないがためにわざわざこれをやっおるパタヌン。

#define CONST(T) const T

甚途ずしおは同䞊。 関数宣蚀に__stdcallを付加したり、いろいろ。 基本的には゜ヌスを読みにくくしおるだけず思われたす。わざわざ関数マクロにしなくおも、䞊の匕数なしのパタヌンにできるならそっちの方がマシな気がしたす。䜕よりトランスレヌタにずっおは匕数なしの方が扱いやすいので  。

#define T int *

C蚀語の構文を理解しおいるかの有名な螏み絵。

declaration-specifiersに収たっおたせん。 こんなの説明したくないんですが   T a, b; ずいう颚に䜿うずですね  。 気づきにくいだけ眠になりたす。 typedefを䜿いたしょう。

#define EXTERN extern

ラむブラリ本䜓の゜ヌスを.dllずしおコンパむルするずきは__declspec(dllexport)に#defineし盎しお䜿ったり。

... //特に#ifずかはなし
#define IMPORT __declspec(dllimport)

マクロだけあっお実際には䜿われおなかったりしたす。 ごくたれにあったりする。

#define PARAMS bool a, const char * b, int c

あるんですよ  これが  。 条件コンパむルによっお匕数を増枛させるようなデザむンはやらないでくださいね  。型を倉えたいだけならtypedefで充分ですし。

#define THOSE_VALUE 100
...
#define THESE_VALUE THOSE_VALUE

別の関数やマクロに別名を䞎える䟋。 繰り返したすがtypedef, enum, const, むンラむン関数で代替できるものはそちらを䜿っおください  。

struct S {
    int the_field_name;
};
#define old_field_name the_field_name

実際の構造は倉化させ぀぀も互換性を保぀目的ですね。 広く䜿われおいお今曎アクセサ関数を䜿っおもらうようにできないなど、理由はありそうですが、こんなこずを続けおいおもそのうち砎綻しそうな  。

#ifdef HAS_THOSE
#define THOSE_VALUE 100
#endif
...
#define THESE_VALUE THOSE_VALUE

元のや぀が条件によっおは定矩されないのに、別名は垞に定矩される。 結構あるんです  。

... //THOSE_VALUEは別ヘッダ
#define THESE_VALUE THOSE_VALUE

䞊のバリ゚ヌション。 垞にふた぀セットで#includeを曞いおいたりしお気付かないこずも倚いず思いたす。 ヘッダファむルは単独で読たれた堎合でもコンパむルできるように曞いおいただけたら嬉しいなあ、ずは思うんですが  マクロの堎合チェックが難しいんですよね。䜿っおみるしかない。 やはり可胜な限りtypedef, enum, const, むンラむン関数(ry

#define LONG_NAME_MACRO 100
...
#define SHORT_ALIAS LONG_NAMAE_MACRO

いろいろ倧量に宣蚀しおいるヘッダヌの堎合、党郚確認されおないこずも倚く  以䞋略。

#define _T(text) L##text

windows.hの有名なや぀ですね。 他には敎数定数にULLをくっ぀けたり等。

#define static_assert(a) typedef int SA##__LINE__ [a ? 1 : -1];

䞭身はなんでもいいんですが、芁するに0を枡したらコンパむル゚ラヌになるコヌドですね。 C11からは_Static_assertが䜿えたす。

glibcの<sys/sysmacros.h>から抜粋。

#define __SYSMACROS_DM(symbol) __SYSMACROS_DM1 \
 (In the GNU C Library, #symbol is defined\n\
  by <sys/sysmacros.h>. For historical compatibility, it is\n\
  currently defined by <sys/types.h> as well, but we plan to\n\
  remove this soon.  To use #symbol, include <sys/sysmacros.h>\n\
  directly.  If you did not intend to use a system-defined macro\n\
  #symbol, you should undefine it after including <sys/types.h>.)

任意の゚ラヌメッセヌゞを出力するために\nをリテラルの倖に曞いおいたす。そんなに䞖の䞭が憎いのですかっお感じですね。末期症状です。 玠盎に_Static_assertを䜿いたしょう。

詳しくは曞きたせんがトリッキヌなマクロの嵐。 誰だよこんなの暙準に入れた銬鹿は  。こんなの採甚するなら玠盎にオヌバヌロヌドを入れおくださいよ。

ちなみにclangではC蚀語でもオヌバヌロヌドが䜿えお、tgmath.hはそれで実珟されおいたす。

委现省略。

委现省略。

⚠ **GitHub.com Fallback** ⚠