The Man Who Fell From The Wrong Side Of The Sky:2013年11月15日分

[最新版] [一覧] [前月] [今月] [翌月]

2013/11/15(Fri)

[Unix C][*BSD][や我N1] sys/cdefs.hとは何ですか? (その4)

その3ではK&RとANSI/ISO-Cでの関数定義の仮引数宣言について学びました。

さてNet/2で導入された時点でのsys/cdefs.hのマクロの残りをざっと片づけてしまいましょう。

@ __CONCAT() マクロ

これはANSI/ISO-Cで導入されたCプリプロセッサの「トークン結合演算子(6.10.3.3 ##演算子)」をラップ(LL Cool J感)するものです。

今時なのかアナクロなのかようわからんですが、Cでパッケージとか名前空間みたいな事をするのに

#define COMPAT(name)	compat_##name

struct COMPAT(data) {
	...
};

int
COMPAT(data_init)(struct COMPAT(data) *data)
{
	...
}

こんなマクロ書いたりしますやね、これを展開すると

struct compat_data {
	...
};

int
compat_data_init(struct compat_data *data)
{
	...
}

となりますが、これをK&RとANSI/ISO-Cどちらでもプリプロセス可能にするため

#define COMPAT(name)	__CONCAT(compat_,name)

と書くわけです。

実際にコメント読んでみると__CONCAT()マクロの使用例として

 The __CONCAT macro is used to concatenate parts of symbol names, e.g.
 with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.

とあるので後方互換の為に古い実装を残す時なんかに使用することを想定していたっぽいですな。

__CONCAT()マクロの実装はANSI/ISO-Cの場合は

#define __CONCAT(x,y)   x ## y

です。

K&Rの場合は若干苦し紛れですが

#define __CONCAT(x,y)   x/**/y

となっています。

ですのでマクロの引数にスペース入れないでくれとコメントに書いてありますな。

The __CONCAT macro is a bit tricky -- make sure you don't put spaces in between its arguments.

@ __STRING() マクロ

これもANSI/ISO-Cで導入されたCプリプロセッサの「文字列化演算子(6.10.3.2 #演算子)」をラップ(Snoop Doggy Dog感)するものです。

よくある使い方としては、デバッグ文とかエラーログに変数名を埋め込むとかですな。

#define ASSERT_NOT_NULL(arg)				\
do {							\
	if (arg == NULL)				\
		errx("%s is null.", #arg);		\
} while (/*CONSTCOND*/0)

int
foo(const char arg1, const char *arg2)
{
	ASSERT_NOT_NULL(arg1);
	ASSERT_NOT_NULL(arg2);
}

これを__STRING()マクロに置き換えると

#define ASSERT_NOT_NULL(arg)				\
do {							\
	if (arg == NULL)				\
		errx("%s is null.", __STRING(arg));	\
} while (/*CONSTCOND*/0)

ってだけっす。

__STRING()マクロの実装はANSI/ISO-Cの場合は

#define __STRING(x)     #x

です。

K&Rの場合は単なるダブルクオートで

#define __STRING(x)     "x"

となっています。

@ __CONCAT(), __STRING() マクロの現状

これも所詮K&RとANSI/ISO-Cの双方向ソース互換の為のマクロですやね。前回までに説明したように*BSDではANSI/ISO-Cに対応しないコンパイラなんぞサポートしないことに決めて、__P()マクロやK&R式関数定義の仮引数定義を消して回ってます。

ですのでこいつらも同様に一気に消しちゃっても問題ないと思うんですが、なぜかそういう動きはないですな。不思議。

@ __BEGIN_DECLS, __END_DECLS マクロ

デフォルトコンパイラがgccになったのでC++にも対応したことで、標準Cライブラリのリンケージに関数宣言に

extern "C" {
...
};

なんかが必要になるのですが、当然こいつらCコンパイラは理解してくれません、ですので

#if defined(__cplusplus)
#define __BEGIN_DECLS   extern "C" {
#define __END_DECLS     };
#else
#define __BEGIN_DECLS
#define __END_DECLS
#endif

というマクロを導入しました。

まぁC++03では #include <stdio.h> → #include <cstdio>に改められて別ファイルになったわけですが、まぁ黒歴史ですな。 ただし現在でもC++98ソース互換を保たないと #include <stdio.h> する旧C++會もいるのでこれは消せません、ちーん。

んでこのマクロなんですが/usr/includeで公開するヘッダ以外で使う必要はほぼ無いです。 ですのでlibc内部で必要なだけのこういう ヘッダの関数宣言には使ってません。

ですが必要もないのに使っちゃってるとこもありますやね、 こことか。

src/lib/libc/citrus以下はは元々独立したライブラリにする予定だったので使ってますけどね。

@ const, inline, signed, volatile キーワード

こいつらもANSI/ISO-Cで導入されたキーワードなので、K&Rの場合はではざっくりマクロで消し去ります。

#ifdef __GNUC__
#define const           __const         /* GCC: ANSI C with -traditional */
#define inline          __inline
#define signed          __signed
#define volatile        __volatile

#else   /* !__GNUC__ */
#define const                           /* delete ANSI C keywords */
#define inline
#define signed
#define volatile
#endif  /* !__GNUC__ */

つーか4.4BSDの時点では対応してるコンパイラが無いので常に消してますな、gccだけは試験実装(__ではじまるキーワード)があるので それに置き換えています。

ちなみに現在では逆にgccの__ではじまるキーワードの方をANSI/ISO-Cのキーワードに置換してますな。

#define	__const		const		/* define reserved names to standard */
#define	__signed	signed
#define	__volatile	volatile
#if defined(__cplusplus) || defined(__PCC__)
#define	__inline	inline		/* convert to C++/C99 keyword */
#else
#if !defined(__GNUC__) && !defined(__lint__)
#define	__inline			/* delete GCC keyword */
#endif /* !__GNUC__  && !__lint__ */
#endif /* !__cplusplus */

__inlineだけはgccの場合そのまま喰わせてます。

@ 次回予告

おっしこれでNet/2での導入当初のsys/cdefs.hに実装されてたマクロの話がすべて終わったので、後は如何にその後の*BSDで「無秩序に」機能追加されたかの話だぜ!


[ホームへ] [ページトップへ]