nakaoka
1
Choreonoidのソースコードのコーディング規約について、ちゃんとした形でまとめる前に、まずこちらでざっと書いておこうと思います。
私自身はChoreonoid本体のソースコードは以下のような規約で記述しており、実際のソースコードも(外部機関が開発した部分などを除いて)概ねこの方針に従っていると思います。
- クラス名はアッパーキャメルケースで定義
- 関数名、メンバ関数名はロワーキャメルケースで定義
- 標準ライブラリを拡張したようなクラスや関数では、標準ライブラリとの親和性を考慮して、(標準ライブラリと同様に)スネークケースにしている部分もある。
- ファイル名は含まれるクラスと同様のキャメルケースの名前とする
- ヘッダファイルの拡張子は.h、実装ファイルの拡張子は.cppとする
- 本体に含まれるクラスや関数は名前空間cnoid内で定義する
- ヘッダファイルでは名前空間は省略せずに記述する(using宣言などによって省略しないようにする)
- 実装ファイル(.cppのファイル)では名前空間の省略を使ってもよい
- ヘッダファイルでは他のヘッダのインクルードは最小限におさえる(コンパイル時の依存を減らすため)。ヘッダファイル中ではクラスのポインタや参照のみで記述できる場合は、対応するヘッダをインクルードしなくても、宣言だけ書いておけば、コンパイルは通すことができる
- クラスの定義が複雑になったり依存が増える場合は、pimplイディオムを導入して、ヘッダからは他のヘッダへの依存を極力減らすようにする。ただしパフォーマンスに影響がでる部分は無理にpimplに入れずに、インライン関数などでアクセスできるようにする。(ただしpimplのポインタ名はimplとしている。文字数を減らしたくて、pがなくてimplだけでも見たら分かると思ったので。)
- インクルードガードはマクロ定義で行い、マクロの名前はCNOID_モジュール名_ファイル名ベース_Hとする(ただマクロでインクルードガードをするのは古くて他によいやり方があるようでしたら教えてください)
- virtual関数をオーバーライドしているところはvirtualとoverrideの両方のキーワードをつけてわかりやすくする
- クラスのAPIでconstをつけられるところはなるべくつけるようにする
- コメントは英語のみとする
- 関数やクラスに説明をつける場合はDoxygen形式のコメントで記述する
- C++11以上の規格に則った記述とする
- C++14までの仕様は使ってもよいものとする。C++17については使ってもよいが、C++14でもビルドできるように記述する
- ヌルポインタは0でなくnullptrを使うようにする
- Choreonoid組み込みのReferenced型を継承してref_ptrのスマートポインタで使う場合は、クラス名Ptrの型名でスマートポインタをtypedefしておく
- shared_ptrなどの標準のスマートポインタを使う場合は、なるべくtypedefせずに明示的に書くようにする
- boostは今後使わないようにする。C++17でboostの標準的なクラスがほぼ標準ライブラリに入ったので、C++17だったらこれでも概ね問題ない。ただしC++14の場合は一部boostを併用したい部分もある。filesystem、optional、variantなど。これらについてはinclude/cnoid/stdx以下にboostとC++1xでどちらか使える方を使うようにしたヘッダを定義しているので、それを使う。他にも必要なクラスがあればこの方法で対応する。
- コーディングスタイルとしては、インデントなどは当方はemacsのccモードを使っていて、概ねそれに整形をまかせている
ざっと思いつく部分を書いてみました。整理されておらずすみません。抜けなどがあればご指摘ください。このトピックでの議論をふまえて、今後正式なものをまとめたいと思います。
3 Likes
nakaoka
2
以下追加です。
- メンバへのアクセス関数はgetterはメンバ名そのまま、setterはsetXXXとする。例えば string nameというメンバに対して、getterはconst string& name()、setterはvoid setName(const string& name) といった定義にする。ただしgetterはメンバ名と名前が同じになってしまうので、メンバの方にアンダーバーのサフィックスをつけておく。nameならname_とする。(ちなみにメンバの定義がpimplクラスにある場合は、名前がかぶらなくなるので、サフィックスもつけなくてよくなる。)
ちなみにChoreonoidのコーディング規約はQtのものをかなり参考にしています。概ねQtと同じようなスタイルにしていますので、Qtの経験がある方でしたらそう言った方が分かりやすいかもしれません。
そうましたら,こちらとの差分/追加分をまとめるのが良いかもしれません
nakaoka
4
情報ありがとうございます。
Qtのこんなページがあるとは知りませんでした。
ざっとみたところ、概ねこれでよいように思いました。
ただ、「キーワードの後と波括弧の前には空白を 1 つ入れる」とかは、けっこう細かいですね。(個人的には空白を入れない方が好み。)
あとポインタの*や参照の&を変数側につけるようになっていますが、型の方につけるほうが一般的な気もしますね。
まあ人それぞれ好みもいろいろですよね…。
ともかくこのQtの記述をベースに加筆修正してまとめるというのは確かによい方法だと思いました。そのようにしてまとめたいと思います。
ひとまずQtのスタイルガイドへ中岡様のリストをマージしてみました.
前半はQtのガイドで,差分を太字または(加筆)で示してあります.
C++のバージョンや,boostの取り扱いについては,統一してしまっても良いかもしれません.
規約案
インデント
- インデントは 4 つの空白である
- タブではなく空白を使用する
変数の宣言
- 変数の宣言は 1 行に 1 つのみ
- 一文字の変数名は、カウンタや一時変数で使用目的が明白である場合に限り認められる
内部で数値計算を含むため要検討
- 本当に必要になるまで変数の宣言はしない
命名規則 (加筆)
- 出来る限り短い変数名は避ける(“a”, “rbarr”, “nughdeget” など)
- クラス名はアッパーキャメルケースで定義
- 関数名、メンバ関数名はロワーキャメルケースで定義
※ 標準ライブラリを拡張したようなクラスや関数では、標準ライブラリとの親和性を考慮して、(標準ライブラリと同様に)スネークケースにしている部分もある。
- メンバへのアクセス関数はgetterはメンバ名そのまま、setterはsetXXXとする。例えば string nameというメンバに対して、getterはconst string& name()、setterはvoid setName(const string& name) といった定義にする。ただしgetterはメンバ名と名前が同じになってしまうので、メンバの方にアンダーバーのサフィックスをつけておく。nameならname_とする。(ちなみにメンバの定義がpimplクラスにある場合は、名前がかぶらなくなるので、サフィックスもつけなくてよくなる。)
名前空間 (加筆)
- 本体に含まれるクラスや関数は名前空間cnoid内で定義する
ヘッダファイル内
- 名前空間は省略せずに記述する(using宣言などによって省略しないようにする)
実装ファイル内
空白
- 処理を適切にまとめるために空白行を使う
- 空白行を 1 行以上続けない
- キーワードの後と波括弧の前には空白を 1 つ入れる
- ポインタや参照においては、型名と * または & の間には空白を入れる。変数名と * または & の間には空白は入れない。
- ビット演算子の前後には空白をいれる
- キャストの後には空白を入れない
- C スタイルのキャストは可能な限り避ける
波括弧
- 基本的に、左波括弧はステートメントの開始行に置く
例外:関数の実装とクラスの宣言では左括弧は常に新しい行の初めに置く
- 条件文では、内部の処理が 1 行を超える場合、または 1 行の処理が複雑な場合のみ、波括弧を使用する
- 例外1:条件式が複数にまたがる場合は波括弧を使用する
- 例外2:if-else 式のいずれかの処理が複数行にまたがる場合、全てに波括弧を使用する
- 条件文の内部が空の場合は波括弧を使用する
丸括弧
switch文
- case は switch と同じインデントで記述する
- 全ての case は break (または return) で終了しなければならない。意図的に break しない場合はその旨をコメントで記述する。複数の case を続けて記述する場合は例外。
改行
- 1つの行は 100 文字以内に収める。必要であれば改行を挿入する。
- 複数行に分割する場合、カンマは行の最後に置き、演算子は行の最初に置く。エディタの幅が小さい場合、行の最後にある演算子は見逃しやすい。
継承と virtual キーワード
- virtual関数をオーバーライドしているところはvirtualとoverrideの両方のキーワードをつけてわかりやすくする
以下,追加分
ファイル命名規則
- ファイル名は含まれるクラスと同様のキャメルケースの名前とする
- ヘッダファイルの拡張子は.h、実装ファイルの拡張子は.cppとする
ヘッダの実装
- ヘッダファイルでは他のヘッダのインクルードは最小限におさえる(コンパイル時の依存を減らすため)。ヘッダファイル中ではクラスのポインタや参照のみで記述できる場合は、対応するヘッダをインクルードしなくても、宣言だけ書いておけば、コンパイルは通すことができる
- クラスの定義が複雑になったり依存が増える場合は、pimplイディオムを導入して、ヘッダからは他のヘッダへの依存を極力減らすようにする。ただしパフォーマンスに影響がでる部分は無理にpimplに入れずに、インライン関数などでアクセスできるようにする。
ただしpimplのポインタ名はimplとしている。文字数を減らしたくて、pがなくてimplだけでも見たら分かると思ったので。
- インクルードガードはマクロ定義で行い、マクロの名前はCNOID_モジュール名_ファイル名ベース_Hとする(ただマクロでインクルードガードをするのは古くて他によいやり方があるようでしたら教えてください)
APIの呼び出し
- クラスのAPIでconstをつけられるところはなるべくつけるようにする
コメント
- コメントは英語のみとする
- 関数やクラスに説明をつける場合はDoxygen形式のコメントで記述する
その他
- ヌルポインタは0でなくnullptrを使うようにする
- Choreonoid組み込みのReferenced型を継承してref_ptrのスマートポインタで使う場合は、クラス名Ptrの型名でスマートポインタをtypedefしておく
- shared_ptrなどの標準のスマートポインタを使う場合は、なるべくtypedefせずに明示的に書くようにする
バージョン
- C++11以上の規格に則った記述とする
- C++14までの仕様は使ってもよいものとする。C++17については使ってもよいが、C++14でもビルドできるように記述する
- boostは今後使わないようにする。C++17でboostの標準的なクラスがほぼ標準ライブラリに入ったので、C++17だったらこれでも概ね問題ない。ただしC++14の場合は一部boostを併用したい部分もある。filesystem、optional、variantなど。これらについてはinclude/cnoid/stdx以下にboostとC++1xでどちらか使える方を使うようにしたヘッダを定義しているので、それを使う。他にも必要なクラスがあればこの方法で対応する。
細かいですが,統一はしておいて損はないと思います.
こちらのスレッドでROS関連の規約も貼ってありますので,もしお時間ありましたらご覧ください.
既に中岡さんがご自身のルールに従って多くのコードを書かれていますから,Qtの規約に従う必要もないかと思いますので,お好きなように変更されていいと思います.
(ただし,規約を策定するのであれば,曖昧にするよりは決めてしまったほうがいいと思います^^; )
1 Like
@nakaoka
この手の話にはclang-formatというツールが便利で,PR投げてみました.
基本的にはここに書かれていることが反映されていると思います.
(設定の量が量なので,見落としがある可能性は否定できません……)
まずはお手元で試してみてください.
ありがとうございます、とりあえずPRをマージしておきました。
今後活用していきたいと思います。