2013/11/12(Tue)
○[Unix C][*BSD][な闇無用] sys/cdefs.hとは何ですか? (その2)
その1においてはsys/cdefs.hという謎のヘッダファイルが生まれた背景を説明しましたが、今回は実装について説明します。
@__P() マクロ (*BSD/glibc2/newlib)
ANSI-C89/ISO-C90においてヘッダファイル等における関数宣言は
#include <stdio.h>
#include <stdlib.h>
/* 関数プロトタイプ宣言 */
void foo(char *);
int
main(void)
{
foo("hello, world.");
exit(EXIT_SUCCESS);
}
void
foo(char *msg)
{
puts(msg);
}
のように引数リストの型(名前は省略可)を明記します、これを関数プロトタイプといいます。
ところがK&Rの時代にはこのプロトタイプの機能はまだ存在せず、関数宣言では引数リストは省略します。
#include <stdio.h>
/* 関数宣言 */
void foo();
main()
{
foo("hello, world.");
}
void
foo(msg)
char *msg;
{
puts(msg);
}
プロトタイプが必要になった理由は、コンパイラーの引数型チェック強化によるエラー検出などですが、ここでは省略。
@__P()マクロの使い方
この差異を埋めるため、__P()マクロは使用されます。
#include <stdio.h>
/* 関数プロトタイプ宣言 */
void foo __P((char *));
main(void)
{
foo("hello, world.");
}
void
foo(msg)
char *msg;
{
puts(msg);
}
以上のように、引数リストを__P()マクロで囲みます。
sys/cdefs.hでは__Pマクロは
#if defined(__STDC__) || defined(__cplusplus)
#define __P(protos) protos /* full-blown ANSI C */
...
#else /* !(__STDC__ || __cplusplus) */
#define __P(protos) () /* traditional C preprocessor */
...
#endif /* !(__STDC__ || __cplusplus) */
のように宣言されていますので
- 事前定義マクロ__STDC__が定義されている = ANSI-C89/ISO-C90準拠のコンパイラである
- 事前定義マクロ__cplusplusが定義されている = C++コンパイラである
の場合には
void foo __P((char *));
はCプリプロセッサによって
void foo(char *);
のように関数プロトタイプ宣言として展開されますし、旧来のK&Rなコンパイラであれば
void foo();
として関数宣言になります、事前定義マクロとはなんぞという人は 過去回読んでどうぞ。
その1でsys/cdefs.hの目的として挙げた「コードを書き直さず」は達成できませんでしたが、このマクロの黒魔術によってK&RとANSI-CどっちのCコンパイラでもこれでビルド可能になったわけです。
@__P()マクロの現状
そもそもK&R文法しか受け付けないコンパイラは、わざわざひっぱり出してでもこない限り無い(断言)ので、NetBSDではobsoleteとしてみなされます。 よって 見次殺。過去のcommit mailを「 de-__P」で検索すれば大量に見つかると思います。
FreeBSDやOpenBSDも同様つーかNより3年くらい先行してやってます、げに恐るべきはNetBSD時間。
@EXFUN() マクロ (gcc/newlib)
オマケネタ。
このマクロはsys/cdefs.hのものではないのですが、 gccにはほぼ同目的のK&RとANSI-Cのポータビリティレイヤである ansidecl.hというものが存在します。
またansidecl.hとほぼ同等のものがnewlibにも _ansi.hとして存在したり、こっちは_EXFUN()のようにアンスコがprefixに付きますが。
EXFUN()は先ほど説明した__P()と同じ目的で導入されたマクロです。
#if defined (__STDC__) || defined(__cplusplus) || ...
...
#define EXFUN(name, proto) name proto
...
#else /* Not ANSI C. */
...
#define EXFUN(name, proto) name()
...
#endif /* ANSI C. */
こちらについては、obsoleteというわけではないようでガチガチに使われています。
私もかつてCitrusのwcs系関数を Cygwinのえらい人(定冠詞)に マージして頂いた時にもガッツリ_ansi.h使って書き直したり。style(9)は大事だからしょうがないね。
@次回予告
今回書ききれなかったのでもうちょいK&RとANSI/ISO-Cの関数定義の違いを吸収するマクロの話をします。
○ テスト
一時壊れたRSS吐いてたのを直したらfeedlyが更新されなくなってるのは何故なんだぜ