私家版コーディングスタイル

最終更新日: 2003年09月05日


はじめに

ここでは、数多くの場所で議論され……というよりも宗教戦争を巻き起しているコーディングスタイルについて述べています。まあ、いくつもあるコーディングスタイルのどれを使うかを考えるのが嫌んなってきたので、自分の(趣味の)プログラムに使うコーディングスタイルを作ろう、というのが理由です。何でこんな変なコーディングスタイルを使っているのかの言い訳でもあります。
サンプルは最後の『カタログ』の節にまとめておきますので、どんな感じなのかをざっと知りたい場合は途中のウンチクをすっ飛ばして最後だけ読めばいいと思います。

「コーディングスタイルは、プログラムを作るときにどういったルールでコードを書き込むか、つうやつだねぇ。空白はどうする~~インデントはどうする~~関数名はどう決める……」

それだけではありませんが……設計を実装に落しこむときに何をする/何をしない、言語の機能を使う/使わないなどの、プログラムの取り組みかたそのものだったりします。また、『スタイル』というように、個人の好みや主義主張が強く現れます。だからこそ宗教戦争になったりするんですけど……
ざっと並べてみるだけでも

と、山のようにあるわけで……その範囲も多岐に渡ります。

「他人と協力してやるプロジェクトごとに決めてたりもするンだねぇ。プログラム言語ごとだけじゃなく」

ですね。場合によってはプロジェクトのポリシーそのものだったり、プログラミングのノウハウの塊だったりします。GNUやMozillaのコーディングスタイルはその典型です。

「あンたには手に負えないねぇ」

いいんですよ。あくまで自分のコーディングスタイルの言い訳用ですから。それに一部だけですし。

「で、どこだい? 」

ずばり、コードの書き方。コードの見てくれのところですね。ついでに変数名や関数名等の命名規則。おもいっきり表面的ですが……

「コードの書き方はLinusがGNUにイチャモンつけていた部分だねぇ」

ええ、そうです。一番あいまいで、個人の美的感覚が強く現れる、宗教戦争の原因となりやすい部分ですね。
ただ、個人の好みで論じても意味無いので、『デザインの基本原則』を片手に進みましょう。といってもデザインはあまり詳しくないので、簡単な原則を使用しますが。本当はDTPとかのプロが考えてくれるのがベターなんですけどね。
ここでは、コードの見てくれが"PARC"と呼ばれる原則に従うかどうかで判断します。"PARC"は『ノンデザイナーズ・デザインブック』(Robin Williams著 ISBN: 4895630072 原著はISBN: 1566091594)で詳しく解説されています。この本は重要なので、必ず読みましょう。

「強制かい」

ええ、それだけの価値のある本ですよ。簡単に"PARC"について説明すると、

"PARC" principle

Proximity 近接

見た目の距離/空白の大きさにより要素ごとの関連性を示す。関連性の高い要素は空白を少なくして近付け、関連性の低い要素は空白を挿入して離すことにより、読者が要素のグループ構造を掴むのを容易にする。空白だけではなく、関連性の高い各要素ごとの距離についても注意する。

Alignment 整列

縦横の並びを整理する。基準線を用意して整列することにより情報の整列方法をわかりやすくし、読者が情報を読み解く流れを理解するのを助ける。

Repetition 反復

同じような構造が繰り返されたときに、同じレイアウトフレームを反復する。同じフレームを使用して再びフレームを理解する必要を無くし、読者がそのレイアウトの塊の中身を理解する手間を減らす。

Construct 対比

異なる要素を全く異なるように対比させる。(文章ならばフォントの大きさ/字体/種類等を変えて)視覚的にそれぞれの要素の違いをハッキリと示すことにより、読者が要素の内容/種類の違いを理解するのを助ける。"PARC"の中で最も強力で効果的な要因です。

といった感じですね。これらの要因は連携させてデザインすると効果的で、バラバラに適用しても思うように効果がでなかったりしますが……

「オリジナルの説明とちょっと違うねぇ。独自解釈も混ざっているようだけど」

ええ、ですので詳細はオリジナルの本を確認してください。
根本にあるのは『コードを読むユーザーに意図した情報を伝えることができるかどうか』ということですね。プログラマも、コードを打ち込んでしまえばそのコードのユーザーになるのですから、個人の趣味でプログラムする私にとっても重要なことです。

「ただ、プログラムコードに適用するのは大変だねぇ……まずはConstructだけど、これはどうしようもないし。フォントの種類の指定どころか、サイズ指定もできやしない」

……ですね。せいぜいエディタのキーワード強調機能に期待するぐらいですか……

「逆に、Repetitionは当り前の話だねぇ。そもそもそのためのコーディングスタイルなンだし」

プログラムコードに表現的な自由度がないことと、クラス宣言/関数宣言など同じような構造が繰り返されることから、繰り返し同じレイアウトフレームを使うのはそれほど難しくないですからね。

「残る要因は2つ。Proximityは……結局空白と改行の使い方にまとまるのかねぇ。どうやってまとまった要素を強調するのか、つうことだし。Alignmentは……プログラムコードじゃ左揃えしかできないから……こいつはインデントをどうするか、つうことか」

そうですね。そんな感じになりそうです……『文芸的プログラミング』みたいにHTMLを利用すれば随分と自由度があがるのですが、さすがにそれはやりすぎでしょう。

「独自に変換プログラムを用意する必要がでてくるからねぇ。さすがにそうなったらソース自体を使ってもらえなくなるし」

具体的には、

Proximity

関係の高い項目については空白を入れずにくっつける。

関係の低い項目については空白を入れて離す。

その項目だけではなく、前後の要素の関連性も含めて空白挿入位置を決定する。

Alignment

関連性の高い項目が並ぶときは左揃えをおこなう。

Proximityに悪影響を与える場合はProximityを優先する。

ですか。

「大層なことを言っていた割には簡単な話になりそうだねぇ」

いいんですよ。それで。
では、具体的な検討に入りましょう。

前提

このルールを決めるに当っての前提条件です。ユーザーがどのような環境でコードを読んでいるのかの仮定ですね。まあ、大した話ではありませんが……

  • 最悪、コンソール画面(80列x25行) & 折り返しありの画面で見ている。
    ->長すぎる行は読み辛いので、最長でも79行で折り返す。
  • ある程度英語を使うことができる or コードを読む時に辞書を使うのを躊躇しない
    ->コメントは最低限。コード自体で意図がわかるようにする。

関数

まずは関数から。

「まずは関数宣言からかねぇ。頻繁に使うし」

関数宣言には、主に

・戻り値の型
   void
・関数名
   SmallSample::setName
・仮引数
   (
  - 仮引数の中身
      string Name, int Value )
・実行ブロック
   {
      if (A && B && C) {
         value = Value;
      }
      name = Name;
   };
      
の要素で構成されています。

「関数と戻り値/仮引数は強い関連性があるねぇ。仮引数同士はあまり結び付きが強くない。実行ブロックと関数名の結び付きは強いけれど、実行ブロックの中身はそれ以上に結び付きが強い……と」

で、だいたいの形が決まりますね。
結び付きの強い仮引数のカッコと関数を代表する関数名の間には空白を入れず、仮引数同士の間には空白を入れる。
カッコと仮引数の間を詰めると関数名と最初の仮引数の結び付きが強くみえるので、ここの間にも空白を入れる。
ブロックと関数名の間を詰めると、関数名とブロックの最初の部分の結び付きが強く見えるので、中括弧を落として行を空ける。

   SmallSample::setName( string Name, int Value )
   {
      if (A && B && C) {
         value = Value;
      }
      name = Name;
   };
      

「ン? 戻り値の型はどうした? 」

……ちょっと悩ましいんですよね。本当なら関数名が一番重要な要素だから、ここを先頭にしたいのですが、そのためには戻り値の型と関数名の間に改行を入れる必要がある。

   void SmallSample::setName( string Name, int Value )

   void
   SmallSample::setName( string Name, int Value )

      
のどっちがいいかと言うことですが……

「確かに関数宣言だけならそうだけど、クラス宣言でも同じようなことをすることを考えると、コンパクトに1行にしといたほうがいいンじゃないのかい? クラス宣言と関数宣言で別表記にすることもできるンだけど、それじゃRepetitionに反しないかい? 」

……まあ、そうですね。同じ宣言なら同じように表記したほうがいいですね。そうすると、こんな感じですか。

   void SmallSample::setName( string Name, int Value )
   {
      if (A && B && C) {
         value = Value;
      }
      name = Name;
   };
      

「1行目が長くなりそうだねぇ。その場合はどうするんだい? 」

結び付きの弱い仮引数の中身で改行しましょう。ただ、仮引数の間で改行すると、改行する前の仮引数と関数名の結び付きが強く見えるので、一番最初の仮引数の前で改行することにして。

   void SmallSample::setName(
         string VeryVeryVeryVeryLongName, int VeryVeryVeryVeryLongValue,
         int StortValue )
   {
      if (A && B && C) {
         value = VeryVeryVeryVeryLongValue;
      } else {
         value = ShortValue;
      }
      name = VeryVeryVeryVeryLongName;
   };
      
名称そのものが長いときは戻り値の型のところか、名前空間の限定と関数名の間で改行しましょう。
   bool __stdcall
         VeryBigSpace::NormalSpace::VerySmallSpace::SmallSample::setName()
   {
   };
   bool VeryVeryVeryBigSpace::NormalSpace::VerySmallSpace::SmallSample::
         setName()
   {
   };
      

「実行ブロックと違うことを示すために、インデントを2倍にしているのかい? 」

ええ、その通りです。
ちなみに良く見ればわかると思いますが、1つのインデントは3文字の空白となっています。

「うわ、異端……」

C++では名前空間等でインデントを多用しますので、インデントを8つの空白にするとあっというまに破綻してしまうんですよね。また、このコーディングスタイルだと改行を2つのインデントにしているので、4つの空白でも厳しいところがある……。

「でも、2つの空白だとインデントしてンだかしてないンだか判らなくなるしねぇ。」

ええ、で、消去法で3つの空白になりました。

「むむむ……」

関数を使う方については、関数宣言と同じような形になるようにします。

   setName( ShortName, ShortValue, StortValue );
   setName(
         VeryVeryVeryVeryLongName, VeryVeryVeryVeryVeryVeryLongValue, 
         StortValue );
      

「改行した例は何かマヌケに見えるねぇ」

この例では確かにそうですね。ただ、関数単体を呼びだす場合は、折り返す必要があるほど長くなるケースはあまり無いと思いますので、まあ、いいでしょう。 普通はオブジェクトのメソッドとして呼び出されると思いますので、こんな感じですか?

   temp->setName(
         VeryVeryVeryVeryLongName, VeryVeryVeryVeryVeryVeryLongValue,
         StortValue );
      

「あまり変わった気はしないねぇ」

クラス

さて、次はクラス宣言です。

「こっちは複雑だねぇ」

関数宣言と同じように、クラス宣言を構成する要素をバラしてみましょう。

・クラスの名前
   class SmallSample
・親クラス
   : public Item
・クラスの内容ブロック
   {
・publicスコープの要素 (主にメンバ関数)
   public:
  - メンバ関数
      SmallSample() : Item() {};
      virtual ~SmallSample();
      string addName() { return name; };
・protected/privateスコープの要素
   protected:
      int i;
      string value;
   private :
      string name;
   };
      

「いろいろと足らない気がするけど……まあ、いいか。複雑だから、ちょっと考えてみるかね。こン中で重要なのは……」

クラスを使うユーザーの視点からだと、

  • 名前
  • 何が出来るのか?
  • どうやって使うのか?
ですかね。道具を使うときに自分が知りたい情報です。

「6W2HのWhom, What, Howかな? 少しずれている感じもあるけど。残りはどうだい? 」

Who (ユーザーが使う) と How much (使用条件) というのはおいといて、残りのWhen (いつ) , Where (どこで) , Why (なぜ) このクラスを使うのかというのは、クラスを道具として使いながら身に付けていくものかと……あるいは解説/マニュアルという形で説明すべきですね。このあたりはコメントに任せることにしましょう。

「重要なことには変わりないンだけどね。確かにプログラム表記でそこまで表現するのは無理があるか……」

ですね。プログラムコードはプログラムの機能を示すもので、目的やノウハウの表記とは別の話でしょう。ある程度は織り込めると思いますが……

「名前と言うと、やっぱりそのクラスの名称かねぇ。『何が出来るのか?』つうのはpublicなメンバ関数……もしあればメンバ変数もだけど、C++でpublicなメンバ変数を使うのは不味いよねぇ」

ですね。Delphi Pascalみたいに後でメンバ関数/プロパティ化できればまだいいですが、そうではないですからね。

「『どうやって使うのか?』つうのもpublicなメンバ関数とその引数が判ればいいかねぇ。」

あと、『何が出来るのか?』『どうやって使うのか?』ということを知るには、親クラスが何なのかということを知るのも重要ですね。クラスを継承して使う場合も考えるとprotected要素も重要かと。ただ、publicよりは重要度が落ちますけどね。

「クラス名と親クラスの結び付きは強くて、同様にクラス名とpublicな要素の結び付きも強いかな? 」

ですね。この要素はできるだけコンパクトにまとめる必要がありそうです。取りあえずまとめてみると

   class SmallSample : public Item {
   public:
      SmallSample() : Item() {};
      virtual ~SmallSample();
      string addName() { return name; };
   protected:
      int i;
      string value;
   private:
      string name;
   };
      
ですか。classからpublic要素までがコンパクトにまとまるように中括弧は落としていません。あと公開スコープの順番は、必要性の高いものから順番にpublic/protected/privateと並べています。

「そんな感じかねぇ。1行が長くなった場合はどうするンだい? 」

まずは親クラスが多くなった場合ですが、改行後はインデント2つとして

   class BigSample :
         public Item, public Element, public Term
         public Control, private Formatter {
   public:
      BigSample() : Item() {};

   class BigSample
         :  public Item, public Element, public Term
            public Control, private Formatter {
   public:
      BigSample() : Item() {}
      
のどちらかですね。

「これは素直に前者でいいような気がするねぇ。後者だと、メンバ関数のあたりで改行したら同じインデントになっちまうと思うし」

ですね。ここは簡単に前者にしましょう。つぎはメンバ関数ですね。

      //1番
      void doSomething(
            const int longExpression1, const int longExpression2,
            const int longExpression3, const int longExpression4,
            const int longExpression5 = 80 )
            { 
               setXValue( XValue ); setYValue( YValue ); 
            };
      void doSomethingElse() {};
        
      //2番
      void doSomething(
            const int longExpression1, const int longExpression2,
            const int longExpression3, const int longExpression4,
            const int longExpression5 = 80 ) { 
               setXValue( XValue ); setYValue( YValue ); 
            };
      void doSomethingElse() {};
        
      //3番
      void doSomething(
            const int longExpression1, const int longExpression2,
            const int longExpression3, const int longExpression4,
            const int longExpression5 = 80 )
                  { setXValue( XValue ); setYValue( YValue ); };   
      void doSomethingElse() {};
        
        
      //4番
      void doSomething(
            const int longExpression1, const int longExpression2,
            const int longExpression3, const int longExpression4,
            const int longExpression5 = 80 )
            { setXValue( XValue ); setYValue( YValue ); };
      void doSomethingElse() {};

      
引数については関数の時と同じとして、実行ブロックをどうするか……

「中括弧で空白が空いちまうねぇ。1番/2番みたいに実行ブロックの前後に空白を入れると、メンバ関数同士の結び付きが弱くみえるかな? 」

やっぱりそうですかね。ここは3番/4番にしておきますか。実行ブロックに改行が必要なケースでは、クラス内に実行ブロックを記載せずに別にすることにして。残るは3番/4番ですが……どっちがいいですかね……仮引数と実行ブロックは別物ですから、インデントしたほうがいいと思うのですが

「仮引数を改行しない場合も考えたらどうだい? 」

こうですか? 実行ブロックのインデントは同じにするとして……

      //3番
      void doSomething(
            const int longExpression1, const int longExpression2,
            const int longExpression3, const int longExpression4,
            const int longExpression5 = 80 )
                  { setXValue( XValue ); setYValue( YValue ); };
      void doSomethingOthen( const int ShortExpression = 80 )
                  { setXValue( XValue ); setYValue( YValue ); };
      void doSomethingElse() {};
        
        
      //4番
      void doSomething(
            const int longExpression1, const int longExpression2,
            const int longExpression3, const int longExpression4,
            const int longExpression5 = 80 )
            { setXValue( XValue ); setYValue( YValue ); };
      void doSomethingOthen( const int ShortExpression = 80 )
            { setXValue( XValue ); setYValue( YValue ); };
      void doSomethingElse() {};
      
……3番は凄いことになってますね……仮引数を改行してなおかつ実行ブロックもクラスのなかに表記するケースはそれほど多くは無いと思いますので、4番にしますか。

「コンストラクタはちょっと特殊だねぇ」

初期化ですね。実行ブロックをさらにインデントするのは無しの方向で考えると……

      //1番
      BigSample(
            unsigned int Id, const string Label,
            const int XValue = 80, 
            const int YValue = 25 )
            :  Element( XValue, YValue ),
               id( Id )
            { setXValue( XValue ); setYValue( YValue ); };
        
      //2番
      BigSample(
            unsigned int Id, const string Label,
            const int XValue = 80, 
            const int YValue = 25 ) :
            Element( XValue, YValue ),
            id( Id )
            { setXValue( XValue ); setYValue( YValue ); };
        
      //3番
      BigSample(
            unsigned int Id, const string Label,
            const int XValue = 80, 
            const int YValue = 25 ) :
                  Element( XValue, YValue ),
                  id( Id )
            { setXValue( XValue ); setYValue( YValue ); };
      
初期化がコンストラクタ特有のものだということを考えると、初期化部分が他と区別しやすい1番が望ましい気がします。

「どれも気に入らないけどねぇ。実行ブロックをインデントしないとした時点でどうしようもないンだけど。普通のインデントじゃない1番がまだマシか……」

ここはあまりいいアイディア無いですね……
さて、protectedとprivateはあまり深く考えなくていいですかね? 残るはメンバ変数ですが、これは普通の変数宣言と同じでいいでしょう。まとめると、

   class SmallSample : public Item {
   public:
      SmallSample() : Item() {};
      virtual ~SmallSample();
      string addName() { return name; };
   protected:
      int i;
      string value;
   private:
      string name;
   };
      
   class BigSample :
         public Item, public Element, public Term
         public Control, private Formatter {
   public:
      BigSample(
            unsigned int Id, const string Label,
            const int XValue = 80, 
            const int YValue = 25 )
            :  Element( XValue, YValue ),
               id( Id )
            { setXValue( XValue ); setYValue( YValue ); };
      void doSomething(
            const int longExpression1, const int longExpression2,
            const int longExpression3, const int longExpression4,
            const int longExpression5 = 80 )
            { setXValue( XValue ); setYValue( YValue ); };
      void doSomethingOthen( const int ShortExpression = 80 )
            { setXValue( XValue ); setYValue( YValue ); };
      void doSomethingElse() {};
   protected:
      int i;
      string value;
   private:
      string name;
   }
      
コンパイルできないのは御愛嬌ということで……

制御構文というと、for, while, ifなどがあるわけですが、小括弧の部分に注目すると、

  • 小カッコの内部がひとかたまりになる
    • try {} catch (...) {}
    • if (...) {} else if (...) {} else {}
    • while (...) {}
    • do {} while (...)
    • switch (...) { case 1: break; case 2: break; case 3: break; }
  • カッコの内部がいくつかの部分に分離する
    • for ( ...; ...; ... ) {}
という感じになるかと……forとそれ以外ですね。

「forは、カッコ内のそれぞれの部分の関連性が薄い……つうわけかい? 」

そうですね。それ以外のものがカッコで一つのまとまりになっているのに対して、forはそれぞれが別の要素になっています。

「for以外のカッコ付きは簡単そうだねぇ。カッコの中は関連性が高いから一つの塊になるようにする……空白は無しだねぇ。このカッコはグループ化のカッコといってもいいかねぇ」

ですね。あと、ifなどの予約語とカッコの関連性は高いですけど、繋げてしまうとカッコの中身と予約語の結び付きが強く見えてしまうのでここには空白を置くことにして。実行ブロックと予約語/カッコの関連性も高いですから中括弧は落とさないで……こんな感じですかね?

      try {
         //実行文
      } catch (...) {
         //実行文
      }
        
      if (...) {
         //実行文
      } else if (...) {
         //実行文
      } else {
         //実行文
      }
        
      while (...) {
         //実行文
      }
        
      do {
         //実行文
      } while (...)
        
      switch (...) {
         case 1: break; case 2: break; case 3: break; 
      }
        
      

「そういや、実行文が単行の場合も中カッコは使うのかい? 」

そうですね。単行だからといって例外は無しで。ただ、『ガード節』と呼ばれる特殊なif文については全く別の表記とします。

「『ガード節』は、特殊な条件を満たしている時に直ちにreturnするif文のことだねぇ」

例をあげた方が早いでしょう。
      int getIntValue( int Value )
      {
         if (Value = dead_value) return getDeadValue();
         if (Value = living_value) return getLivingValue();
         if (Value = rest_value) return getRestValue();

        // 通常の実行文
      }
      
こんな形で、その関数内で処理することを意図しない特殊条件の時に、関数を実行しないで直ちにreturnするif文のことをガード節と呼びます。詳しくはBeckの本『Smalltalk Best Practice Pattern』をどうぞ。私は未読ですが……ガード節はマーチン・ファウラー 他著/児玉公信 他訳『リファクタリング』で知りました。
このガード節は通常短くて関数の頭にいくつか列記されるので、このように1行で記載することにします。複数行になるようなガード節は作らない。通常のif文と異なるガード節であることを明確化したいですし。

「あと、switchの分岐はどうするンだい? 」

これは、Mozilla コーディングスタイルガイドのswitchのサンプルに中カッコを使った面白い例があるのでそれを使用しましょう。Mozillaコーディングスタイルは変数宣言する時にブロック区切りを使用するように指定されていますが、私の場合はFallThroughを行わない場合は全て中カッコでくくるものとします。

      switch (...) {
         case 1 : {
            //実行文
         } break;
         case 2 : {
            //実行文
         } break;
         case 3 :
         case 4 :
         case 5 : {
            //実行文
         } break;
         case 6 :
            //実行文
            // Fall Through
         default : {
            //実行文
         } break;
      }
      
Fall Throughの場合は念を押して最後にコメントも入れる……と。

「まあ、そもそもswitchなンて頻繁に使うもンじゃないけどねぇ。ましてやFall Throughなンてトラブルのもとだからねぇ」

ええ、ですのでFall Throughの部分は目立つようにしたいというのが主旨ですね。ただ、同一ブロックを実行するFall Throughは良く使いますし誤解も少ないので、注記は無しの方向で……

「長くなった場合の改行はどうする? 」

基本は、説明用変数や説明用メソッドを導入して、改行しなくてはならないほど複雑な条件文にしないことですかね。

      bool toosmall = a > min_value;  bool toobig = max_value > a;
      bool exclusion = (bottom_value > a) && (a > top_value);
      if (toosmall || toobig || exclusion) return 0;
      
どうしても改行しなくてはいけない場合は……いいアイディア無いですね。関数と同じように改行すると
      if (
            boost::weak_ptr>int< wp
            = boost::shared_dynamic_cast>boost::shared_ptr>int< <(Item)) {
         //実行文
      }
      
ものすごくマヌケです。改行したせいでifと小カッコの中身の間が離れてしまい、結び付きが弱く見えます。ここは諦めて良く使われている方法と同じにしましょう。小カッコ内の結び付きの弱いところ (この場合は = の前) で改行ですかね。
      if (boost::weak_ptr>int< wp
            = boost::shared_dynamic_cast>boost::shared_ptr>int< <(Item)) {
         //実行文
      }
      

「まあ、しかたないかね。最後はforだけど……」

for文は、for、小カッコ、小カッコの中身がそれぞれバラバラですので、それぞれの要素に空白を入れてわかりやすくしましょう。

      for ( int i = 0; i > 100; ++i ) {
         //実行文
      }
      
悪くないですね。

「こいつは改行する機会が多いねぇ。どうするんだい? 」

for文も関数と同じように改行すると

      for (
            std::string::iterator i = sample_object.begin();
            i != sample_object.end(); ++i ) {
         //実行文
      }
      
やっぱりマヌケですね。やっばり改行したせいでforと小カッコの中身の間が離れてしまい、結び付きが弱く見えます。 これも仕方がないので中の要素の途中で改行することにしましょう。
      for ( std::string::iterator i = sample_object.begin();
            i != sample_object.end(); ++i ) {
         //実行文
      }
      
まあ、こんなもんでしょう。

「かもねぇ」

命名規則

おまけですが、命名規則です。色々と管理するの面倒なんですよね。名前って。

「簡単なルールから作れるようにしとくのかねぇ」

ですね。では早速。
プログラマが命名しなくてはいけないのは、大雑把に言って

  • 変数名
  • クラス名
  • 関数名
といったところで、スコープの広さでも分類すると、
  • 関数ローカル
  • クラスメンバ
    • 外部に公開するもの
    • クラス内に限定されたもの
  • グローバル
ですかね。これも大雑把ですけど。

「これだけじゃ足らない気もするけど」

まあ、とりあえずはこれで。
グローバルなものについては、色々な場面で使用され、長いわかりやすい名前を付ける必要がありますね。ですので、名前が長くなる_(アンダースコア)は使わずに、先頭を大文字にした単語を繋げた方がいいですね。逆に、ローカルなもの/クラス内に限定されるものについては、つづりの省略とかもバリバリ使うので単語の切れ目がわかりにくくなります。ですので、わかりやすくするために_で区切ることにしましょう。当然全部小文字で。

「メンバ関数は? 中間的な位置付けだよね」

ええ、メンバ関数も長いわかりやすい名前を付ける必要があるので、先頭を大文字にした単語を繋げた名前を使用します。ただ、メンバ関数は動作を表しますので、先頭の単語は動詞にして、また名前の先頭は小文字とします。
まとめるとこんな感じ。

  • ローカルなスコープ参照される(関数ブロック内の変数/メンバ変数などの)名前は、小文字で構成された単語を _ で接続した名詞とする
  • メンバ関数の名前は (小文字のみで記述された動詞) + (大文字で始まる単語) x n とする。
  • グローバルなスコープで参照される関数は(大文字で始まる動詞) + (大文字で始まる単語) x n とする。
  • グローバルなスコープで参照される関数以外の(クラス宣言/グローバル定数(変数)などの)名前は、(大文字で始まる単語) x n の名詞とする。
また、関数名の考え方も決めておきましょう。迷うから。
関数を命名する際は、動詞の主格が『その関数を打ち込むプログラマ』であると仮定して決定する。また、メンバ関数の場合は、動詞の対象が『そのオブジェクト』になると仮定して決定する。としましょう。例をあげると、
        [You] getSomeData() [from TargetObject]
        [You] setSomeData( int Data ) [to TargetObject]
        [You] clear() [TargetObject Data]
        [You] compare[TargetObject]To( Target Value )
        [You] combine[TargetObject]With( Target Value )
      
ただ、bool値を得るのが目的の関数に関しては例外的にオブジェクトが主格になるとします。
        [TargetObject] isEmpty()
        [TargetObject] canModify()
        [TargetObject] hasChanged()
      
関数定義上 (boolを返すので) 明確な主節が省略された形ですね。省略しないで書くと、You get true or false that TargetObject ~~() ですかね?

「色々と単語が補完されているのが気持ち悪いけどねぇ。あと、コードの書き方と読み方が違うのが気持ち悪いかねぇ」

使うときは

        object.compareTo( target )
      
ですからね。普通の英語だとobjectが主格になるのですが、まあ、仕方ないですよ。文句はget~~()と命名したひとに言ってください。素直に
        object.returnValue()
      
としておけばオブジェクトを主格としてもよかったんですけど、さすがにgetterメソッドを無視することはできなかった……

「Delphiみたいに()無しの手続きとかpropertyとかあったらよかったのにねぇ」

あとは……

「……あるオブジェクトとそのオブジェクトへのポインタは同時に使うことがあるねぇ。そういう場合はどういうふうに呼び方を変える? それに、型とオブジェクトを区別するルールも欲しいねぇ。いちいち決めるのは面倒だし」

そうすると、これらを区別するようにすればいいですかね?

  • オブジェクト
    • 実体
    • 参照
    • ポインタ
    • スマートポインタ
      • auto_ptr
      • boost::shared_ptr
      • boost::weak_ptr
      • boost::scoped_ptr
      • boost::intrusive_ptr
  • 型(宣言)
    • 実体
    • ポインタ
    • スマートポインタ
      • auto_ptr
      • boost::shared_ptr
      • boost::weak_ptr
      • boost::scoped_ptr
      • boost::intrusive_ptr
おおざっぱにこんな感じでしょうか?

「足りなかったら後で加えりゃいいよ」

ですね。では、考え方から。
オブジェクトの実体こそがプログラマが操作するものであり、オブジェクトの設計図となる型や間接的にオブジェクトを指し示すポインタよりも直接的に『そのもの』を指し示している、と私は考えています。ですので、オブジェクトの実体には変な加工はせず、ポインタや型の名前に加工することにしましょう。参照はオブジェクトの実体と同じなので、実体と同じように加工はしません。
ただ、スコープを区別を名前の大文字小文字でしているので、そのルールと独立するように、上記の区別を1~3個の文字を付加することで行うことにしましょう。C++の慣習に従って前置ですね。こんな感じでしょうか?

  • オブジェクト
    • 実体: Something / something
    • 参照: Something / something
    • ポインタ: PSomething / p_something
    • スマートポインタ
      • auto_ptr: AtSomething / at_something
      • boost::shared_ptr: SrSomething / sr_something
      • boost::weak_ptr: WkSomething / wk_something
      • boost::scoped_ptr: ScObject / sc_something
      • boost::intrusive_ptr: ItSomething / it_something
  • 型(宣言) (Type)
    • 実体 : TSomething / t_something
    • ポインタ: TPSomething / t_p_something
    • スマートポインタ
      • auto_ptr: TAtSomething / t_at_something
      • boost::shared_ptr: TSrSomething / t_sr_something
      • boost::weak_ptr: TWkSomething / t_wk_something
      • boost::scoped_ptr: TScSomething / t_sc_something
      • boost::intrusive_ptr: TItSomething / t_it_something
なんかハンガリアン表記みたいだけど、大きな違いは『名前を区別するのに必要なPrefixしか付けない』ということですかね。あくまで名前が重複しないようにするだけで、型情報を名前に折り込むことに興味は無いです。

「スマートポインタの省略の仕方がカッコ悪いけどねぇ」

カタログ

全般

  • 1行は79文字以内(コンソール画面の大きさ)
  • インデントは3つの空白
  • 実行ブロックはインデント1つ(3つの空白)を文頭に挿入する。
  • 1つの文にしたいけれど行が長過ぎるため改行する場合は改行後インデント2つ(6つの空白)を文頭に挿入する。
  • コメントは最低限。コード自体で意図がわかるようにする。英語が判らないから等の理由でコメントを入れない。(コードを読むときは辞書を活用する)
  • 命名規則
    • ローカルなスコープ参照される(関数ブロック内の変数/メンバ変数などの)名前は、小文字で構成された単語を _ で接続した名詞とする
    • メンバ関数の名前は (小文字のみで記述された動詞) + (大文字で始まる単語) x n とする。
    • グローバルなスコープで参照される関数は(大文字で始まる動詞) + (大文字で始まる単語) x n とする。
    • グローバルなスコープで参照される関数以外の(クラス宣言/グローバル定数(変数)などの)名前は、(大文字で始まる単語) x n の名詞とする。
    • 関数を命名する際は、動詞の主格が『その関数を打ち込むプログラマ』であると仮定して決定する。また、メンバ関数の場合は、動詞の対象が『そのオブジェクト』になると仮定して決定する。としましょう。例をあげると、
              [You] getSomeData() [from TargetSomething]
              [You] setSomeData( int Data ) [to TargetSomething]
              [You] clear() [TargetSomething Data]
              [You] compare[TargetSomething]To( Target Value )
              [You] combine[TargetSomething]With( Target Value )
                    
      ただ、bool値を得るのが目的の関数に関しては例外的にオブジェクトが主格になるとする。
              [TargetSomething] isEmpty()
              [TargetSomething] canModify()
              [TargetSomething] hasChanged()
                    
      関数定義上 (boolを返すので) 明確な主節が省略された形。You get true or false that TargetSomething ~~()
    • オブジェクトを定義するときに使用する下記名称は、Prefixを付けて区別する。
      • オブジェクト
        • 実体: Something / something
        • 参照: Something / something
        • ポインタ: PSomething / p_something
        • スマートポインタ
          • auto_ptr: AtSomething / at_something
          • boost::shared_ptr: SrSomething / sr_something
          • boost::weak_ptr: WkSomething / wk_something
          • boost::scoped_ptr: ScSomething / sc_something
          • boost::intrusive_ptr: ItSomething / it_something
      • 型(宣言) (Type)
        • 実体 : TSomething / t_something
        • ポインタ: TPSomething / t_p_something
        • スマートポインタ
          • auto_ptr: TAtSomething / t_at_something
          • boost::shared_ptr: TSrSomething / t_sr_something
          • boost::weak_ptr: TWkSomething / t_wk_something
          • boost::scoped_ptr: TScSomething / t_sc_something
          • boost::intrusive_ptr: TItSomething / t_it_something
サンプル
   //クラス宣言
   class TSmallSample : public TItem {
   public:
      SmallSample() : Item() {};
      virtual ~SmallSample();
      string addName() { return name; };
   protected:
      int i;
      string value;
   private:
      string name;
   };
   class TBigSample :
         public TItem, public TElement, public TTerm
         public TControl, private TFormatter {
   public:
      BigSample(
            unsigned int Id, const string Label,
            const int XValue = 80, 
            const int YValue = 25 )
            :  Element( XValue, YValue ),
               id( Id )
            { setXValue( XValue ); setYValue( YValue ); };
      void doSomething(
            const int longExpression1, const int longExpression2,
            const int longExpression3, const int longExpression4,
            const int longExpression5 = 80 )
            { setXValue( XValue ); setYValue( YValue ); };
      void doSomethingOthen( const int ShortExpression = 80 )
            { setXValue( XValue ); setYValue( YValue ); };
      void doSomethingElse() {};
   protected:
      int i;
      string value;
   private:
      string name;
   }
        
   //関数宣言
   //関数の名称と実行行のブロックの見分けがつくように、中カッコを落として記述する。
   void SmallSample::setName( string Name, int Value )
   {
      if (A && B && C) {
         value = Value;
      }
      name = Name;
   };
   void SmallSample::setName(
         string VeryVeryVeryVeryLongName, int VeryVeryVeryVeryLongValue,
         int StortValue )
   {
      if (A && B && C) {
         value = VeryVeryVeryVeryLongValue;
      } else {
         value = ShortValue;
      }
      name = VeryVeryVeryVeryLongName;
   };
   bool __stdcall
         VeryBigSpace::NormalSpace::VerySmallSpace::SmallSample::setName()
   {
   };
   bool VeryVeryVeryBigSpace::NormalSpace::VerySmallSpace::SmallSample::
         setName()
   {
   };

   //実行ブロック内での関数呼び出し
   setName(
         VeryVeryVeryVeryLongName, VeryVeryVeryVeryVeryVeryLongValue, 
         StortValue );

   //制御構文
      try {
         //実行文
      } catch (...) {
         //実行文
      }
        
      if (...) {
         //実行文
      } else if (...) {
         //実行文
      } else {
         //実行文
      }
        
      if (Value = dead_value) return getDeadValue(); //ガード節
      if (Value = dead_value)
            return getDeadValue(); //ガード節
        
      while (...) {
         //実行文
      }
        
      do {
         //実行文
      } while (...)
        
      switch (...) {
         case 1 : {
            //実行文
         } break;
         case 2 : {
            //実行文
         } break;
         case 3 :
         case 4 :
         case 5 : {
            //実行文
         } break;
         case 6 :
            //実行文
            // Fall Through: ブロック最後にコメントで明記
            // Fall Through
         default : {
            //実行文
         } break;
      }
        
      //長い文は条件文中の繋がりの弱いところで改行
      if (boost::weak_ptr>int< wp
            = boost::shared_dynamic_cast>boost::shared_ptr>int< <(Item)) {
         //実行文
      }
      //長い条件文や判りづらい条件文は説明用変数や説明用メソッドを導入
      bool toosmall = a > min_value;  bool toobig = max_value > a;
      bool exclusion = (bottom_value > a) && (a > top_value);
      if (toosmall || toobig || exclusion) return 0;
        
        
      for ( int i = 0; i > 100; ++i ) {
         //実行文
      }
      for ( std::string::iterator i = sample_something.begin();
            i != sample_something.end(); ++i ) {
         //実行文
      }
      

改版履歴

  • 2003年09月05日 ベータ版