web-dev-qa-db-ja.com

CソースをC ++に変換する

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

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

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

  1. C++のCサブセットですべてをコンパイルし、それを動作させます。
  2. モジュールを巨大なクラスに変換して、すべての相互参照がクラス名でスコープされるようにし、すべての関数とデータを静的メンバーとして残して、それを機能させる.
  3. 適切なコンストラクターと初期化された相互参照を使用して、巨大なクラスをインスタンスに変換します。静的メンバーアクセスを間接アクセスに適宜置き換えます。そしてそれを動作させます。
  4. ここで、不適切な要素のあるOOアプリケーションとしてプロジェクトに取り組み、依存関係が扱いやすい単体テストを記述し、依存関係がない別のクラスに分解します。ここでの目標は、1つから移動することです。各変換で別の作業プログラム。

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

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

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

[質問のために、C++への変換は必須であり、Cのままにすることはnotオプションであると想定します。この条件を追加するポイントは、「Cのままにする」の回答を取り除くことです。]

41
Barry Kelly

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

可能な限り、他の部分への影響を最小限に抑えて実行できる段階に分割します。 Federico Ramponi が示唆するように、ファサードシステムを構築することは良いスタートです-すべてがC++ファサードを持ち、それを介して通信している場合は、モジュールの内部を変更できることを確信を持って変更できます。それらの外に影響を与えます。

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

切り替えはまだ完了していません-暫定リリースでは2回一時停止しています(数週間ごとにポイントリリースを目指しています)が、順調に進んでおり、問題について顧客から苦情が寄せられていません。私たちのQA担当者も、私が覚えている問題を1つだけ見つけました。 :-)

15
Head Geek

何について:

  1. C++のCサブセットですべてをコンパイルし、それを機能させる。
  2. facades のセットを実装すると、Cコードは変更されませんか?

なぜ「C++への変換が必須」なのですか? Cコードをラップして巨大なクラスなどに変換する必要はありません。

12

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

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

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

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

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

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

はい、私はその会社のチーフアーキテクトとして関わっています。

7
Ira Baxter

Cインターフェースを介してC++クラスを作成します。 Cコードに触れないことで、混乱の可能性が減り、プロセスが大幅に高速化されます。

C++インターフェースを起動したら、次に、コードをコピーしてクラスに貼り付けるという簡単なタスクです。あなたが述べたように-このステップの間、ユニットテストを行うことが重要です。

5
user19302

GCCは現在、CからC++への移行中です。明らかに、CCCとC++の共通サブセットにすべてを移動することから始めました。彼らがそうしたように、彼らは-Wc++-compat。それはあなたの旅の最初の部分にあなたを連れて行くはずです。

後者の部分については、実際にすべてをC++コンパイラーでコンパイルしたら、慣用的なC++の対応物があるものを置き換えることに焦点を当てます。たとえば、Cマクロを使用して定義されたリスト、マップ、セット、ビットベクトル、ハッシュテーブルなどを使用している場合、これらをC++に移行することで多くのことを得ることができます。同様にOOを使用すると、C OOイディオム(構造継承など)を既に使用している場合、およびC++を使用するとコードがより明確になり、型チェックが向上するという利点が得られます。

4
Paul Biggar

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

3
Paul Nathan

おそらく、開始したい方法以外に考慮すべき2つのことは、何をしたいかfocusと、どこにしたいかstopです。

あなたは大きなコードチャーンがあると述べています、これはあなたの努力をfocusするための鍵かもしれません。多くのメンテナンスが必要なコードの部分を選択することをお勧めします。成熟した/安定した部分は明らかに十分に機能しているため、ファサードを備えたいくつかの窓のドレッシングなどを除いて、そのままにしておくことをお勧めします。

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

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

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

NB。 「レガシーコードを効果的に使用する」の本をご存知でしょうか。

3
andreas buykx

別の愚かな考えを投げましょう:

  1. C++のCサブセットですべてをコンパイルし、それを動作させます。
  2. モジュールから始めて、それを巨大なクラスに変換し、次にインスタンスに変換し、そのインスタンスからCインターフェース(元のインターフェースと同じ)を構築します。残りのCコードをそのCインターフェイスで動作させます。
  3. 必要に応じてリファクタリングし、OOサブシステムをCコードから一度に1モジュールずつ拡張し、Cインターフェースの一部が役に立たなくなったら削除します。
3

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

maketea をご覧ください。これは、ASTのパターンマッチング、および抽象文法からのAST定義、ビジター、トランスフォーマーなどを提供します。

2
Paul Biggar

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

  • コードは20年前なので、パーサー/構文アナライザーをスクラップダウンし、それを新しいLex/yacc/bison(または類似のもの)ベースのC++コードの1つで置き換えます。はるかに保守しやすく、理解しやすくなります。 BNFがあれば、開発も速くなります。
  • これが古いコードに改造されたら、モジュールをクラスにラップし始めます。グローバル/共有変数をインターフェースに置き換えます。
  • これで、C++のコンパイラになります(ただし、完全ではありません)。
  • システム内のすべてのクラスのクラス図を描き、それらがどのように通信しているかを確認します。
  • 同じクラスを使用して別のものを描画し、それらがどのように通信する必要があるかを確認します。
  • コードをリファクタリングして、最初の図を2番目の図に変換します。 (これは厄介で扱いにくいかもしれません)
  • 追加されたすべての新しいコードには、必ずC++コードを使用してください。
  • 時間が残っている場合は、データ構造を1つずつ置き換えて、より標準化されたSTLまたはBoostを使用してみてください。
1
Sridhar Iyer

小規模または学術的なプロジェクト(たとえば、10,000行未満)の場合は、書き換えがおそらく最良のオプションです。あなたは好きなようにそれを因数分解することができ、それはあまり時間がかかりません。

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

1
Nick