英語の鍛錬のためにstyle(9)を自力で英訳してみた。
FreeBSD カーネル開発者用マニュアル
#NAME#
style -- カーネルソースファイル書式ガイド
#DESCRIPTION#
このファイルでは、FreeBSDソース群の中でもカーネルソースファイルの推奨される
書式を規定している。またこれは、ユーザー向けのプログラムのコードの書式
ガイドでもある。多くの書式規則が例文とともに示されている。その書式が
重要な点について記していないと思う前によく例をチェックされい。
/*
* FreeBSDの書式ガイド。CSRGのカーネル標準書式(KNF)に基づいた。
* @(#)style 1.14 (Barkeley) 4/28/95
* $FreeBSD release/9.1.0/share/man.style.9 217807 2011-01-07 08:34:12Z trasz $
*/
/*
* "とても"重要な1行のコメントは、この様に書く。
*/
/*
* 複数行のコメントは、この様に書く。普通の文章と同じように。普通の文章の
* 段落分けと同じように記述する。
*/
著作権の表示は、複数行であるべきだ。一行目は、アスタリスクの後にダッシュを続け
次の様に書く。
/*-
* Copyright (c) 1982-2025 John Q. Public
* All rights reserved.
*
* 長く退屈なライセンスは、ここに書く。しかし、完結かつ短く。
*/
スクリプトは、ソースツリーから"/*-"から始まるコメントの全てをライセンス情報として収集する。
もし、indent(1)の1文字目から始まるライセンスや著作権表示ではないコメントを再整形しないフラグ
を立てることを望む場合は、そのコメントのダッシュをアスタリスクに変更せよ。1行目以外の
コメントは、ライセンス文として考慮されない。
著作権ヘッダーの後に、空行を入れる。そして、C/C++以外のソースファイルのための$FreeBSD$。
バージョンコントロールIDタグ一つのファイルに1つのみ含まれるべきである(これとは違い)。
C/C++以外のソースファイルは、上記の例の様にあるべきである。C/C++のソースファイルにおいては、
以下の1つに準拠する。ファイルの全てのVCS(Version control system) の版は、
その他のファイルでは、メンテナンスされ、含まれ、適用可能で、複数のIDがファイルの履歴を
示す。総じて、外部のIDとインフラを編集すべきではない。ラップされることなしに("#if
defined(#LIBC_SOCS)")、非互換のビットを隠し、IDをオブジェクト
ファイルに含ませないために"#if 0... #nedif"で囲む。ファイル名が変更された
場合、外部のVCSIDの直前にのみ"From: "を追加する。
#if 0
#ifndef lint
static char sccsid[] = "@(#)style 1.14 (Barkeley) 4/28/95":
#endif /* not lint */
#endif
#include <sys/cdefs.h>
__FBSID("#FreeBSD: release/9.1.0/share/man/man9/style.9 217087 2011-01-07 08:34:12Z trasz $"):
ヘッダーファイルの前のその他の空行は、そのままにしておく。
通常は、カーネルのインクルードファイル(例: sys/*.h)が最初に来る。
<sys/types.hと<sys/param.h>の両方ではなく、どちらか一方をインクルードする。
<sys/types.h>は<sys/coefs.h>インクルードする。そして、<sys/types.h>は、
<sys/coefs.h>に依存して問題ない。
#include <sys/types.h> /* ローカルではないインクルードは、<>で囲む */
ネットワークプログラムにおいては、次にネットワーク関係のインクルードファイルを次に書く。
#include <net/if.h>
#include <net/if_dl.H>
#include <net/route.h>
#include <netinet/in.h>
#include <[protocolos/wrhod.h>
カーネルのソースに/usr/includeにあるファイルを使ってはいけない。
次のグループの前に空行を置く。/usr/includeにあるファイルは、アルファベット順で
ソートされていなければいけない。
#include <stdio.h>
グローバルなパスは、<paths.h>で定義されている。プログラム固有のローカルパスは、
ローカルの"pathnames.h"に記述する。
#include <paths.h>
ユーザー固有のインクルードファイルの前に空行を置く。
#include "pathnames.h" /* ローカルのインクルードファイルは、ダブルクオートでくくる*/
実装名前空間でアプリケーションインターフェースを実装する場合を除いて#define したり名前
を宣言してはいけない。
"unsafe"マクロの名前(副作用を伴う)と内容が明白なマクロの名前は、全て大文字にする。
語句のようなマクロの拡張は、一つのトークンか括弧の外を持つ。#defineと
マクロ名の間には、タブ一つを置く。もしマクロがインライン関数の拡張ならば、関数名は、全て小文字
にする。読みやすくするためにバックスラッシュを右寄せにする。もし、マクロが複合ステートメント
ならば、それをdoループで囲む。そうすればif文の中で安全に使うことができる。文章の終端の
セミコロンは、華奢なプリンターやエディタのパースを簡単にするべくマクロよりむしろ
マクロの呼び出しによっておかれるべきだ。
#define MACRO(x, y) do { \
variable = (x) + (y); \
(y) += 2; \
} while(0)
コードが#ifdefや#ifを使い条件的にコンパイルされる時、マッチする#endifや
#elseに続き、読み手が容易に条件付きコンパイルのコード部分の終わりが
分かるようにコメントを追加するかもしれない。そのコメントは、
(主観的に)当該コード部分が長いと思われる場合、20行を越える場合、#ifdef
のネストが読み手を混乱させる可能性がある場合にのみ用いられるべきである。
例外は、lint(1)の目的のために条件付きコンパイルされない複数のケースのために作られるかもしれ
ない。これは、コンパイルされるコードが少ない場合においてさえそうである。コメントは、
1つのスペースで#endifや#elseと離すべきである。短い条件付きコンパイル部分に対しては、
近接したコメントは、つけるべきではない。
#endifへのコメントは、対応する#if、#fidefと表現を一致させるべきである。#else,#elif
へのコメントは、先行する#if、#elif文と逆の表現とすべきである。コメントにおける、
"defined(FOO)"のような準表現は、"FOO"と略される。コメントの目的は、"#ifndef FOO"は、
"#if !defined(FOO)"として扱われる。
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#ifdef COMPAT_43
/*A large region here, or other condional code. */
#else /* !COMPAT_43 */
/* Or here. */
#endif /* COMPAT_43 */
#ifndef COMPAT_43
/* Yet another large region here, or other conditional code. */
#else /* COMPAT_43 */
/* Or here */
#endif /* !COMPAT_43 */
プロジェクトは、古いBSDスタイルのu_intxx_t式の整数識別子よりむしろISOIEC 9899:1999
("ISO C99") uintxx_t式の符号無し整数の識別子を使うべくゆっくり動いている。新しい
コードは、後者を使うべきである。そして、もし、他の主要な作業が終わり格別の理由がなければ
そのエリアにある古い様式を、新しい様式に変換すべきである。何もないコミットの様に、ケアは、uintxx_tのみのコミットにすべきである。
列挙型の値は、全て大文字である。
enum enumtype {ONE, TWO} et;
識別子における内側の_アンダースコアの使用は、cammelCaseやTitleCaseにもまして
好まれる。
宣言部においては、トークンが型に関連した識別子である場合を除いて隣接したトークンと
アスタリスクの間にスペースを開けてはいけない。(それら識別子は、基底型の名前、修飾子、
独立して宣言されるものと言うよりむしろtypedefで定義されるものである。)
それら識別子は、スペース一つでアスタリスクと分けなさい。
構造体のメンバを宣言する時、使用する順番、サイズ(大きいものから小さいもの)
、アルファベット順でソートして宣言しなさい。最初のカテゴリは、通常適用されない。
しかし、例外がある。1つのメンバーは、1行で宣言しなさい。可読性を向上させるため
メンバー名をあなたの裁量で1つまたは2つのタブで整列するよう努めなさい。一つのタブは、
少なくともメンバーの90%が揃う場合にのみ使用するべきである。とても長い型に続く
名前は、スペース1つで分離すべきである。
主要な構造体は、構造体が使われるファイルの先頭で宣言するすべきである。
また、構造体が複数のソースで使われる場合は、ヘッダーファイルに分離すべきである。
構造体の使用部と宣言部は、分離すべきであり、ヘッダーファイルで宣言されている
場合は、externすべきである。
struct foo {
struct foo *next; /* List of active foo */
struct mumblue amumble; /* mumble のコメント */
int bar; /* コメントを揃えるように */
strcut verylongtypename *baz /* 2つのタブで揃わない場合 */
};
struct foo *foohead; /* グローバルなfooリストの先頭 */
可能な時は常に、自分でリストを回すのではなくてquese(3)マクロを用いよ。
従って、前述の例は、次の様に書くのが好ましい。
#include <sys/queue.h>
struct foo {
LIST_ENTRY(foo) link; /* fooリストには、queueマクロを用いる */
struct mumble amumble; /* mumbleのコメント */
int bar; /* コメントを揃えるように。 */
struct verylongtypename *baz; /* 2つのタブで揃わない場合 */
};
LIST_HEAD(, foo) foohead; /* グローバルなfooリストの先頭 */
構造体の型にtypedefsを用いるのは、避けなさい。Typedefsは、元の型を適切に
隠さないので、問題を抱えがちである。例えば、typedefが構造体自身もしくは構造体への
ポインターかどうかを知る必要がある。加えてそれらは、正確に1回宣言しなければいけない。
ところが、不完全な構造体の型は、不必要であるにもかかわらず何回も記述することができる。
Typedefsは、独立したヘッダーファイルで用いいるのが難しい。typedefを定義するヘッダーは、
用いられるヘッダーより以前に、用いられるヘッダによって(名前空間の汚染をもたらす)インクルード
されなければいけない。また、それらは、typedefを含むことに対するバックドアでなければいけない。
慣習がtypedefを必要とするとき、その名前は、構造体のタグ名と一致させるべきである。Standard C
もしくはPOSIXで指定されている場合を除いてtypedefの最後を"_t"とするのは避けなさい。
/* 構造体の名前は、タグ名と一緒にせよ */
typedefi struct bar {
int level;
} BAR;
typedef int foo; /* これは、foo */
typedef const long baz; /* これは、baz */
全ての関数は、どこかでプロトタイプ宣言されている。
(ほかの場所から使われない)プライベートな関数のプロトタイプ宣言は、ソースコードの
一番最初に持っていくべきである。ソースコード内のみで使われる関数は、staticと
宣言すべきである。
カーネルの別の部分から使われる関数は、関連のあるインクルードファイルでプロトタイプ宣言される。
関数のプロトタイプ宣言は、論理的順序で列挙するべきである。他の順番を使う
明白な理由がない場合は、なるべく、アルファベット順を使うべきである。
一つ以上のモジュールから使用されるローカルな関数は、"extern.h"のような別のヘッダーファイルに
含めるべきである。
__Pマクロを用いるべからず。
総じて、50%以上ファイルが影響を受けるコードは、"新しいコード"と認識される。これは、
既存のコードの慣例を崩すのに十分である。現在のstyleガイドラインに従いなさい。
カーネルは、パラメータの型にに関連付けられた名前を持つ。以下カーネルでの使用例。
void function(int fd);
ヘッダーファイルにおいては、ユーザーランドのアプリケーションに対して見えるようにせよ。
見えるプロトタイプ宣言は、(アンダーバーから始まる)"protected"な名前か名前のない型のどちらか
を用いなければいけない。purotectedな名前を使う方が好まれる。
void function(int);
か
void function(int _fd);
プロトタイプ宣言は、関数の名前を揃えるためにタブの後に多くのスペースを含むかもしれない。
static char *function(int _argc, const char *_arg2, struct foo *_arg3,
struct bar *_arg4);
static void *usage(void);
/*
* 全ての主要なルーチンには、何をするかということを簡単に説明したコメントをつけるべきである。
* mainルーチンの前のコメントは、このプログラムが何をするかを説明すべきである。
*/
int
main(int argc, char *argv[])
{
char *ep;
long num;
int ch;
一貫性のために、オプションをパースするときは、getopt(3)を用いるべきである。
オプションは、switch文の一連の流れの一部ではなく、getopt(3)の呼び出しとswitch文の中で
ソートされるできである。switch文の中の要素であるcascadeには、FALLTHROUGHコメントを
つけるべきである。数の引数に対しては、精度をチェックすべきである。明白な理由に到達不可能な
コードは、/* NOTREACHED */とマークされるかもしれない。
while((ch = getopt(arc, argv, "abNn:")) != -1)
switch (ch) { /* switch文をインデントせよ */
case 'a': /* case文はインデントしない */
aflag = 1; /* case文本体は、1つのタブでインデント */
/* FALLTHROUGH */
case 'b':
bflag = 1;
break;
case 'N':
Nflag = 1l
break;
case 'n':
num = strtol(optarg, &ep, 10);
if (num<= 0 || *ep != '\0') {
warnx("illegal number, -n argument -- %s",
optarg);
usage();
}
break;
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
(if, while, for, return, switch)の後には、スペースをいれよ。ステートメントが
一行以上になる許される場合の除いてゼロもしくは1ステートメントと共にブレース("{""}")を
ステートメントを制御するために用いてはいけない。無限ループは、whileではなく、forを用いよ。
for (p = buf; *p != '\0'; ++p)
/* nothing */
for (; :)
stmt;
for (; :) {
z = a + really + long + statement + that + needs +
two + lines + gets + indented + for + spaces +
on + the + second + and + subsequent + lines;
}
for (; ;) {
if (cond)
stmt;
}
if (val != NULL)
val = realloc(val, newsize);
forループの一部は、空になるかもしれない。ルーチンが特殊で複雑な場合を除いてブロック内で
宣言をしてはいけない。
for (; cnt < 15l cnt++) {
stmt1;
stmt2;
}
インデントは、8文字のタブにすること。2段目のインデントは、4つのスペースにすること。もし、
長いステートメントとなる場合は、演算子を行の最後に書くこと。
while (cnt < 20 && this_variable_name_is_too_long &&
ep != NULL)
z = a + really + long + statement + that + needs +
two + lines + gets + indented + four + spaces +
on + the + second + and + subsequent + lines;
行末に空白を入れては、いけない。タブは、空白の後ろにインデントを揃えるためにのみ使いなさい。
タブよりも多くのスペースを使ってはいけない。タブの前にスペースを入れてはいけない。
ブロックの始まりと終わりのブレースは、elseと同じ行に書きなさい。不必要なブレースは、
いれない。
if (test)
stmt;
else if (bar) {
stmt;
} else
stmt;
関数名の後にスペースを入れない。コンマの後には、スペースを入れよ。'('や'["の後に
スペースを入れない。')'や']'の前にスペースを入れない。
error = function(a1, a2);
if (error != 0)
exit(error);
単項演算子にスペースは、必要ない。二項演算子には、スペースが必要である。優先させる必要がある
場合や複雑なステートメントの場合を除いて()を使ってはいけない。他人は、自分より
混乱しやすいかもしれないということを覚えておこう。下記のコードが理解できるか?
a = b->c[0] + ~b == (e || f) || g && h ? i : j >>1;
k = !(l & FLAGS);
処理に成功した場合は、0を返し、失敗した場合は、1を返せ。
exit(0); /*
* "処理成功時は、0を返す"の様な
* 明白なコメントは、避けよ。
*/
}
関数の返り値の型は、"function"の前の行に書きなさい。関数の中身を書くための右ブレースは、
1行に書きなさい。
satic char *
function(int a1, int a2, float fl, int a4)
{
関数内で変数を宣言する時、サイズ順->アルファベット順で宣言しなさい。複数の変数を
1行でまとめて宣言するのは、OK。もし、一つの型の変数が1行に収まらない時は、複数行に
分けて、型名を行頭に書きなさい。宣言部で変数を初期化することによってコードが
分かりにくくなることのない様に気を付けなさい。このことについては、あまりシビアに
ならなくても良い。初期化部において関数の呼び出しをしてはいけない。
struct foo one, *two;
double three;
int *four, five;
char *six, seven, eight, nine, ten, elven, twelve;
for = myfunction();
関数の中で関数を宣言しては、いけない。ANSI Cにおいては、そのような
不注意な入れ子の宣言は、ファイルスコープを持つ。ローカルスコープに現れる
ファイルスコープは、思わぬ不平を良いコンパイラから引き出す。
キャストとsizeofの後にスペースを入れては、いけない。ちなみに、indent(1)は、
このルールに依らない。sizeofは、丸括弧とともにかかれる。sizeof(var)インスタンスに
冗長な丸括弧のルールを適用しては、いけない。
NULLは、空のポインタ定数を好む。アサイメント等コンパイラが型を知っているコンテキストにおい
ては、(type *)0や(type *)NULLの代わりにNULLを使え。その他のコンテキスト、特に全ての関数の
引数においては、(type *)NULLを使え。(もし、関数のプロトタイプ宣言がスコープにない場合、キャ
ストは、可変長引数においては必要であるが、その他の引数に対しては、不必要である。)
ポインタのNULLチェックの例
(p=f()) == NULL
悪い例:
!(p = f())
論理型でないにもかかわらず、チェックに!を使うな。次の様にすべきである。
IF (*p == '\0')
悪い例:
if (!*p)
void *を返すルーチンは、返り値を勝手にキャストして返してはいけない。
return文における値は、丸括弧で囲むべきである。
err(3)やwarn(3)を使え。自分で実装するな。
if ((four = malloc(sizeof(struct foo))) === NULL)
err(1, (char *)NULL);
if ((six = (int *)overflow()) == NULL)
errx(1, "number overflowwed")'
return (eight);
}
この様な古い様式の関数宣言。
static char *
function(a1, a2, f1, a4)
int a1, a2; /* 整数型の宣言。これらをデフォルトにしてはいけない。*/
float f1; /* floatとdoubleのプロトタイプの違いに注意せよ。 */
int a4; /* 順番が宣言されたリスト */
{
明白にK&Rとの互換性が必要な場合を除いて、ANSIの関数宣言を使いなさい。長いパラメータの
リストは、4つの普通のスペースでインデントしなさい。
可変長の引数は、次の様にすべきである。
#include <stdarg.h>
void
vaf(const char *fmt, ...)
{
vs_list ap;
va_start(ap, fmt);
STUFF;
vs_end(ap);
/* void型を返す関数においては、return文は不要である */
}
static void
usage()
{
/* 関数に、ローカル変数がない場合は、空行を入れる。 */
どんなにそれが、早くきれいだとしても、おろかなバグに気を使わなくても良いように
fputs(3),puts(3),putchar(3)ではなくて、printf(3)を使いなさい。
Usage文は、マニュアルページのSYNOPSISのようにあるべきである。そして、
下記順番になっているべきである。
1. オペラントの必要ないオプションを一番最初仁1組のラケット"[]"の内側にアルファベット順
で書く。
2. オペランドが必要なオプションを次に、同じくアルファベット順でブラケットの内側に書く。
3. 必要な引数があれば、次にコマンドラインで指定すべき順序で書く。
4. 最後に、残りの任意の引数をブラケットの内側に同じくコマンドラインで指定すべき
順序で列挙する。
バーは、"どちらか一方の"オプション、引数を分離する。共に指定すべき複数のオプション、引数は、
一組のブラケットの内側に書く。
"usage: f [-aDde] [-b b_arg] -m m_arg] req1 req2 [opt1 [opt2]]\n"
"usage: f [-a | -b] [-c [-dEe] [-n number]]\n"
(void)fprintf(stderr, "usage" f [ab]\n");
exit(1);
}
マニュアルのオプションの説明は、純粋にアルファベット順であるべきだ。これは、オプションが
引数を取る/取らないにかかわらない。また、大文字小文字の順で示すべきだ。
現在、カーネルコードは、styleガイドに従うべきである。サードパーティによるモジュールや
ディバイスドライバのガイドラインは、より緩和的である。しかし、最低限内面的に
それらのstyleと矛盾しないようにすべきである。
様式的な変化(空白の変化も含め)は、ソースリポジトリにとって重大であり、良い理由を除いて
避けなければいけない。リポジトリにおけるFreeBSD KNF style規則のコードは、コンプライアンス
から逸脱してはいけない。可能な時は、いつでもコードをコードチェッカー(lint(1)やgcc -Wall
にかけ、警告を最小限に減らすべきである。
#SEE ALSO#
indent(1), lint(1), err(3), warn(3), style.Makefile(5)
#HISTORY#
このマニュアルページのほとんどは、4.4BSD-Lite2 release の src/admin/style/styleの
ファイルに基づき、日々の更新は、現在のFreeBSDプロジェクトの要望と実践に反映されている。
src/admin/style/style は、Ken ThompsonとDenis RitchieのAT&T UNIX Version 6に
おけるプログラミング様式のCSRGである。
0 件のコメント:
コメントを投稿