41

かなり大きく (>300K)、かなり成熟した C コードベースを C++ に変換するにはどうすればよいでしょうか?

念頭に置いている種類の CI は、モジュールにほぼ対応するファイルに分割され (つまり、典型的な OO クラスベースの分解よりも粒度が低い)、プライベート関数とデータの代わりに内部リンケージを使用し、パブリック関数とデータには外部リンケージを使用します。グローバル変数は、モジュール間の通信に広く使用されます。非常に広範な統合テスト スイートが利用可能ですが、ユニット (モジュール) レベルのテストはありません。

私は一般的な戦略を念頭に置いています:

  1. C++ の C サブセットですべてをコンパイルし、それを機能させます。
  2. モジュールを巨大なクラスに変換して、すべての相互参照がクラス名によってスコープされるようにしますが、すべての関数とデータを静的メンバーとして残し、それを機能させます。
  3. 適切なコンストラクターと初期化された相互参照を使用して、巨大なクラスをインスタンスに変換します。必要に応じて、静的メンバー アクセスを間接アクセスに置き換えます。そしてそれを機能させます。
  4. 次に、問題のある OO アプリケーションとしてプロジェクトにアプローチし、依存関係が扱いやすいユニット テストを作成し、そうでない場合は個別のクラスに分解します。ここでの目標は、変換のたびに 1 つの作業プログラムから別のプログラムに移行することです。

明らかに、これはかなりの作業になります。この種の翻訳に関するケーススタディ/戦争の話はありますか? 代替戦略?その他の役立つアドバイスはありますか?

注 1: プログラムはコンパイラであり、おそらく何百万もの他のプログラムがその動作が変更されないことに依存しているため、大規模な書き換えはほとんどオプションではありません。

注 2: ソースはほぼ 20 年前のものであり、おそらく 1 年あたり 30% のコード チャーン (変更された行 + 追加された行 / 以前の合計行数) があります。言い換えれば、それは大幅に維持および拡張されています。したがって、目標の 1 つは保守性を高めることです。

[質問のために、 C++への変換は必須であり、C のままにしておくことは選択肢ではないと仮定します。この条件を追加するポイントは、「C に残す」という回答を除外することです。]

4

11 に答える 11

15

数か月前にほぼ同じことを始めたばかりなので (10 年前の商用プロジェクトで、もともと「C++ はスマートな C を備えた C に他ならないstruct」という哲学で書かれていました)、同じ戦略を使用することをお勧めします。 d 象を食べる:一度に一口ずつ食べます。:-)

できるだけ他の部分への影響を最小限に抑えて実行できる段階に分割します。Federico Ramponiが提案したように、ファサード システムを構築することは良い出発点です。すべてに C++ ファサードがあり、それを介して通信するようになると、モジュールの内部を変更して、モジュールの外部に影響を与えないというかなりの確信を持てるようになります。

(以前の小規模なリファクタリングの取り組みにより) 部分的な C++ インターフェース システムが既に配置されていたため、このアプローチは私たちの場合は難しくありませんでした。すべてを C++ オブジェクトとして通信できるようになると (これには数週間かかり、完全に別のソース コード ブランチで作業し、すべての変更が承認されたときにメイン ブランチに統合されました)、完全にコンパイルできないことはほとんどありませんでした。その日の出発前の作業バージョン。

切り替えはまだ完了していません。中間リリースのために 2 回一時停止しました (数週間ごとのポイント リリースを目指しています)。私たちの QA 担当者が見つけた問題は 1 つだけで、私も覚えています。:-)

于 2008-10-14T03:19:28.517 に答える
12

どうですか:

  1. C++ の C サブセットのすべてをコンパイルし、それを機能させる。
  2. C コードを変更せずに一連のファサードを実装しますか?

なぜ「C++ への翻訳が必須」なのですか? C コードを巨大なクラスに変換する手間をかけずにラップできます。

于 2008-10-14T00:57:39.847 に答える
7

あなたのアプリケーションにはたくさんの人が取り組んでおり、壊れてはいけない必要があります。OOスタイルへの大規模な変換を真剣に考えている場合、必要なのは作業を自動化するための大規模な変換ツールです。

基本的な考え方は、データのグループをクラスとして指定し、コードをリファクタリングしてそのデータをクラスに移動し、そのデータの関数をそれらのクラスに移動し、そのデータへのすべてのアクセスを修正してクラスを呼び出すことです。 。

自動化された事前分析を実行して統計クラスターを形成し、いくつかのアイデアを得ることができますが、グループ化するデータ要素を決定するには、アプリケーションを意識したエンジニアが必要です。

このタスクを実行できるツールは、DMS SoftwareReengineeringToolkitです。DMSには、コードを読み取るための強力なCパーサーがあり、Cコードをコンパイラー抽象構文ツリーとしてキャプチャし(従来のコンパイラーとは異なり)、300KSLOC全体のフロー分析を計算できます。DMSには、「バック」エンドとして使用できるC++フロントエンドがあります。C構文をC++構文にマップする変換を記述します。

大規模なアビオニクスシステムでの主要なC++リエンジニアリングタスクは、この種のアクティビティにDMSを使用することがどのようなものかについてのアイデアを提供します。www.semdesigns.com/Products/DMS/DMSToolkit.htmlのテクニカルペーパー、特に自動プログラム変換によるC++コンポーネントモデルのリエンジニアリングを参照してください。

このプロセスは、気の弱い人向けではありません。しかし、大規模なアプリケーションの手動リファクタリングを検討する人は誰よりも、すでにハードワークを恐れていません。

はい、私は会社のチーフアーキテクトである会社と関係があります。

于 2009-06-14T04:32:57.020 に答える
5

私なら、C インターフェイスを介して C++ クラスを作成します。C コードに触れないことで、めちゃくちゃになる可能性が減り、プロセスが大幅に速くなります。

C++ インターフェイスを起動したら、次に、コードをコピーしてクラスに貼り付けるのは簡単な作業です。おっしゃったように、このステップでは単体テストを行うことが不可欠です。

于 2008-10-14T00:54:57.233 に答える
3

どのように始めたいか以外に、おそらく考慮すべき 2 つのことは、何に焦点を合わせたいか、どこで止めたいかということです。

大規模なコード チャーンがあると述べています。これは、取り組みに集中するための鍵となる可能性があります。多くのメンテナンスが必要なコードの部分を選択することをお勧めします。成熟した/安定した部分は明らかに十分に機能しているため、おそらくファサードなどのウィンドウドレッシングを除いて、そのままにしておくことをお勧めします.

どこで停止するかは、C++ に変換する理由によって異なります。これ自体が目標になることはほとんどありません。サード パーティの依存関係が原因である場合は、そのコンポーネントへのインターフェイスに注力してください。

私が取り組んでいるソフトウェアは、何年も前に C から C++ に「変換」された巨大な古いコード ベースです。GUIがQt化されたからだと思います。今でもほとんどクラスを持つ C プログラムのように見えます。パブリック データ メンバーによって引き起こされる依存関係を壊し、手続き型のモンスター メソッドを使用して巨大なクラスをより小さなメソッドとクラスにリファクタリングすることは、実際には成功していません。次の理由からだと思います。

  1. 機能していて拡張する必要のないコードを変更する必要はありません。そうすることで、機能を追加せずに新しいバグが発生し、エンド ユーザーはそれを評価しません。
  2. リファクタリングを確実に行うことは非常に困難です。コードの多くの部分は非常に大きく、また非常に重要であるため、人々はほとんど触れようとしません。機能テストのかなり広範なスイートがありますが、十分なコード カバレッジ情報を取得するのは困難です。その結果、リファクタリング中に問題を検出するのに十分なテストがすでに実施されているかどうかを確認することは困難です。
  3. ROI を確立するのは困難です。エンド ユーザーはリファクタリングの恩恵を受けないため、メンテナンス コストを削減する必要があります。リファクタリングによって、成熟したコード (つまり、バグのないコード) に新しいバグが発生するため、最初は増加します。また、リファクタリング自体にもコストがかかります...

注意。「レガシーコードを効果的に使用する」という本を知っていると思いますか?

于 2008-10-14T07:42:42.230 に答える
3

あなたのリストは問題ないように見えますが、コーディングを行う前に、最初にテスト スイートを確認し、できるだけ厳密にすることをお勧めします。

于 2008-10-14T01:19:39.087 に答える
3

別のばかげた考えを投げてみましょう:

  1. C++ の C サブセットですべてをコンパイルし、それを機能させます。
  2. モジュールから開始し、巨大なクラスに変換してからインスタンスに変換し、そのインスタンスから C インターフェイス (開始したものと同じ) を構築します。残りの C コードをその C インターフェイスで動作させます。
  3. 必要に応じてリファクタリングし、一度に 1 モジュールずつ C コードから OO サブシステムを成長させ、不要になった C インターフェイスの一部を削除します。
于 2008-10-14T02:03:52.820 に答える
2

あなたのツールはコンパイラであり、「実際には、型マッチングだけでなく、複数のディスパッチでのパターンマッチングがさらに優れている」と述べています。

maketeaをご覧になることをお勧めします。AST のパターン マッチング、抽象文法からの AST 定義、ビジター、トランスフォーマーなどを提供します。

于 2009-08-29T15:12:36.320 に答える
1

これが私がすることです:

  • コードは 20 年前のものなので、パーサー/構文解析器を廃棄して、新しい lex/yacc/bison (または類似のもの) などに基づく C++ コードに置き換えてください。BNF が手元にあると、開発も速くなります。
  • これを古いコードに後付けしたら、モジュールをクラスにラップし始めます。グローバル/共有変数をインターフェイスに置き換えます。
  • これで、C++ のコンパイラができました (完全ではありません)。
  • システム内のすべてのクラスのクラス図を描き、それらがどのように通信しているかを確認します。
  • 同じクラスを使用して別のものを描画し、それらがどのように通信する必要があるかを確認します。
  • コードをリファクタリングして、最初の図を 2 番目の図に変換します。(これは面倒でトリッキーかもしれません)
  • 追加されたすべての新しいコードには、忘れずに C++ コードを使用してください。
  • 時間があれば、データ構造を 1 つずつ置き換えて、より標準化された STL または Boost を使用してみてください。
于 2008-10-14T16:41:55.760 に答える
1

小規模または学術的なプロジェクト (たとえば、10,000 行未満) がある場合は、書き直しがおそらく最良の選択肢です。必要に応じて因数分解できますが、それほど時間はかかりません。

実際のアプリケーションがある場合は、C++ としてコンパイルすることをお勧めします (これは通常、主に関数プロトタイプなどを修正することを意味します)。その後、リファクタリングと OO ラッピングに取り組みます。もちろん、受け入れられる C++ コードであるためには、コードを OO 構造にする必要があるという哲学には同意しません。必要に応じて、部分ごとに変換、書き直し、リファクタリングを行います(機能または単体テストを組み込むため)。

于 2008-10-14T01:01:18.700 に答える