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で「無秩序に」機能追加されたかの話だぜ!