web-dev-qa-db-ja.com

ソースコード生成はアンチパターンですか?

何かを生成できる場合、それはデータではなくコードです。

それを考えると、ソースコード生成のこの全体的な考えは誤解ではありませんか?つまり、何かのコードジェネレーターがある場合、その何かを、必要なパラメーターを受け取り、「生成された」コードが行うであろう正しいアクションを実行できる適切な関数にしてみませんか?

パフォーマンス上の理由で行われている場合、コンパイラの欠点のように聞こえます。

2つの言語を橋渡しするためにそれが行われている場合、それはインターフェースライブラリの欠如のように聞こえます。

ここで何か不足していますか?

コードもデータであることは知っています。わからないのはなぜソースコードを生成するのか?パラメータを受け入れてそれらに作用できる関数にしてみませんか?

127
Utku

ソースコード生成はアンチパターンですか?

技術的には、コードを生成する場合、たとえ人間が読めるテキストであってもsourceではありません。 ソースコードは、人間または他の真の知性によって生成された元のコードであり、機械的に翻訳されておらず、(真の)ソースから(直接的または間接的に)すぐに再現できません。

何かを生成できる場合、それはデータではなくコードです。

とにかく私はすべてを言いますis dataとにかく。ソースコードも。 特にソースコード!ソースコードは、プログラミングタスクを実行するために設計された言語の単なるデータです。このデータは、必要に応じて他の形式(データの一部)に変換、解釈、コンパイル、生成され、その一部はたまたま実行可能です。

プロセッサはメモリ外の命令を実行します。データに使用されるのと同じメモリ。プロセッサが命令を実行する前に、プログラムはメモリにロードされますデータとして

したがって、すべてがデータ、偶数コードです。

[生成されたコードはデータである]とすると、コード生成のこの全体的な考えは誤解ではありませんか?

コンパイルに複数のステップを含めることはまったく問題ありません。そのうちの1つは、テキストとしての中間コード生成です。

つまり、何かのコードジェネレーターがある場合、その何かを、必要なパラメーターを受け取り、「生成された」コードが行うであろう正しいアクションを実行できる適切な関数にしてみませんか?

それは1つの方法ですが、他にもあります。


コード生成の出力はテキストであり、これは人間が使用するように設計されたものです。

すべてのテキストフォームが人間の消費を目的としているわけではありません。特に、生成されたコード(テキストとして)は通常、コンパイラによる消費を目的としています人間による消費ではありません。


ソースコードはオリジナルと見なされます:マスター—私たちが編集および開発するもの。ソースコード管理を使用してアーカイブするもの。生成されたコードは、人間が読めるテキストであっても、通常は元のソースコードから再生成されます。生成されたコードは、一般的に言えば、ビルド中に再生成されるため、ソース管理下にある必要はありません。

148
Erik Eidt

実用的な推論

OK、コードもデータであることは知っています。私が理解していないのは、なぜソースコードを生成するのですか?

この編集から、理論的なコンピュータサイエンスではなく、かなり実用的なレベルで質問していると思います。

Javaのような静的言語でソースコードを生成する古典的な理由は、そのような言語には、非常に動的な処理を行うための使いやすい言語ツールが実際には付いていないためです。たとえば、Javaの形成時代に戻って、動的な名前(DBのテーブル名と一致)と動的なデータ型(一致するテーブルの属性と一致)を持つクラスを簡単に作成することは不可能でした上記の属性のタイプ)。 特に Javaは、コンパイル時に型エラーをキャッチできることを保証するために、非常に重要です。

したがって、このような設定では、プログラマーはJavaコードを作成して、手動で多数のコード行を書き込むことができます。多くの場合、プログラマーは、テーブルが変更されるたびに、戻って一致するようにコードを変更する必要があることに気付くでしょう。そして、彼がそれを忘れると、悪いことが起こります。したがって、プログラマーは、彼のためにそれを行うツールをいくつか作成するようになります。したがって、道はますますインテリジェントなコード生成に始まります。

(はい、バイトコードをその場で生成できますが、そのようなものをJavaでプログラミングすることは、ランダムなプログラマーが数行のドメインコードを書き込む間に行うものではありません。)

これを、Rubyなどの非常に動的な言語と比較します。Rubyは、ほとんどの点でJavaとは正反対です(どちらのアプローチも評価せずに言っていることに注意してください。これらは単純に異なります)。ここで、実行時にクラス、メソッドなどを動的に生成することは100%通常で標準であり、最も重要なのは、プログラマが「メタ」レベルに移行することなく、コード内で簡単にそれを実行できることです。はい、Ruby on Railsのようなものはコード生成に付属していますが、私たちの作業では、基本的にそれを新しいプログラマー向けの一種の高度な「チュートリアルモード」として使用していることがわかりましたが、その後しばらくの間は余分になります(そのエコシステムに書き込むコードが非常に少ないため、何をしているのかわかっている場合は、生成されたコードをクリーンアップするよりも手動で書く方が速くなります)。

これらは、「実世界」からの2つの実際的な例にすぎません。次に、LISPのような言語のコードis dataを文字通り使用します。一方、コンパイルされた言語(JavaやRubyなどのランタイムエンジンなし)では、クラスを定義するという概念はありません(または、私は最新のC++機能に追いついていません...)。または実行時のメソッド名。したがって、コード生成、ビルドプロセスは、ほとんどの場合に最適なツールです(他のC/C++固有の例は、flex、yaccなどです)。

70
AnoE

なぜコードを生成するのですか?

パンチカード(または メモ帳のAltコード )を使用したプログラミングは面倒だからです。

パフォーマンス上の理由で行われている場合、コンパイラの欠点のように聞こえます。

本当です。強制されない限り、パフォーマンスは気にしません。

2つの言語を橋渡しするためにそれが行われている場合、それはインターフェースライブラリの欠如のように聞こえます。

うーん、何を話しているのかわからない。

次のように見えます。生成および保持されたソースコードは、いつまでも永遠に、お尻の痛みです。理由は1つだけです。誰かが1つの言語で作業したいが、他の誰かが別の言語で作業することを主張しているが、どちらもどちらも相互運用する方法を理解する必要がないため、好きな言語を強制言語に変換して、何ができるかを理解する必要がない彼らは望んでいる。

私がそれを維持しなければならないまでそれは問題ありません。その時点ですべて死ぬことができます。

それはアンチパターンですか?いいえ、ため息。以前の言語の欠点に別れを告げたくなければ、多くの言語は存在しません。古い言語のコードを生成することで、新しい言語がいくつ始まりますか。

それは私が我慢できない半分変換されたフランケンシュタインの怪物のパッチワークに残っているコードベースです。生成されたコードは変更できないコードです。手に負えないコードを見るのは嫌いです。それでも人々はチェックインし続けます。なぜですか?実行可能ファイルをチェックインすることもできます。

さて、今私は怒っています。私の要点は、私たちはすべて「コードを生成する」ことです。生成されたコードをソースコードのように扱うとき、私は夢中になります。ソースコードがソースコードにならないように見えるだけです。

44
candied_orange

ソースコードを生成する理由

私がキャリアで使用しなければならなかったコードジェネレーターの最も頻繁な使用例は、ジェネレーターでした。

  • ある種のデータモデルまたはデータベーススキーマ(リレーショナルスキーマ、またはなんらかのXMLスキーマなど)の高レベルのメタ説明を入力しました

  • データアクセスクラスのボイラープレートCRUDコードを出力として生成し、対応するSQLやドキュメントなどの追加の要素を生成した可能性があります。

ここでの利点は、短い入力仕様の1行から5〜10行のデバッグ可能、タイプセーフ、バグのない(コードジェネレーターの出力が成熟していると想定)コードを取得することです。これにより、メンテナンスと開発の労力がどれだけ削減されるか想像できます。

最初の質問にもお答えします

ソースコード生成はアンチパターンですか

いいえ、それ自体はソースコードの生成ではありませんが、確かにいくつかの落とし穴があります。 The Pragmatic Programmer で述べたように、コードジェネレーターが理解しにくいコードを生成する場合、コードジェネレーターの使用を避ける必要があります。 。それ以外の場合、このコードを使用またはデバッグするための労力の増加は、コードを手動で記述しないことによって節約される労力を簡単に上回ります。

また、再生成によって手動の変更が上書きされないように、生成されたコードの部分を手動で記述されたコードから物理的に分離することは、ほとんどの場合良い考えです。ただし、古い言語Xで記述されたコードを別のより現代的な言語Yに移行するというタスクにも何度か対処しました。その後、言語Yでのメンテナンスを目的としています。これは有効な使用法です。 1回限りのコード生成の場合。

41
Doc Brown

サスマンは、彼の古典的な「コンピュータプログラムの構造と解釈」で、主にコードとデータの二重性について、そのようなことについて語るのが非常に面白かった。

私にとってアドホックコード生成の主な用途は、利用可能なコンパイラを利用して、ドメイン固有の言語をプログラムにリンクできるものに変換することです。 BNFを考え、ASN1(実際にはそうではない、醜い)を考え、データ辞書のスプレッドシートを考えてください。

自明なドメイン固有の言語は時間を大幅に節約できるため、標準の言語ツールでコンパイルできるものを出力することは、そのようなものを作成するときに行う方法であり、編集するのではなく、自国のあらゆる自国語で自明でない手でハッキングされたパーサーです書き込み、または自動生成されたもののBNF?

テキストを出力することで、システムコンパイラに送られるので、そのコンパイラの最適化とシステム固有の構成をすべて考える必要はありません。

コンパイラ入力言語を別の中間表現として効果的に使用していますが、問題は何ですか? テキストファイルは本質的にソースコードではなく、コンパイラのIRである可能性があります、およびCまたはC++のように見える場合、またはJavaまたは何でも、 ?

今あなたが考えるのが難しいである場合、おもちゃの言語パーサーのOUTPUTを編集する可能性があります。これは、誰かが次に入力言語ファイルを編集して再構築するときに明らかにがっかりするでしょう、答えは自動生成をコミットしないことですリポジトリへのIR。ツールチェーンによって生成されるようにします(また、開発グループにそのような人々がいることを避けてください。彼らは通常、マーケティングでより幸せに働いています)。

これは、私たちの言語の表現力の失敗ではなく、仕様の一部を、自動的にコードに変換できる形式に取得(またはマッサージ)でき、通常ははるかに少なくなるという事実を表しています。バグがあり、保守がはるかに容易になります。テスト担当者と構成担当者にスプレッドシートと、そのデータを取得し、私のフラッシュ上の完全な16進数ファイルを出力するために実行できるツールを提供できる場合、ECUである場合、それは誰かが手動で最新の設定をその日の言語の定数のセットに変換するよりも大幅に時間を節約できます(タイプミスを完了)。

Simulinkでモデルを構築し、RTWでCを生成し、意味のあるツールを使用してターゲットにコンパイルするのと同じことですが、中間のCは読み取れません。高レベルのMatlab RTWはCのサブセットを知るだけでよく、Cコンパイラがプラットフォームの詳細を処理します。生成されたCを人間が這う必要があるのは、RTWスクリプトにバグがある場合のみです。そのようなことは、バイナリ解析ツリーだけを使用するよりも、名目上人間が読み取り可能なIRを使用する方がはるかに簡単です。

もちろん、バイトコードや実行可能コードを出力するためにそのようなことを書くことができますが、なぜそれを行うのでしょうか? IRをこれらのものに変換するためのツールがあります。

13
Dan Mills

実用的な答え:コード生成は必要かつ有用ですか?それは本当に非常に有用でプロプライエタリなコードベースに必要なものを提供しますか、それとも次善の結果のためのより多くの知的オーバーヘッドに貢献する方法で物事を行う別の方法を作成するように見えますか?

OK、コードもデータであることは知っています。私が理解していないのは、なぜコードを生成するのですか?パラメータを受け入れてそれらに作用することができる関数にしてみませんか?

この質問をする必要があり、明確な答えがない場合は、おそらくコード生成は不必要であり、単にコードベースにエキゾティシズムと多大な知的オーバーヘッドをもたらしているだけです。

一方、OpenShadingLanguageなどの場合: https://github.com/imageworks/OpenShadingLanguage

...そして、そのような質問は、印象的な結果によってすぐに答えられるので、提起する必要はありません。

OSLはLLVMコンパイラフレームワークを使用して、シェーダーネットワークをオンザフライでマシンコードに変換し(ジャストインタイム、つまり「JIT」)、その過程で、シェーダーパラメーターとその他のランタイム値を完全に理解して、シェーダーとネットワークを大幅に最適化します。シェーダーがソースコードからコンパイルされたときに知られています。その結果、OSLシェーディングネットワークは、Cで作成された同等のシェーダーよりも25%高速に実行されます。 (これがレンダラーで古いシェーダーが機能した方法です。)

このような場合は、コードジェネレーターの存在を確認する必要はありません。このタイプのVFXドメインで作業している場合、即時の応答は通常、「黙って私のお金を取りなさい!」のようになります。または、「うわー、このようなものを作る必要もあります」

13
user204677

なぜソースコードを生成するのですか?

生成された(ビルド時に、チェックインされていない)コードの2つの使用例に遭遇しました。

  1. ゲッター/セッター、toString、equals、hashCodeなどのボイラープレートコードを、そのようなものを指定するために構築された言語(Javaのプロジェクトlombokなど)から自動的に生成します。
  2. いくつかのインターフェース仕様(REST、SOAPなど)からDTOタイプクラスを自動的に生成し、メインコードで使用します。これは言語ブリッジの問題に似ていますが、生成されたクラスなしで同じものを実装しようとするよりも型処理が優れているため、より簡潔で単純になります。
13
Maybe_Factor

いいえ、中間コードの生成はアンチパターンではありません。質問のもう1つの部分である「なぜそれを行うのですか?」に対する回答は、非常に幅広い(そして個別の)質問ですが、とにかくいくつかの理由を挙げます。

人間が読める中間コードが存在しないという歴史的影響

CとC++は、最も有名な言語の1つであるため、例として取り上げます。

Cコードをコンパイルする論理的な行列は、マシンコードではなく、人間が読めるアセンブリコードを出力することに注意してください。同様に、C++コードをCコードに物理的にコンパイルするために使用されていた古いC++コンパイラ。その一連のイベントでは、人間が読めるコード1から人間が読めるコード2に、人間が読めるコード3からマシンコードにコンパイルできます。 "なぜ?"何故なの?

人間が読める中間コードが生成されなかった場合、have CまたはC++さえまったく生成されない可能性があります。それは確かに可能性です。人々は自分たちの目標への抵抗が最も少ない経路をたどります。Cの開発が停滞したために他の言語が最初にSteamを獲得した場合、Cはまだ若いうちに死亡した可能性があります。もちろん、「でも他の言語を使っているのかもしれないし、もっといいのかもしれない」と主張することもできます。多分、または多分それはより悪いでしょう。あるいは、私たち全員がまだアセンブリで書いているでしょう。

人間が読める中間コードを使用する理由

  1. ビルドの次のステップの前に変更できるように、中間コードが必要な場合があります。この点が一番弱いと認めます。
  2. 元の作業が人間が読める言語ではなくGUIモデリングツールで行われたことが原因である場合があります。
  3. 時々あなたは非常に反復的な何かをする必要があり、言語すべきではないそれは非常にニッチなものまたは非常に複雑なものであり、ビジネスが複雑さまたは文法を増加させないためですちょうどあなたに対応するためのプログラミング言語の。
  4. 時々あなたは非常に反復的な何かをする必要があり、そこに可能な方法はありません希望するものを一般的な方法で言語に取り込むために==;言語の文法で表現できないか、矛盾しています。
  5. コンピュータの目標の1つは人間の労力を減らすことです。また、二度と触れられることがほとんどない(メンテナンスの可能性が低い)コードでは、10分の1の時間で長いコードを生成するメタコードを作成できます。 2週間ではなく1日でそれができて、それが維持される可能性が低い場合は、それを生成するほうがよい-そして、5年後の誰かが実際に彼らのために悩まされているという偶然があったらdo =維持する必要がある場合、必要に応じて2週間かけて完全に書き出すか、厄介なコードを維持する1週間でイライラする(ただし、その時点ではまだ1週間進んでいる)。 ifメンテナンスをまったく行う必要がないこと。
  6. 見落としている理由は他にもあると思います。

私は以前に、他のドキュメントのデータまたは情報に基づいてコードを生成する必要があるプロジェクトに取り組んできました。たとえば、1つのプロジェクトでは、すべてのネットワークメッセージと定数データが​​スプレッドシートで定義され、スプレッドシートを通過してC++のlotおよびJava =これらのメッセージを処理できるコード。

それがそのプロジェクトをセットアップするための最良の方法であったとは言いません(私はスタートアップの一部ではありませんでした)、それは私たちが持っていたものであり、それは数百(おそらく数千、多分、わからない)の構造とオブジェクトおよび定数でした生成されていました。その時点では、おそらくラプソディのようなものでそれをやり直すのは遅すぎるでしょう。しかし、それがRhapsodyのようなものでやり直されたとしても、とにかくRhapsodyから生成されたコードがありますです。

また、すべてのデータをスプレッドシートに収めることは、ある意味で優れています。これにより、すべてがソースコードファイル内にあるだけでは実現できなかった方法でデータを表現できるようになりました。

例2

コンパイラー構築でいくつかの作業を行ったとき、Antlrツールを使用して字句解析と構文解析を行いました。言語文法を指定し、ツールを使用してC++またはJavaのいずれかで大量のコードを吐き出し、その生成コードを自分のコードと一緒に使用して、ビルドに含めました。

他にどのようにそれを行うべきでしたか?おそらく、別の方法を考え出すことができます。おそらく他の方法があります。しかし、その作業のために、他の方法は、私が生成したLex /解析コードよりも優れていませんでした。

8
Aaron

ソースコードとは何か、そうでないものに焦点を当てる理由に焦点を当てた、もう少し実際的な答え。ソースコードの生成は、このすべてのケースでビルドプロセスの一部であることに注意してください。したがって、生成されたファイルがソース管理に到達することはありません。

相互運用性/シンプルさ

主な例として、Googleのプロトコルバッファを見てみましょう。1つの高レベルのプロトコル記述を記述して、複数の言語で実装を生成するために使用できます。多くの場合、システムのさまざまな部分が異なる言語で記述されています。

実装/技術的な理由

TypeScriptを取得-ブラウザーはそれを解釈できないため、ビルドプロセスはtranspiler(コードからコードへの変換)を使用してJavaScriptを生成します。実際、多くの新しいまたは難解なコンパイル済み言語は、適切なコンパイラーを取得する前に、Cへのトランスパイルから始まります。

使いやすさ

Cで記述され、単一のバイナリ(RTOSまたはOSなし)のみを使用する組み込みプロジェクト(IoTを考える)の場合、直接リンクするのではなく、通常のソースコードのようにコンパイルするデータを含むC配列を生成するのは非常に簡単です。リソースとして。

編集する

Protobufの拡張:コード生成により、生成されたオブジェクトを任意の言語でファーストクラスのクラスにすることができます。コンパイルされた言語では、ジェネリックパーサーは必然的にキーと値の構造を返します。つまり、多くのボイラープレートコードが必要であり、コンパイル時のチェック(特にキーと値のタイプ)を逃し、パフォーマンスの低下とコード補完なし。それらすべてを想像してくださいvoid* Cまたはその巨大なstd::variant C++(C++ 17を使用している場合)では、一部の言語にはそのような機能がない場合があります。

7
Jan Dorniak

欠けているのはreuseです。

コンパイラと呼ばれる、ソースコードテキストをバイナリに変換する素晴らしいツールがあります。入力は明確に定義されており(通常は!)、最適化の方法を改善するために多くの作業が行われてきました。実際にseコンパイラーでいくつかの操作を実行する場合は、既存のコンパイラーを使用し、独自のコンパイラーを作成しないでください。

多くの人々が新しいプログラミング言語を発明し、独自のコンパイラを作成しています。ほとんど例外なく、彼らは挑戦を楽しんでいるためではなく、needその言語が提供する機能のために、すべてこれを行っています。彼らが行うことはすべて、別の言語で行うことができます。彼らはそれらの機能が好きなので、単に新しい言語を作成しています。ただし、うまく調整されず、高速で、効率的で、最適化されたコンパイラーはありません。テキストをバイナリに変換できるものを確実に取得しますしかし、既存のすべてのコンパイラほど優れているわけではありません

テキストは人間が読み書きするものだけではありません。コンピュータも完全にテキストを備えています。実際、XML(およびその他の関連フォーマット)のようなフォーマットは成功します理由プレーンテキストを使用します。多くの場合、バイナリファイル形式はあいまいで文書化が不十分であり、読者はそれらがどのように機能するかを簡単に見つけることができません。 XMLは比較的自己文書化されているため、XML形式のファイルを使用するコードを簡単に記述できます。そして、すべてのプログラミング言語は、テキストファイルを読み書きするように設定されています。

それで、あなたの生活を楽にするためにいくつかの新しい施設を追加したいとしましょう。おそらく、それはGUIレイアウトツールです。多分それは Qt が提供する信号とスロットのインターフェースです。たぶん TIのコードComposer Studio を使用すると、使用しているデバイスを構成し、適切なライブラリをビルドにプルすることができます。おそらく、データディクショナリを取得しているのでしょう。 typedefとグローバル変数定義を自動生成します(はい、これは組み込みソフトウェアではまだ非常に重要です)。それが何であれ、既存のコンパイラーを活用する最も効率的な方法は、何でも構成するツールを作成することです。それは選択した言語でコードを自動的に生成します。

何が起こっているのかがわかっていて、ソースコードを読み取れるので、開発もテストも簡単です。 GCCに匹敵するコンパイラを構築するために何年も費やす必要はありません。完全に新しい言語を習得したり、他の人に学習を要求したりする必要はありません。必要なのは、この1つの小さな領域を自動化することだけで、その他はすべて同じままです。仕事が終わりました。

7
Graham

ソースコード生成はアンチパターンですか?

これは、表現力が不十分なプログラミング言語の回避策です。適切な組み込みメタプログラミングを含む言語でコードを生成する必要はありません。

6
kevin cline

ソースコードの生成は常にアンチパターンであるとは限りません。たとえば、私は現在、与えられた仕様によって2つの異なる言語(JavascriptとJava)でコードを生成するフレームワークを書いています。フレームワークは、生成されたJavascriptを使用してユーザーのブラウザーアクションを記録し、SeleniumのJavaコードを使用して、フレームワークが再生モードのときに実際にアクションを実行します。コード生成を使用しなかった場合、両方が常に同期していることを手動で確認する必要があります。これは面倒であり、何らかの形で論理的に重複しています。

ただし、ジェネリックスなどの機能を置き換えるためにソースコード生成を使用している場合、それはアンチパターンです。

6
Hristo Vrigazov

ここで何か不足していますか?

中間コードが成功の理由であることが判明した良い例でしょうか? HTMLを提供できます。

HTMLがシンプルで静的であることが重要だと思います。これにより、ブラウザーの作成が簡単になり、モバイルブラウザーを早期に開始できるようになりました。さらなる実験(Javaアプレット、Flash)が示すように、より複雑で強力な言語はより多くの問題を引き起こします。実際には、ユーザーはJavaアプレットによって危険にさらされており、そのようなWebサイトにアクセスすることは、DC++を介してダウンロードされたゲームのクラックを試すのと同じくらい安全でした。一方、プレーンHTMLは無害です。デバイスのセキュリティを合理的に信じているサイトをチェックしてください。

ただし、HTMLは、コンピューターで生成されていない場合、現在の場所には近づきません。私の答えは、誰かがデータベースからHTMLファイルに手動で書き直すまで、このページには表示されません。幸い、ほとんどすべてのプログラミング言語で使用可能なHTMLを作成できます。

つまり、何かのコードジェネレーターがある場合、その何かを、必要なパラメーターを受け取り、「生成された」コードが行うであろう正しいアクションを実行できる適切な関数にしてみませんか?

生成された中間コードとしてHTMLを使用するよりも、ユーザーに質問およびすべての回答とコメントを表示するより良い方法を想像できますか?

6
Džuris

なぜソースコードを生成するのですか?

特に手間のかかる反復的なタスクでは、コードを手動で作成するよりも速くて簡単です(エラーが発生しにくくなります)。また、高レベルツールを使用して、1行のコードを記述する前に設計を検証および検証することもできます。

一般的な使用例:

  • RoseやVisual Paradigmなどのモデリングツール。
  • 高レベル-er Embedded SQLのようなレベルの言語またはコンパイル可能なものに前処理する必要があるインターフェイス定義言語。
  • Flex/bisonのようなレクサーおよびパーサージェネレーター。

「関数にしてパラメータを直接渡すだけではない理由」については、上記のどれもそれ自体が実行環境ではないことに注意してください。それらに対してコードをリンクする方法はありません。

3
John Bode

場合によっては、プログラミング言語に必要な機能がないだけで、実際に必要な機能を実行するための関数やマクロを作成できないことがあります。または、多分あなたはcouldしたいことをしますが、それを書くためのコードは醜いでしょう。単純なPythonスクリプト(または類似の)スクリプトは、ビルドプロセスの一部として必要なコードを生成し、それを#includeを実際のソースファイルに挿入します。

どうすればわかりますか?これは、さまざまな異なるシステムを操作しているときに何度も到達したソリューションであり、最近ではSourcePawnです。単純なPythonスクリプトは、ソースコードの単純な行を解析し、2行または3行の生成コードを生成します。このような行が2ダースになる場合、手動で生成コードを作成するよりもはるかに優れています(すべての私のcvarを作成します)。

デモ/サンプルソースコードは、必要に応じて利用できます。

2
rosuav

一度だけコードを生成する

すべてのソースコード生成が一部のコードを生成する場合であり、決してそれに触れることはありません。更新が必要な場合は、元のソースから再生成します。

場合によっては、コードを1回だけ生成し、元のソースを破棄して、新しいソースを維持して前進することがあります。

これは、コードをある言語から別の言語に移植するときに発生することがあります。特に、元の新しい変更を後で移植することを望まない場合(たとえば、古い言語コードは維持されないか、実際には完全です(たとえば、一部の数学機能の場合))。

1つの一般的なケースは、これを行うコードジェネレーターを作成すると、実際にはコードの90%しか正しく変換されない場合があることです。そして、最後の10%は手動で修正する必要があります。これは、100%を手動で翻訳するよりもはるかに高速です。

このようなコードジェネレータは、多くの場合、完全な言語トランスレータ(Cythonやf2cなど)が生成するコードジェネレータの種類とは大きく異なります。目標はコードを一度維持することなので。彼らはしばしば彼らがしなければならないことを正確に行うために、1回限りとして作られています。多くの点で、コードを移植するために正規表現/検索置換を使用する次のレベルのバージョンです。 言うことができる「ツール支援移植」

例えばからコードを一度だけ生成するウェブサイトのスクレイピング。

密接に関連しているのは、再度アクセスしたくないソースからコードを生成した場合です。例えば。コードを生成するために必要なアクションが反復可能でないか、一貫していない場合、またはそれらの実行にコストがかかる場合。現在、一対のプロジェクトに取り組んでいます: DataDeps.jl および DataDepsGenerators.jl

DataDeps.jlは、ユーザーがデータをダウンロードするのに役立ちます(標準のMLデータセットのように)。これを行うには、RegistrationBlockと呼ばれるものが必要です。これは、ファイルのダウンロード元やチェックサムなどのメタデータを指定するコードと、データの条件/条件/ライセンスステータスをユーザーに説明するメッセージです。

それらのブロックを書くのは面倒です。そしてその情報は、多くの場合、データがホストされているWebサイトの(構造化または非構造化)フォームから入手できます。したがって、DataDepsGenerators.jlは、大量のデータをホストする一部のサイトに対して、webscraperを使用してRegistrationBlockCodeを生成します。

正しく生成されない場合があります。したがって、生成されたコードを使用する開発者は、それをチェックして修正することができます。たとえば、ライセンス情報が見落とされていないことを確認したいのではないでしょうか。

重要なのは、DataDeps.jlを使用するユーザー/開発者は、生成されたRegistrationBlockコードを使用するためにwebscraperをインストールまたは使用する必要がないことです。 (そして、Webスクレーパーをダウンロードしてインストールする必要がないため、特にCIの実行では、かなりの時間を節約できます)

ソースコードを一度生成することはアンチパターンではありません。そしてそれは通常、メタプログラミングに置き換えることはできません。

1
Lyndon White

コード生成を使用する方法はいくつかあります。それらは3つの主要なグループに分けることができます:

  • コンパイルプロセスのステップからの出力としてdifferent言語でコードを生成する。典型的なコンパイラの場合、これは低レベルの言語になりますが、JavaScriptにコンパイルされる言語の場合のように、別の高レベルの言語になる可能性があります。
  • コンパイルプロセスのステップとして、ソースコード言語でコードを生成または変換します。これがmacrosの機能です。
  • ツールでコードを生成する個別に通常のコンパイルプロセスから。これからの出力は、通常のソースコードと共にファイルとして存在し、それとともにコンパイルされるコードです。たとえば、ORMのエンティティークラスはデータベーススキーマから自動生成される場合があり、データ転送オブジェクトとサービスインターフェースはSOAPのWSDLファイルのようなインターフェース仕様から生成される場合があります。

これは最も物議を醸している形式なので、生成されたコードの3番目の種類について話していると思います。最初の2つの形式では、生成されたコードは中間ステップであり、ソースコードから非常に明確に分離されています。ただし、3番目の形式では、生成されたコードに「このコードを編集しないでください」というコメントが含まれている場合を除いて、ソースコードと生成されたコードが正式に分離されることはありません。それでも、開発者が生成されたコードを編集するリスクがまだあります。コンパイラーから見ると、生成されるコードはソースコードです。

それにもかかわらず、そのような形式の生成されたコードは、静的に型付けされた言語で本当に役立つ場合があります。たとえば、ORMエンティティと統合する場合、データベーステーブルに厳密に型指定されたラッパーがあると非常に便利です。確かにcouldは実行時に動的に統合を処理しますが、タイプセーフとツールサポート(コード補完)は失われます。静的型言語の主な利点は、実行時だけでなく、書き込みのタイプでの型システムのサポートです。 (逆に、このタイプのコード生成は、動的に型付けされた言語ではあまり普及していません。そのような言語では、ランタイム変換と比較して利点がないためです。)

つまり、何かのコードジェネレーターがある場合、その何かを、必要なパラメーターを受け取り、「生成された」コードが行うであろう正しいアクションを実行できる適切な関数にしてみませんか?

タイプセーフとコード補完はコンパイル時(およびIDEでコードを記述しているとき)に必要な機能なので、通常の関数は実行時にのみ実行されます。

ただし、中間的な理由があるかもしれません。F#は、基本的にはコンパイル時にプログラムで生成される厳密に型指定されたインターフェイスである型プロバイダーの概念をサポートしています。この概念は、おそらくコード生成の多くの使用法に取って代わり、懸念をより明確に分離することができます。

1
JacquesB

「ソース」コードの生成は、生成される言語の欠点を示しています。これを克服するためのツールの使用はアンチパターンですか?絶対にありません-私に説明させてください。

通常、コード生成が使用されるのは、結果として得られるコードを記述できる上位レベルの定義が存在するため、下位レベルの言語よりもはるかに冗長ではないためです。したがって、コード生成は効率と簡潔さを促進します。

C++を作成するときは、アセンブラーやマシンコードを使用するよりも効率的にコードを作成できるためです。それでもマシンコードはコンパイラによって生成されます。当初、c ++は単にCコードを生成するプリプロセッサでした。汎用言語は、汎用的な動作を生成するのに最適です。

同様に、DSL(ドメイン固有言語)を使用することにより、簡潔に記述できますが、特定のタスクに限定されたコードが含まれる可能性があります。これにより、コードの正しい動作を生成するのが簡単になります。 コードはtoおよびendであることを忘れないでください。開発者が求めているのは、動作を生成する効率的な方法です。

理想的には、ジェネレーターは、操作と理解がより簡単な入力から高速コードを作成できます。これが満たされる場合、ジェネレーターを使用しないのはアンチパターンです。このアンチパターンは通常、「純粋な」コードが「よりクリーン」であるという概念に由来します。これは、木工や他の職人が電動工具の使用、またはCNCを使用してワークピースを「生成」することを考えるのと同じです( ゴールデンハンマー )。

一方、生成されたコードのソースの維持またはコードの生成がより効率的ではない場合、ユーザーは間違ったツールを使用するという罠に陥ります(同じ ゴールデンハンマーが原因で )。

1
daramarak

人間が簡単に利用できるようにするには、テキスト形式が必要です。コンピュータはまた、テキスト形式のコードを非常に簡単に処理します。したがって、生成されたコードは、生成が最も簡単で、コンピューターが最も使いやすい形式で生成する必要があります。これは、非常に多くの場合、読み取り可能なテキストです。

また、コードを生成するとき、コード生成プロセス自体を人間がデバッグする必要があることがよくあります。人間がコード生成プロセスで問題を検出できるように、生成されたコードが人間が読める形式であると、非常に役立ちます。結局のところ、誰かがコードを作成してコードを生成する必要があります。薄い空気からは起こりません。

1
gnasher729

プロセッサ命令セットは基本的に 命令的 ですが、プログラミング言語は 宣言的 にすることができます。宣言型言語で記述されたプログラムを実行するには、必ず何らかのタイプのコード生成が必要です。 この回答 などで述べたように、人間が読める言語でソースコードを生成する主な理由は、コンパイラーによって実行される高度な最適化を利用することです。

0
Kevin Krumwiede

ソースコード生成は、生成されたコードがデータであることを絶対に意味します。しかし、それはファーストクラスのデータであり、プログラムの残りの部分が操作できるデータです。

私が知っている、ソースコードに統合されている2つの最も一般的なデータタイプは、ウィンドウに関するグラフィック情報(さまざまなコントロールの数と配置)とORMです。どちらの場合も、コード生成による統合により、データを操作するのが簡単になります。これは、それらを使用するために特別な「特別な」手順を実行する必要がないためです。

オリジナル(1984年)のMacで作業する場合、ダイアログとウィンドウの定義は、データをバイナリ形式で保持するリソースエディターを使用して作成されていました。アプリケーションでこれらのリソースを使用することは、「バイナリ形式」がPascalであった場合よりも困難でした。

したがって、いいえ、ソースコードの生成はアンチパターンではありません。データの一部をアプリケーションの一部にすることで、使いやすくなります。

0
jmoreno

コードとデータの両方は次のとおりです。情報。

データは、正確に必要な形式(および値)の情報です。コードも情報ですが、間接的または中間的な形式です。本質的に、コードもデータの形式です。

より具体的には、コードは、マシンが人間自身で情報を処理することから解放するための情報です。

人間を情報処理から解放することが最も重要な動機です。中間ステップは、生活を楽にするものであれば問題ありません。これが、中間情報マッピングツールが存在する理由です。コードジェネレーター、コンパイラー、トランスパイラーなど.

なぜソースコードを生成するのですか?パラメータを受け入れてそれらに作用できる関数にしてみませんか?

誰かがそのようなマッピング関数を提供していて、その実装がわかりにくいとしましょう。関数が約束どおりに機能する限り、内部でソースコードを生成しているかどうかを気にしますか?

0
S.D.

コード生成は、達成するよりもコストがかかる場合、アンチパターンです。この状況は、AからBへの生成が行われる場合に発生します。AはBとほぼ同じ言語ですが、Aでコーディングするだけで、AからBのすべてのカスタムツールやビルドステージングよりも少ない労力で実行できるマイナーな拡張がいくつかあります。 。

外部のテキスト処理のステージングを通じてメタプログラミングを実現することの複雑さと不十分さのために、メタプログラミング機能(構造マクロ)を持たない言語でのコード生成に対してトレードオフはより禁止されます。

貧弱なトレードオフは、使用量にも関係している可能性があります。言語AはBと大幅に異なる可能性がありますが、カスタムコードジェネレーターを備えたプロジェクト全体では、1つまたは2つの小さな場所でのみAを使用するため、複雑さの合計(Aの小さなビットとA-> Bコードジェネレーター、さらに、周囲のビルドステージング)は、Bで行われたばかりのソリューションの複雑さを超えています。

基本的に、コード生成に専念する場合は、おそらく「大きくするか、家に帰る」必要があります。実質的なセマンティクスを持たせて、それを多く使用するか、または気にしないでください。

0
Kaz

私はこれが明確に述べられているのを見ませんでした(1つまたは2つの回答に触れられているのを見ましたが、あまり明確ではないようです)

(あなたが言ったように、あたかもそれがデータであるかのように)コードを生成することは問題ではありません。これは、コンパイラを二次的な目的で再利用する方法です。

生成されたコードを編集することは、これまでに遭遇する最も陰湿で、邪悪で、恐ろしいアンチパターンの1つです。こんなことしないで。

せいぜい、生成されたコードを編集するだけで、一連の貧弱なコードがプロジェクトに取り込まれます(コードの完全なセットは、本当にソースコードになり、もはやデータではなくなります)。最悪の場合、プログラムに組み込まれたコードは、冗長性が高く、名前が不十分で、ほぼ完全に保守不可能なガベージです。

3つ目のカテゴリは、一度使用したコード(guiジェネレータ?)で、編集して、開始/学習を支援するものだと思います。これは少しずつですが、開始するには良い方法かもしれませんが、GUIジェネレーターは「生成可能な」コードを使用することを目的としているため、プログラマーとしてはあなたにとって素晴らしいスタートとはなりません-さらに、もう1つのGUIにもう一度使用したいという誘惑は、冗長なソースコードをシステムに取り込むことを意味します。

ツールが、生成されたコードの編集を一切許可しないほど賢い場合は、それを試してください。そうでなければ、私はそれをそこにある最悪のアンチパターンの1つと呼びます。

0
Bill K

何かを生成できる場合、それはデータではなくコードです。

後でそのコードがデータであると規定しているので、命題は「何かが生成できる場合、それはコードではありません」に減少します。では、Cコンパイラによって生成されたアセンブリコードはコードではないと言えるでしょうか。手作業で作成したアセンブリコードと正確に一致する場合はどうなりますか?よろしければそこへ行っても大丈夫ですが、私は同行しません。

代わりに、「コード」の定義から始めましょう。技術的になりすぎない限り、この説明の目的に適した定義は「計算を実行するための機械で実行可能な命令」になります。

それを考えると、ソースコード生成のこの全体的な考えは誤解ではありませんか?

ええ、はい、あなたの最初の命題はコードが生成できないということですが、私はその命題を拒否します。私の「コード」の定義を受け入れれば、一般的なコード生成に概念的な問題はないはずです。

つまり、何かのコードジェネレーターがある場合、その何かを、必要なパラメーターを受け取り、「生成された」コードが行うであろう正しいアクションを実行できる適切な関数にしてみませんか?

まあ、それは性質についてというよりは、コード生成を採用するためのreasonについての完全に異なる質問です。コードジェネレーターを作成または使用する代わりに、結果を直接計算する関数を作成するという代替案を提案しています。しかし、どの言語で?誰もがマシンコードで直接書き込んだ時代は終わりました。他の言語でコードを書く場合、コンパイラーやアセンブラーの形のコードジェネレーターに依存して、実際に実行されるプログラムを作成します。

では、なぜJavaまたはCまたはLISPなど何でも書きたいと思いますか?アセンブラでさえありますか?これらの言語は、それを作成するデータと操作の抽象化を提供しているため、少なくとも部分的にはそうです。実行する計算の詳細を簡単に表現できます。

同じことがほとんどの上位レベルのコードジェネレーターにも当てはまります。典型的なケースは、おそらくLexyaccなどのスキャナーおよびパーサージェネレーターです。はい、スキャナーとパーサーをCで直接作成することも、選択した他のプログラミング言語(未加工のマシンコードでさえ)で作成することもできます。ただし、非常に複雑な問題の場合は、Lexやyaccなどの高レベルの特殊目的言語を使用すると、手書きのコードの記述、読み取り、保守が容易になります。通常、はるかに小さいです。

また、「コードジェネレーター」が正確に何を意味するかを考慮する必要があります。 Cの前処理とC++テンプレートのインスタンス化は、コード生成の練習問題だと思います。これらに異議を唱えますかそうでなければ、私はあなたがそれらを受け入れ、他の種類のコード生成を拒否することを合理化するために、いくつかの精神的な体操を実行する必要があると思います。

パフォーマンス上の理由で行われている場合、コンパイラの欠点のように聞こえます。

どうして?基本的には、ユーザーがデータをフィードする「プログラム」と「入力」に分類され、計算を実行して「出力」と呼ぶより多くのデータを出力するユニバーサルプログラムが必要であると考えています。 (ある観点から、そのような普遍的なプログラムを「オペレーティングシステム」と呼ぶかもしれません。)しかし、コンパイラーがそのような汎用プログラムを最適化するのに、より専門的なものを最適化するのと同じくらい効果的であると仮定するのはなぜですか?プログラム? 2つのプログラムには、異なる特性と機能があります。

2つの言語を橋渡しするためにそれが行われている場合、それはインターフェースライブラリの欠如のように聞こえます。

あなたは、まるでユニバーサルからある程度のインターフェース・ライブラリーを持っているのが良いことであるかのように言う。おそらくそうなるかもしれませんが、多くの場合、そのようなライブラリーは大きく、作成や保守が難しく、場合によっては遅くなることもあります。そして、そのような獣が実際に特定の問題に対処するために存在しない場合、コード生成アプローチが問題をより迅速かつ簡単に解決できるとき、誰があなたがそれを作成することを主張するでしょうか?

ここで何か不足していますか?

いくつかあると思います。

コードもデータであることは知っています。私が理解していないのは、なぜソースコードを生成するのですか?パラメータを受け入れてそれらに作用することができる関数にしてみませんか?

コードジェネレーターは、ある言語で記述されたコードを別の通常は下位レベルの言語のコードに変換します。では、なぜ人々が複数の言語を使用してプログラムを書きたいのか、特に主観的に異なるレベルの言語を混在させたいのかと尋ねています。

しかし、私はすでにそれについて触れました。特定のタスクの言語は、そのタスクの明快さと表現力に部分的に基づいて選択します。コードが小さいほど平均してバグが少なく、保守が容易になるため、少なくとも大規模な作業では、高水準言語に偏る傾向があります。しかし、複雑なプログラムには多くのタスクが含まれており、その一部は1つの言語でより効果的に処理できる場合があり、他のプログラムは別の言語でより効果的またはより簡潔に処理される場合があります。仕事に適したツールを使用することは、コード生成を採用することを意味することがあります。

0
John Bollinger

コメントのコンテキスト内で質問に答える:

コンパイラの義務は、人間が読める形式で記述されたコードを取得し、それを機械可読形式に変換することです。したがって、コンパイラが効率的なコードを作成できない場合、コンパイラはその仕事を適切に行っていません。それは間違っていますか?

コンパイラーがタスクに最適化されることはありません。その理由は簡単です。多数タスクを実行するように最適化されています。これは、多くの人々がさまざまなタスクに使用する汎用ツールです。タスクが何であるかがわかったら、ドメイン固有の方法でコードにアプローチし、コンパイラーができなかったトレードオフを作成できます。

例として、私はアナリストがコードを書く必要があるかもしれないソフトウェアに取り組みました。彼らはアルゴリズムをC++で記述し、依存するすべての境界チェックとメモ化のトリックを追加できますが、これにはコードの内部動作についてlotを知る必要があります。彼らはどちらかと言えば単純なものを書いて、最終的なC++コードを生成するためのアルゴリズムをここに投げさせます。次に、静的分析のようにパフォーマンスを最大化するためのエキゾチックなトリックを実行できます。これにより、アナリストが耐えることは決してありません。コード生成を使用すると、ドメイン固有の方法で記述できるため、汎用ツールよりも簡単に製品をドアから出すことができます。

正反対のこともしました。 「コードを生成しない」という義務があった別の作業があります。私たちはソフトウェアを使用する人たちの生活を楽にしたかったので、膨大な量のテンプレートメタプログラミングを使用して、コンパイラーにオンザフライでコードを生成させました。したがって、私は自分の仕事をするために、汎用のC++言語のみが必要でした。

ただし、落とし穴があります。エラーが読み取り可能であることを保証するのは非常に困難でした。これまでにテンプレートメタプログラムコードを使用したことがある場合は、1つの無害な間違いが、何が問題かを理解するために理解できないクラス名とテンプレート引数の100行を要するエラーを生成する可能性があることがわかります。この影響は非常に顕著だったため、構文エラーの推奨デバッグプロセスは、「自分のファイルの1つにエラーがあるのが初めて表示されるまでエラーログをスクロールします。その行に移動して、何が起きるかがわかるまで目を細めてください。間違った」

コード生成を使用していれば、人間が読めるエラーを備えた、より強力なエラー処理機能があったはずです。 C'est la vie。

0
Cort Ammon