コードベースの全体的な構造を理解する良い方法は何ですか?


8

他の誰かがオープンソースコードを変更したり、独自のアプリケーション用に特定のものを開発する方法を見つけたりすると、私の仕事で役立つことがあります。ただし、すべてのソフトウェアに適切なドキュメントがあるわけではありません。

コードベースの全体的な構造を理解する良い方法は何ですか?

たとえば、どのルーチンがどのルーチンを呼び出すかなどです。この目的のためにDoxygenなどのドキュメントツールを自分で使用する可能性がありますが、もっと良い戦略があるかどうか疑問に思っていましたか?


5
これは妥当な質問だと思いますが、おそらくプログラマー .SEに尋ねるべきだとも思います。厳密には計算科学ではなく、より概念的なプログラミングです。
Geoff Oxberry、2011

2
これを行うための科学的コードに固有のテクニックはありますか?
David Ketcheson、

Doxygenを使用するのは、ルーチンがどのように接続されているかを示す簡単でグラフ付きの素敵な写真を提供できるからです。すべてをラップするメインルーチンを作成して、そのような全体像を把握することが必要になる場合があることに注意してください。
Allan P. Engsig-Karup、2011

@DavidKetcheson:これを科学的コードに固有に行う方法はないと思います。文書化されていない研究コードの強い伝統を考えると、おそらくあるはずです。
Geoff Oxberry、2011

回答:


9

次のスレッドは接線方向に関連しています。

論文の最初の部分では、18か月かけて文書化されていないFortranコードを変更しました。最初のタスクの1つは、コードベースの全体的な構造を理解することでした。私が提案する最も重要なことは、何かを理解するときはいつでもテキストファイルにメモをとることです。この時間のかかるイライラするプロセスの間に、物事を再学習したり再発見したりする必要はありません。

私の場合、前のプログラマーがFortran 77のようなスタイルを使用していたため、関数の引数が自己文書化されていなかったため、言うべき「API」はありませんでした。彼らは意味した。テストはなく、Fortranなのでヘッダーもありませんでした。ミックスにさらに楽しみを加えると、CまたはC ++で書かれた関数がいくつかあちこちにありました。

私にとってうまくいったこと(あなたがLinuxで働いていると仮定して):

  • grep。愛することを学ぶgrep。シェルでこれを使用して、関数が宣言されて呼び出されている場所を見つけます。出力から、どのファイルを調べるかがわかります。
  • お気に入りのコードエディタまたはIDEの「検索」コマンド。調べるファイルがわかったら、「find」コマンドを使用すると、関数呼び出しを探す時間を節約できます。
  • コードベースを変更できる場合は、積極的なリファクタリング。変数名を自己文書化したので、すでにわかっていることを再学習するために精神的な労力を費やす必要がありませんでした。また、コードの設計を合理化して、わかりやすくしたので、混乱が少なくなりました。1000行のメインプログラムはもう必要ありません。
  • 積極的なコメント。繰り返しになりますが、コードベースを変更できる場合は、コメントにコメントを付けて、理解したことを理解してください。私はDoxygenを使用しませんでしたが、Doxygenはこれに適しています。
  • nm。ライブラリのソースコードがない場合にライブラリで役立ちますが、発生した関数がそのライブラリにあるかどうかを知りたい場合に使用します。ただし、ライブラリのシンボルが削除されていない場合にのみ機能します。
  • デバッガーでコードをステップ実行します。printステートメントを使用するよりもはるかに効率的です。dddそしてgdb偉大であり、そこに事実上すべてのLinuxシステム上で。お好きなデバッガをご利用ください。
  • 開発者にバグを報告します。このオプションは、対象を絞った質問に最適です。(私がしたように)彼らに行って、「ここで何が起こっているのか理解していない」と言うと、彼らはあなたに同情して一般的に詳細に説明しようとするかもしれませんが、それは長期的には。開発者がドキュメントを書いたり、コードベースの構造を書いたりする際に開発者があなたのためにそれをしなかったので、あなたは脚の仕事をしなければならないでしょう。開発者は、あなたが本当に小さなことにこだわっているとき(彼らが何をしたかを覚えている場合)に本当に良いです。

私が以前に考えたかったこと、または私にとっての選択肢ではなかったこと:

  • ドキシゲン。Doxyfileオプションを調整すると、特別なDoxygenコメント構文がなくても、Doxygenが多くのドキュメントを自動的に生成します。私が遭遇した後のプロジェクトでこれを使用しましたが、それは信じられないほど役に立ちました。
  • ユニットテスト。コードベースを変更でき、それが何をすべきかについてある程度の考えがある場合は、さまざまな関数の単体テストを記述してください。(それは関係なくピックアップするのに便利なスキルです。)
  • C / C ++を使用している場合は、ヘッダーを確認してください。
  • サンプルプログラムを記述します。そのFortranプロジェクトでは私には選択肢がありませんが、サードパーティのAPIを選択するのに役立ちました。また、もしあれば、彼らのサンプルプログラムを見てください。
  • 使用する例や実行可能ファイルがある場合は、gcovおよびlcovを使用して、コードの一般的な実行のカバレッジ分析を行います。コードベースの大部分を実行することになっている例がある場合、これらの2つのツールを組み合わせると、コードの各行にアクセスした回数がわかります。デバッグフラグが有効になっている場合に最も役立ちます。コードの一部にまったくアクセスしない場合は、すぐに理解することはおそらくそれほど重要ではありません。コードの一部が頻繁にアクセスされる場合、それが何であるかを理解することはおそらく価値があります。多分それは重要でないループであるのでコードはたくさん実行されます、または他の多くの関数が依存する重要な関数であるかもしれません。カバレッジ分析だけではわかりませんが、カバレッジ分析は、時間をどこに集中するかについてのアイデアを提供します。
  • のような静的コード分析ツールsplintは、一部の変数が決して使用されないなど、コードで何か怪しいことが起こっているかどうかを教えてくれます。
  • プロファイリング。繰り返しになりますが、何が重要であるかは重要ではないことはすぐにはわかりませんが、何が重要であるかを示唆するデータを提供します。1つの関数の呼び出しに多くのCPU時間を費やしている場合は、それを調べて、その機能を確認することをお勧めします。dotおよびでプロファイリング出力を使用して、graphvizコールグラフを生成し、カバレッジ分析などの関数が呼び出された回数を確認することもできます。複雑なコードの場合、グラフィック分析がはるかに役立つ場合があります。
  • Cで作業している場合、Frama-Cはコードの分析に役立つはずですが、あまりにも複雑すぎて作業に値しないように思われたため、これを使用したことはありません。純粋なCでいくつかの作業を行いますが、私が書くのはほとんどコードです。ドキュメントに記載されていないCコードを使用したことがありません。

2
非常に大きなC ++アプリケーションを理解する最も一般的な方法は何ですか?Stack OverflowではC ++固有ですが、いくつかの古さについての別の既存の問題があります。
dmckee ---元モデレーターの子猫

いいリスト。すべてをgitに入れ、「git grep」を使用します。また、個々のファイルのgit履歴(利用可能な場合)。
オンドレジ・セティク

3

私は常に生徒にコードを下から上に読むように言います。あなたはmain()から始めて、それが何を呼び出すかを確認します。通常、これは少数の関数です。次に、一般的にアルゴリズムの全体的なフローを定義するmain()から呼び出された関数(時間ステップループ、アセンブリ、ソルバー、出力など)を調べます。30,000フィートからアルゴリズムの概要を取得するには、2レベルほど深く行きます。残りはdoxygenのドキュメントなどから収集できます。

しかし、私が言ったように、メッセージは:ボトムアップでコードを読みます。


3
あなたのアドバイスはよく受け取られていますが、コードを最初から上から読むようなものではありません。むしろ、上から下に読んでいるように聞こえます。Main()(またはFortranではProgram)は、実行可能コードの最上位のプログラム単位です。さらに、問題のコードがAPIを定義するライブラリである場合は、アドバイスを少し調整する必要があります。main()関数はありません。この場合、通常はサンプルプログラムを調べる(またはコーディングする)ことにより、どの関数が呼び出すかを確認する戦略と、どの関数が重要かを判断する戦略を組み合わせる必要があります。
Geoff Oxberry、2011

Pascalを早い段階で学習したプログラマは、エントリポイントをファイルの最後(または下)に置くことが多いため、「下から」について混乱が生じる可能性があります。これは、Pascalが1パスのみを作成し、その前にすべてを導入する必要があったためです。と呼ばれていました。ただし、Geoffと同様に、Main()が下向きに成長するツリーの概念上の「トップ」にあるロジックを好みます。
dmckee ---元モデレーターの子猫'30

はい、そうでなければすべてを前方宣言する必要があるため、ファイルの最後にmain()を置くdmckeeの方法を意味しました。
Wolfgang Bangerth、2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.