2012-04-11 12 views
5

非常に大量のコード、主にC++をリファクタリングして、与えられた値に永続的に設定された多数の一時的な構成チェックを削除しています。たとえば、私は次のコードを持っています:C++リファクタリング:条件付き展開とブロック除去

#include <value1.h> 
#include <value2.h> 
#include <value3.h> 

... 

if (value1()) 
{ 
    // do something 
} 

bool b = value2(); 

if (b && anotherCondition) 
{ 
    // do more stuff 
} 

if (value3() < 10) 
{ 
    // more stuff again 
} 

ここで、値の呼び出しはboolかintのどちらかを返します。私はこれらの呼び出しは、常に返す値を知っているので、私は彼らの正常値への呼び出しを拡張するためのいくつかの正規表現置換を行ってきた:

// where: 
// value1() == true 
// value2() == false 
// value3() == 4 

// TODO: Remove expanded config (value1) 
if (true) 
{ 
    // do something 
} 

// TODO: Remove expanded config (value2) 
bool b = false; 

if (b && anotherCondition) 
{ 
    // do more stuff 
} 

// TODO: Remove expanded config (value3) 
if (4 < 10) 
{ 
    // more stuff again 
} 

注値が固定されているが、それらはコンパイル時に設定されていないことが、共有メモリから読み込まれるため、コンパイラは現在、何もしていない状態で最適化を行っていません。

この正規表現のアプローチは少しばかげているようですが、コードの振る舞いを変更せずに、呼び出しに依存することをやり直すのは簡単で、コンパイラブロックが決して呼び出されない、またはチェックが常に真を返すことを知って、多くのものを最適化することができます。また、(バージョン管理に対する差分の時は特に)、それは合理的に簡単に変更されているかを確認し、それをクリーンアップする最後のステップを取るようになり、次のようにコード上記のコードは、最終的になります。

// do something 

// DONT do more stuff (b being false always prevented this) 

// more stuff again 

トラブルがIということです2番目の正しい、ばかげた段階から最終的なクリーンコードに到達するまでに何百(何千もの)変更を加える必要があります。

これを処理するリファクタリングツールを知っている人がいれば、私はそれを適用することができます。主な問題は、C++の構文が完全拡張または削除を非常に困難にし、上記のコードに多くの順列があることです。私は、私がカバーする必要がある構文のバリエーションに対処するためにコンパイラがほとんど必要だと感じています。

似たような質問がありましたが、私はこれに似た要件を見つけることができず、質問されてからツールや手順が出てきたのか疑問に思っています。

答えて

5

ですね。これは、組織化された実行時設定変数のほとんどのシステムでは非常に一般的な問題です。最終的に、一部の設定変数は永続的な固定状態になりますが、実行時に繰り返し評価されます。

regexはC++コードを確実に解析しないため、注意したように、治療法は正規表現ではありません。 必要なものはprogram transformation systemです。これはソースコードを解析し、解析ツリーに一連のコードからコードの書き換えルールを適用し、変更されたツリーからソーステキストを再生成できるツールです。

私はここにいくつかの能力があることを理解しています。 C++を解析してツリーを構築できますが、ソースからソースへの変換機能はありません。あなたはASTからASTへの変換を書くことによってその能力をシミュレートできますが、それはもっと不便なIMHOです。私はそれがC + +コードを再生成することができると信じていますが、コメントやプリプロセッサのディレクティブを保持するかわかりません。

DMS Software Reengineering ToolkitC++(11) front endは、C++ソースコードを大量に変換することができます(これまでは使用されていました)。ソースからソースへの変換があります。 AFAIK、それはこれを行うことができる唯一の生産ツールです。必要なのは、目的の構成変数の最終状態に関する知識と、簡単なコード簡略化ルールの知識を表す一連の変換です。以下のDMSのルールは、あなたがそう望むものに近い:

rule fix_value1():expression->expression 
    "value1()" -> "true"; 
    rule fix_value2():expression->expression 
    "value2()" -> "false"; 
    rule fix_value3():expression->expression 
    "value3()" -> "4"; 

    rule simplify_boolean_and_true(r:relation):condition->condition 
    "r && true" -> "r". 
    rule simplify_boolean_or_ture(r:relation):condition->condition 
    "r || true" -> "true". 
    rule simplify_boolean_and_false(r:relation):condition->condition 
    "r && false" -> "false". 
    ... 
    rule simplify_boolean_not_true(r:relation):condition->condition 
    "!true" -> "false". 
    ... 

    rule simplify_if_then_false(s:statement): statement->statement 
     " if (false) \s" -> ";"; 
    rule simplify_if_then_true(s:statement): statement->statement 
     " if (true) \s" -> "\s"; 
    rule simplify_if_then_else_false(s1:statement, s2:statement): statement->statement 
     " if (false) \s1 else \s2" -> "\s2"; 
    rule simplify_if_then_else_true(s1:statement, s2: statement): statement->statement 
     " if (true) \s1 else \s2" -> "\s2"; 

はまた、(「倍」)算術演算を含む定数式を簡素化するためのルールを必要とし、ルールは今一定である式にスイッチを処理するために。整数定数の折り畳みについてDMSルールがどのように見えるかを確認するには、Algebra as a DMS domainを参照してください。

正規表現とは異なり、DMS書き換えルールはコードと「不一致」することはできません。それらは対応するASTを表し、一致するASTです。 ASTマッチングであるため、空白、改行、コメントに問題はありません。オペランドの順序に問題があると思われるかもしれません(「何か偽」の場合& & x "が発生しましたか?彼らは、文法の規則として、& &||は、DMS C++パーサーで連想型および可換型としてマークされ、マッチング処理で自動的に考慮されます。

これらのルールが単独で行うことができないことは、割り当てを越えた(あなたのケースでは定数の)伝播です。このためには、そのような割り当て(「到達する定義」)をトレースできるように、フロー分析が必要です。明らかに、あなたがそのような割り当てを持っていないか、またはごくわずかな場合は、パッチを渡すことができます。そうしたら、フロー解析が必要になります。ああ、DMSのC++フロントはあまりありませんが、私たちはそれに取り組んでいます。制御フロー解析が適切に行われています。 (DMSのCフロントエンドにはフルフロー分析があります)。

(編集:2015年2月:機能/メソッド内のフロー解析、フルフローC++ 14)

実際にこのテクニックをIBM Tivoliとの混合CおよびC++コードの1.5M SLOCアプリケーションに適用したのは、ほぼ10年前のことです。私たちはフロー解析を必要としませんでした: -

+0

はい、これはまさに私が欲しいものです。正規表現はかなり私を得ることができますが、それらは不正確です。あなたが言うように、重要なことに、彼らは言語を「知っていません」。どうもありがとう。 (あなたの "DMSソフトウェアリエンジニアリングツールキット"のリンクは壊れています) –

+0

基本的に正規表現では入れ子になるものは扱えません。 (ブール)式にはネストが含まれます。 QED:regexesは式では動作しません。 PS:リンクが固定されました。 –

1

あなたは言う:値を合理的に固定されているが、それらはコンパイル時に設定されていませんが、コンパイラは、現在、舞台裏離れて何かを最適化されていないので、共有メモリから読み出されていることを

注意を。

定数折り、彼らは固定完全ない限り手で値は多くの意味がありません。あなたのコンパイラは、あなたがそれを使用することができconstexpr提供、またはあなたがこのようなプリプロセッサマクロに置き換えることができた場合:

#define value1() true 
#define value2() false 
#define value3() 4 

オプティマイザはそこからあなたの世話をするでしょう。 <valueX.h>ヘッダーにあるものの正確な例を見たり、これらの値を共有メモリから取得するプロセスがどのように機能しているかを知ることなく、既存のvalueX()関数の名前を変更してランタイムチェックを行うと便利かもしれません彼らは将来的に再び変更する場合は:あなたは、私が実際に死んだ...「ゾンビコード」と呼ぶが、それでも限りコンパイラが懸念しているとして生きるものを持っているよう

// call this at startup to make sure our agreed on values haven't changed 
void check_values() { 
    assert(value1() == get_value1_from_shared_memory()); 
    assert(value2() == get_value2_from_shared_memory()); 
    assert(value3() == get_value3_from_shared_memory()); 
} 
+0

ありがとう、私の文言は悪かった:彼らは固定され、変更されませんが、コンパイル時ではありません。私は、コンパイラが最適化できるように、代わりの考え方が好きで、私はすでにregex置換で自動的に達成しました。しかし、目的の1つはコード「クルフト(cruft)」を削除することです。大量のコードブロックがあり、かなり大きいものがあります。値が固定されると完全に冗長になり、ソースから自動的に削除できればいいでしょう同じように。 –

+0

あなたの頭の中の表現を評価するのに苦労しているなら、到達不能なコードを見つけるための貧しい人のテストは私の提案に従って、gccの '-Wunreachable-code'警告(通常はオフです。 '-Wall'で)。私の推測では、あなたのためにこれを行う無料のコードを書き直すツールは、おそらく自分自身で行うよりもセットアップが面倒です。あなたが間違いを犯す可能性があるならば、それを一気に置き換えるのではなく、式を持ち上げて、あなたの単純化に平等を主張してください。 – HostileFork

関連する問題