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

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

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) */

のように宣言されていますので

の場合には

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が更新されなくなってるのは何故なんだぜ


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