Skip to content

Instantly share code, notes, and snippets.

@n01e0
Last active August 19, 2019 01:51
Show Gist options
  • Save n01e0/c394a9ad29773324b1719a5c571ca257 to your computer and use it in GitHub Desktop.
Save n01e0/c394a9ad29773324b1719a5c571ca257 to your computer and use it in GitHub Desktop.
”型”を受け取るマクロの実装

"型"を引数に受け取るマクロ

agenda

前の記事stdarg.hを使っていた中で、

va_arg()マクロで"型"を引数として受け取っている点が気になったので少し調べてみた。

includeしたマクロの詳細が気になるのは人間の性。

自力実装

"型"を引数に受け取るマクロの実装となって最初に思いついたのはtypeofだった。

#define cast(src, type) ((typeof(type))src)

これで一応srcに渡した値をtypeの型にキャスト出来た。

ではva_argはどういった実装だろうか。

manを読む

まずはstdarg.hのmanを読んでみる。

すると、va_arg

type va_arg(va_list ap, type); 

va_arg() マクロは、呼び出しの次の引数の型と値がある式に展開されます。 パラメータ ap は、 va_start() によって初期化される va_listap です。 va_arg() の呼び出しごとに ap は修正され、次の呼び出しが次の引数を返します。 パラメータ type は、指定型のオブジェクトを指すポインタの型が * を type に追加するだけで得られるように指定された型名です。

次の引数がない場合、または type が (デフォルトの引数生成に従って生成されたような) 実際の次の引数の型と互換性がない場合、 ランダムなエラーが発生します。

と定義されている。

ここで気になるパラメータはtypeである。

要約すると引数の型へのポインタ型であり、*をつければ実際の型が得られる

と言うような定義である。

なるほど。

ソースを読む

定義はなんとなくわかったので、どうなっているかを見てみる。

まずはstdarg.hを読む

includeしたファイルのソースを読みたくなったら、gccのオプションを使って

$ gcc -H hoge.c

とする事で、ソースファイルの名前がわかる。

自分の環境では. /usr/lib/gcc/x86_64-linux-gnu/7/include/stdarg.hにあった。

早速読んでみると、

#define va_arg(v, l) __builtin_va_arg(v, l)

とある。

う〜ん…

gccのソース内のbuiltin系を読んでみる。

調べてみると一部の環境では以下のような実装だとわかった。

__builtin_va_arg(ap, t) (*(*(t **)&ap)++)

これは意外とわかりやすい。

再実装

この方式で実装し直してみると

#define cast(src, type) (*(type *)&src)

となる。

なかなか綺麗だ。

が、これはマクロである事を忘れてはいけない。

そう考えるとそもそも

#define cast(src, type) ((type)src)

で良いのだ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment