私は C++ コードを生成していますが、非常に面倒になりそうです。単純な生成クラスでさえ、すでにたくさんの特殊なケースがあります。現在のコードは次のとおりです: http://github.com/alex/alex-s-language/tree/local%2Fcpp-generation/alexs_lang/cpp。
8 に答える
XML データ スキーマから C++ コードを生成するために Cog を作成しました。C++ ソース ファイルに埋め込まれた Python コードを使用して、C++ ソースを生成できます。
私がコード生成に使用した手法の 1 つは、コード ジェネレーターでの書式設定についてまったく気にしないことです。次に、コードを生成した後の次のステップとして、コードを実行しindent
て合理的にフォーマットし、コードを読み取れるようにします (さらに重要なことに、デバッグできます)。
テスト ケースを作成するためのツール を参照してください。
あなたの問題が何であるかは明らかではありません。
「生成クラスですべての特殊なケースをどのように処理すればよいですか?」という質問がある場合。それではアドバイスです。質問が他のものである場合は、質問を更新してください。
テンプレートジェネレーターを使用します。 たとえば、マコはあなたの人生をよりシンプルにします。
結果の例を書きます。
${thing}
パーツをプレースホルダーに置き換えます。機能するものから始めたので、それをテンプレートに変えるのは簡単です。別の言語でコードを生成する場合、柔軟なアセンブリ用に設計された他の言語ですべてのクラス定義を用意する必要があります。新鮮で新しいコードをできるだけ生成しないようにします。少し微調整してカスタマイズしたいが、ゼロから多くのものを生成したくない.
特殊なケースは、通常のポリモーフィズムで処理するのが最適です。共通のスーパークラスの個別のサブクラスは、さまざまな例外と特殊なケースを実装できます。非常に複雑な状況は、Strategyデザイン パターンによって適切に処理されます。
本質的に、現実世界のオブジェクトを表す Python クラスがあります。これらのクラスには、それらのオブジェクトの C++ バージョンを生成するために C++ テンプレートに適合できる属性があります。
Ned が示唆したように、Cog は定型コードを作成するための優れたツールです。たとえば、次のように機能する特定のクラスの AOP スタイルのイベント システム サポートを作成する必要がありました。
- クラスのメソッドを宣言します
- メソッドごとに、呼び出し時にイベントをトリガーし、メソッドの引数をイベント パラメーターとして渡す必要があります。
だから私は、各メソッドとイベントのボイラープレート宣言と定義を生成する歯車領域で呼び出す特別な python 宣言子関数を実行しました。コグ領域の最後に、ユーザーは、実装を非表示にし、AOP ラッパーによって呼び出される関数のコード ブロックを配置します。大まかに次のようになります。
class MyFoo
{
public:
/*[[[cog
import myAOPDeclarators
AOP = myAOPDeclarators.AOP
AOP.declareAOPInterceptorMethod( 'invokeSomeStuff' , '(int param1, std::string param2)' )
]]]*/
//AOP wrapper
void invokeSomeStuff_ImplementationAOP(int param1, std::string param2);
void invokeSomeStuff(int param1, std::string param2) {
sendAOPPreEvent( param1 , param2 , "invokeSomeStuff" );
invokeSomeStuff_ImplementationAOP( param1 , param2);
}
void invokeSomeStuff_ImplementationAOP(int param1, std::string param2)
//[[[end]]]
{
// ...invokeSomeStuff implementation, not automatically generated
}
コード生成について私が提供できる最良のガイドラインは次のとおりです。生成されたコードを、手書きのコードと同じくらい読みやすいものにします。これにより、コード生成の使用が透過的になります (テンプレート コードよりもさらに透過的です。YMMV を購入してください)。もちろん、Greg が提案したように、インデントは後で適用できるため、コード生成とインデントのいじりを混ぜて時間を無駄にすることは本当に意味がありません。とにかくツールがソースファイルを後処理できる場合
生成したいものの例を書き出す必要があるというS.Lottに同意します。
コード生成を使用して問題を解決することは、コード生成を使用しない場合よりも簡単になるはずです。
これは、プログラム全体で大量の入力情報を処理する必要があり、その情報のサブセットがほとんど (週に 1 回など) 変更されない場合、コード ジェネレーターはそのサブセットのみを条件とする必要があるためです。より頻繁に変化する残りの入力に対する生成コード条件。分割統治戦略です。別名「部分評価」です。
生成されたコードは一般的ではないため、実行速度も大幅に向上するはずです。
あなたの特定のケースでは、コード生成を 2 つ (またはそれ以上) のパスで実行しても害はありません。パス 1 と同様に、宣言を生成します。パス 2 では、プロセス コードを生成します。または、2 つの出力ストリームを生成し、最後にそれらを連結することもできます。
それが役立つことを願っています。当たり前のことばかり言ってすみません。
実際には、すべての関数宣言を引き出して別の場所に配置する必要があることと、すべての関数呼び出しに対してすべての引数のベクトルを構築し、それを関数に渡す必要があることを除いて、まっすぐ下に再帰するだけです。 、C++ にはベクトルの構文がないためです。
私もこのようなものを探していて、この質問を見つけました。私は cog にあまり満足していなかったので、最終的には似ているが (imo) 非常に必要な機能をいくつか追加した独自のものを作成しました。
私はコード生成システムを持っていますが、それで取った最良の選択の 1 つは、結果のプログラムの多くを非生成コード (ライブラリ/ランタイムなど) に入れることです。テンプレートの使用もうまくいきます。複雑なテンプレート システムを手動で操作するのは難しいかもしれませんが、手動で操作するわけではないので、それを活用してください。