Cの異なる行に関数タイプとメソッド名を配置する理由


16

私は会社に入社したばかりで、最初のコードレビューでのスタイルコメントの1つは、戻り値の型とメソッド名を別の行にする必要があるというものでした。たとえば、これ

void foo() {
}

これであるべき

void
foo() {
}

私は常に最初のスタイルを使用してきましたが、人々が2番目のスタイルを使用するのは個人的な好み以外の理由があるのではないかと考えていましたか?最初のものは読みやすさを害するとは思わない。Cプログラマーや大規模なオープンソースプロジェクトでは、一方が他方よりも一般的ですか?


3
これはGNU標準にあります(必ずしもそれが良いことであるとは限りませんが、ブレースのスタイルは奇妙です)。gnu.org/prep/standards/standards.html#Formatting
ゴーティエ

回答:


19

人々が第二のスタイルを使用する理由が個人的な好み以外にあるのではないかと考えていましたか?

それはCの初期の頃に人気があったスタイルなので、その理由は、それが彼らが非常に長い間それをやった方法、彼らがそのように見える多くのコードを持っているからかもしれません。誰もが慣れています。企業の勢いほど個人的な好みではありません。

別の理由は、関数名は常に最初の列から始まることです。戻り値の型は長さが異なり、やや複雑になる可能性があります。型を独自の行に置くと、関数名を見つけやすくなります。

会社にスタイルが設定されている場合は、コーディング標準ドキュメントも用意されている場合があります。それを求めます。この選択の理由を説明している可能性があり、コピーがあると、今後のレビューで同様の問題を回避するのに役立ちます。


2
多くのプログラマーによってまだ使用され、防御されている80文字の行制限にも関係していると思います。80文字の制限を持ち、説明的なメソッド/関数名を使用する場合、いくつかの妥協が必要です。メソッドヘッダーの分割もその1つです。
サルタン

宣言はより長くすることもできます-呼び出し規約識別子(stdcallなど)、シンボルの可視性に関する情報(DLLEXPORT)または__attributes など、異なる(非標準の)修飾子について考えてください。また、C ++を見ると、比較的複雑な戻り値の型を持つことができるため、どこかに改行を追加する必要があるかもしれません。
ヨハネス

@Sulthan興味深いのは、プログラマーだけではありません(ターミナルウィンドウの使用とターミナルエディターの使用)。組版では、一般に、読みやすさの観点から、72〜80文字がテキストの列の理想的な幅と見なされます。(したがって、TeXとその派生物が、一部の人が奇妙に狭い列と考えるものにデフォルト設定する理由です。)
JAB

1
@JAB実際に私は今。また、正当化されたテキストの読み取りとソースコードの読み取りは2つの非常に異なるものであり、共通点はほとんどないため、理想的な幅は無関係です。
スルタン

1
「もう1つの理由は、関数名は常に最初の列から始まるため、見つけやすいということです。」そして、これは単なる視覚的な利点では^start_of_a_long_funcありません。正規表現を検索して、検索している関数にすぐに移動できます。
underscore_d

7

これは私が実際に読みやすさに顕著な影響を与えることがわかった唯一のコードフォーマットルールであり、ほとんど労力はかかりません(コードエディタがあなたとの戦いを始めないと仮定します)。

宣言/定義の中で一貫した位置に名前を表示することは、プログラミング言語の設計として適切です。理論的根拠は簡単です。名前の始まりをすぐに見つけるために使用できる素敵な視覚的アンカー(中括弧またはぶら下がったインデント)があります。ファイルをスキャンして名前を見つけるときに、実際に言語を解析する必要はありません。

文書をフォーマットするときと同じです。新しいセクションを開始するときは、名前を太字で(多くの場合、独自の行に)付けます。

初期のCには非常に簡潔な署名がありました。戻り値の型はオプションであり、引数の型は署名の後に宣言されていました。名前も非常に短い傾向がありました。これにより、名前を相殺する不定期の戻り値型の影響が軽減されました。

double dot(x, y);

まだかなり消化可能です。

C ++はこれを少し悪化させました。引数タイプの仕様を署名に移動し、署名を長くしました。この構文は、後にCの標準化中に採用されました。

static struct origin *find_origin(struct scoreboard *sb,
                  struct commit *parent,
                  struct origin *origin)

消化性は劣りますが、それほど悪くはありません。(Gitからの抜粋)

ここで、長くて説明的な名前とパラメーター化された型を使用した最新のプログラミング手法を検討し、この選択がどのように悲惨なものになっているかを見てみましょう。Boostヘッダーの例:

template <class A1, class A2, class A3, class A4, class A5, class A6>
inline typename normalise<policy<>, A1, A2, A3, A4, A5, A6>::type make_policy(const A1&, const A2&, const A3&, const A4&, const A5&, const A6&)
{ 
   typedef typename normalise<policy<>, A1, A2, A3, A4, A5, A6>::type result_type;
   return result_type(); 
}

汎用コードを作成している場合、そのような署名は普通ではありません。一生懸命努力することなく、これよりもはるかに悪い事例の例を見つけることができます。

C、C ++、およびそれらの派生物であるJavaおよびC#は、読み取り可能な宣言/定義を持つ例外のようです。人気の高い前任者と同業者(Fortran、ALGOL、Pascal)は結果タイプの前に名前を付け、ありがたいことに、後継者(Go、Scala、TypeScript、Swiftなど)の多くはより読みやすい構文も選択しました。


1
Fortranプログラムを書く必要がないことに感謝してください。関数の引数を個別に宣言すると、やらなければならない場合、すぐに気がかりになります。特に、関数のシグネチャを読み取って期待される引数を理解しようとすると、C ++は型をそれらが属する場所に配置します。Fortranは、変数名の視覚的なキーワード検索を開始してその型を決定します。
cmaster-モニカの復元

2
関数宣言に型を入れることは、実際にC ++によって考案されましたか?それが事実だということを聞いたことがなく、引用を見つけるのに苦労しています。
underscore_d

1
C言語の開発を参照してください。標準化のセクションで、リッチーは構文がC ++から借用されたことに言及しています。
user2313838

5

私はこのスタイルに最初に出会ったのは、C&C ++で働いて19年目でした。誰かがこの邪悪なものをどのように発明できるかについて、かなり困惑していました。

私が見つけることができる唯一の(潜在的に)肯定的な点は、grep ^ FuncNameを使用して関数定義を見つけることができるということです。コミュニティを嫌う実際のツールでは、10年以上前に関連する要因になる可能性があります...私が見たところでは、この属性さえも殺すC ++とクラスメンバーの機能に適用されました。

私の意見を推測します。:)


1
grep -P '^(\w+::)?FuncName'
カイルストランド

ええ、関数名のクラス型はこれの使用をまったく妨げません。そのため、ソースファイルをすばやくナビゲートするのに依然として有用です。見た目が悪かどうか、意見は意見です。
underscore_d
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.