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


118

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

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

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

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

ここに何かが足りませんか?

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


11
コードの生成に関連した用語は、メタプログラミング
UselesssCat

4
en.wikipedia.org/wiki/Code_as_data、Lispの、FP、スクリプト、メタプログラミング、フォン・ノイマン/修正ハーバード・アーキテクチャなどは、それはカバーされていうんざりします。tl; dr「ソースコード」と「出力コード」、「コード」と「データ」などの区別は、物事を単純化するためのものです。彼らは独断的ではありません。
vaxquis

9
@Utku、コード生成を行うより良い理由は、現在の言語が表現できるよりも高いレベルの説明を提供たいことに関係していることがよくあります。コンパイラが効率的なコードを作成できるかどうかは、実際には何の関係もありません。パーサージェネレーターを検討してください-によって生成されたレクサーflexまたはによって生成されたパーサーは、bisonほぼ確実に、Cで手書きされた同等のものよりも予測可能、正確で、多くの場合実行が高速になります。また、はるかに少ないコードから構築されます(したがって、保守作業も少なくなります)。
チャールズダフィー

1
多分あなたは多くの機能的な要素を持たない言語から来ますが、多くの言語では関数が第一級です-あなたはそれらを渡すことができるので、それらのタイプの言語ではコードはデータであり、あなたはそれをそのように扱うことができます。
Restioson

1
関数型言語コードの@Restiosonはデータではありません。ファーストクラス関数とは、まさに次のことを意味します。関数はデータです。必ずしも特に良いデータというわけではありません:それらを少しだけ変異させることはできません(関数内のすべての加算を減算に変異させるなど)。コードは同音異義語のデータです。(ほとんどのホモイコニック言語にはファーストクラスの機能がありますが、その逆は当てはまりません。)
リンドンホワイト

回答:


150

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

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

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

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

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

そのため、すべてがデータ、さらにはコードです。

[生成されたコードはデータ]であることを考えると、このコード生成の考え全体が誤解ではないでしょうか?

コンパイルに複数のステップがあり、その1つがテキストとしての中間コード生成である場合もあります。

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

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


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

すべてのテキスト形式が人間が使用するためのものではありません。特に、生成されたコード(テキストとして)は通常、人間が消費するのではなく、コンパイラーが消費することを目的としています。


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


1
コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
maple_shaft

65

実用的な推論

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

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

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

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

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

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

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


1
これは、より多くの投票をした回答よりも優れていると思います。特に、Javaおよびデータベースプログラミングで説明した例は、コード生成が使用されており、有効なツールである理由を実際に解決する上で、はるかに優れています。
パンツァークライシス

最近、JavaでDBから動的テーブルを作成することは可能ですか?または、ORMを使用するだけですか?
ヌメノン

「(または、私は最新のC ++機能に追いついていませんでした...)」これは、関数ポインターのおかげで20年以上にわたってC ++で可能になりましたか?私はそれをテストしていませんが、おそらくchar配列を割り当て、マシンコードでそれを埋めてから、最初の要素へのポインターを関数ポインターにキャストしてから実行する必要があると確信していますか?(ターゲットプラットフォームには、それを行うのを止めるためのセキュリティ対策がないと仮定します。これは
おそらく

1
「char配列を割り当て、マシンコードで埋めてから、最初の要素へのポインターを関数ポインターにキャストしてから実行しますか?」未定義の動作であることに加えて、「オンザフライでバイトコードを生成する」と同等のC ++です。「通常のプログラマーには考慮されない」という同じカテゴリーに分類されます
カレス

1
@Pharap、「これはC ++で20年以上に渡って実現できました」...私は少し笑わなければなりませんでした。最後にC ++をコーディングしてから約20年です。:)しかし、C ++に関する私の文章はとにかくひどく定式化されました。私はそれを少し変更しましたが、今は私が意図したものがより明確になるはずです。
-AnoE

44

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

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

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

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

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

うーん、何を言っているのかわかりません。

このように見えます:生成され保持されたソースコードは、常に永遠に苦痛です。理由は1つだけです。他の誰かが別の言語で作業することを主張している間、誰かが1つの言語で作業することを望んでおり、どちらも彼らの間で相互運用する方法を見つけ出すのに悩むことができないので、彼らの1人は自分の好きな言語を課された言語に変換する方法を見つけ出します彼らが欲しい。

私がそれを維持する必要があるまで、これは問題ありません。その時点で、あなたはすべて死ぬことができます。

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

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

さて、私は暴言を言っています。私のポイントは、すべて「コードを生成する」ことです。生成されたコードをソースコードのように扱うとき、私は夢中になります。ソースコードがソースコードにならないように見えるからです。


41
生成する場合、ソースコードではありません。中間コードです。私は今泣きに行きます。
candied_orange

65
ARG !!! それがどのように見えるかは関係ありません!!! テキスト、バイナリ、DNA、ソースではない場合、変更を加える際に触れるべきものではありません。私のコンパイルプロセスが42個の中間言語を通過する場合、それはビジネスではありません。それらに触れないでください。チェックインを停止します。ソースで変更を加えます。
candied_orange

24
XMLはテキストであり、明らかに人間が消費するためのものではありません。:
ニックケイリー

38
@utku:「人間が何かを消費することを意図していない場合、それはテキストであってはなりません」:私は完全に同意しません。私の頭の上のいくつかの反例:HTTPプロトコル、MIMEエンコーディング、PEMファイル-base64をどこでも使用するほとんど何でも。データを7ビットの安全なストリームにエンコードする理由はたくさんありますが、人間がデータを見る必要はありません。物事の非常に大きなスペースは言うまでもありません通常、人間が対話することはありませんが、しかし、彼らは時折したいこと:ファイル、ログ/etc/などのUnix上のファイルを、
ダニエルPryden

12
「パンチカードを使ったプログラミング」は、あなたがそれが意味すると思うことを意味するとは思わない。私はそこに行って、それをやった、そしてええ、それ苦痛だった。しかし、「生成されたコード」とは関係ありません。パンチされたカードのデッキは、ディスク上のファイル、テープ上のファイル、SDカード上のファイルなど、別の種類のファイルです。昔は、カードのデッキにデータを書き込み、そこからデータを読み取りました。そのため、コードを生成する理由がパンチカードを使用したプログラミングが苦痛であるためである場合、それはあらゆる種類のデータストレージ使用したプログラミングが苦痛であることを意味します。
ソロモン遅い

41

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

私のキャリアで一緒に仕事をしなければならなかったコードジェネレーターの最も頻繁なユースケースは、ジェネレーターでした。

  • 何らかの種類のデータモデルまたはデータベーススキーマの高レベルのメタ記述を入力として使用しました(リレーショナルスキーマ、または何らかの種類のXMLスキーマなど)

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

ここでの利点は、短い入力仕様の1行から、5行から10行のデバッグ可能で、タイプセーフでバグのない(コードジェネレーターの出力が成熟していると仮定した)コードを手作業で実装および保守する必要があることです。これにより、メンテナンスと進化の労力がどれだけ削減されるか想像できます。

最初の質問にも答えさせてください

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

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

また、再生成によって手動の変更が上書きされないように、生成されたコードの部分を手動で記述されたコードから物理的に分離することをお勧めします。ただし、古い言語Xで記述されたコードを別のより現代的な言語Yに移行し、その後言語Yでメンテナンスすることを目的とする状況にも複数回対処しました。これは有効な使用方法ですワンタイムコード生成の場合。


私はこの答えに同意します。Torque for javaのようなものを使用して、SQLデータベースに一致するフィールドを持つJavaソースファイルの自動生成を行うことができます。これにより、クラッド操作がはるかに簡単になります。主な利点は、データベースに存在するフィールドのみを参照できることを含むタイプセーフティです(オートコンプリートをありがとう)。
MTilsted

はい、静的に型付けされた言語の場合、これは重要な部分です。手書きのコードが生成されたコードに実際に適合することを確認できます。
パエロエベルマン

「古い言語で書かれたコードを移行する」-それでも、1回限りのコード生成は大きな苦痛になるかもしれません。たとえば、いくつかの手動の変更の後、ジェネレータのバグを検出し、修正後に生成をやり直す必要があります。幸いなことに、gitなどは通常痛みを和らげることができます。
maaartinus

13

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

生成された(ビルド時に、チェックインされなかった)コードの2つのユースケースに遭遇しました:

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

15
表現力に欠ける言語での非常に反復的なコード。たとえば、多くの類似しているが同一ではないデータ構造で、本質的に同じことを行うコードを書く必要がありました。たぶん、C ++テンプレートのようなものでできたかもしれません(そのコード生成ではないのですか?)。しかし、私はCを使用していました。コード生成により、ほぼ同一のコードを大量に記述できました。
ニックケイリー

1
@NickKeighleyおそらく、あなたのツールチェーンは、他のより適切な言語の使用を許可していなかったのでしょうか?
ウィルソン

7
通常、実装言語を選択することはできません。プロジェクトはCで行われましたが、これはオプションではありませんでした。
ニックケイリー

1
@Wilsonは、より表現力のある言語ではコード生成(たとえば、Lispマクロ、Ruby on Rails)を使用することが多く、その間にテキストとして保存する必要はありません。
ピートカーカム

4
ええ、コード生成は本質的にメタプログラミングです。Rubyのような言語では、言語自体でメタプログラミングを行うことができますが、Cではできないため、代わりにコード生成を使用する必要があります。
ショーンバートン

13

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

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

些細なドメイン固有の言語は時間を大幅に節約でき、標準的な言語ツールでコンパイルできるものを出力することは、そのようなものを作成するときに行く方法です。書き込み、または自動生成されたもののBNF?

テキストを出力し、それをシステムコンパイラに渡すことで、考えずにすべてのコンパイラの最適化とシステム固有の構成を取得します。

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

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

これは、仕様の一部を自動的にコードに変換できる形式にしたり、通常ははるかに少なくなるという事実の表現として、私たちの言語の表現力の失敗ではありませんバグやメンテナンスがはるかに簡単になります。テストと設定の担当者が調整できるスプレッドシートと、そのデータを取得して実行するツールを提供できれば、ECUのフラッシュ用に完全な16進ファイルを吐き出し、手動で翻訳するよりも大幅に時間を節約できますその日の言語での定数セットへの最新のセットアップ(タイプミスで完了)。

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

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


これは良いことですが、どのIRを使用するかを決定するときにトレードオフがあることを付け加えます。たとえば、CをIRとして使用すると、x86アセンブリ言語と比較すると、いくつかのことが簡単になり、他のことは難しくなります。Java言語コードとJavaバイトコードのどちらかを選択する場合、選択はさらに重要になります。1つまたは他の言語でのみ存在する操作がさらにたくさんあるためです。
ダニエル・プライデン

2
しかし、X86アセンブリ言語は、ARMコアまたはPPCコアをターゲットにすると、IRが低下します!すべてのものはエンジニアリングのトレードオフであり、それが彼らがそれをエンジニアリングと呼ぶ理由です。Javaバイトコードの可能性がJava言語の可能性の厳密なスーパーセットであり、ツールチェーンやIRを注入する場所に関係なく金属に近づくにつれて、これが一般的に正しいことを願うでしょう。
ダン・ミルズ

ああ、私は完全に同意します:私のコメントは、なぜあなたがバイトコードや低レベルのものを出力するのか疑問に思っているあなたの最後の段落への応答でした-時々あなたは低レベルを必要とします。(特にJavaでは、Java言語自体ではできないバイトコードでできることはたくさんあります。)
ダニエル・プライデン

2
私は同意しませんが、一般性を低下させるだけでなく、通常は本当に厄介な低レベルの最適化の責任を負うことになりますが、金属に近いIRを使用するコストがかかります。実装よりもアルゴリズムの選択を最適化するという観点で私たちが一般的に考えているという事実は、コンパイラーがどこまで進んだかを反映している場合があります。低すぎるレベルのIRを使用して最適化する能力。
ダン・ミルズ

1
「彼らは通常、マーケティングで働いている方が幸せです」キャティ、しかし面白い。
dmckee

13

実用的な答え:コード生成は必要で便利ですか?それは本当に非常に有用で、独自のコードベースに必要なものを提供しますか、それとも、次善の結果のために知的オーバーヘッドを増やす方法で物事を行う別の方法を作成するように見えますか?

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

この質問をする必要があり、明確な答えがない場合、おそらくコード生成は不要であり、単に異国情緒とコードベースに多大な知的オーバーヘッドをもたらすだけです。

一方、OpenShadingLanguageのようなものを取る場合:https : //github.com/imageworks/OpenShadingLanguage

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

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

このような場合、コードジェネレーターの存在を疑問視する必要はありません。このタイプのVFXドメインで作業している場合、通常、「シャットダウンしてお金を取りなさい!」という行に即答します。または、「うわー、私たちもこのようなものを作る必要があります。」


シェーダーネットワークをマシンコードに変換します。これは、コードジェネレーターではなくコンパイラのように聞こえますか?
-Utku

2
基本的には、ユーザーが接続するノードネットワークを取り、LLVMによってコンパイルされたJITである中間コードを生成します。コンパイラとコードジェネレーターの違いは、あいまいです。C ++のテンプレートやCプリプロセッサなどの言語のコード生成機能の行についてもっと考えていましたか?

ソースコードを出力するジェネレーターを考えていました。
-Utku

私は、出力がまだ人間の消費のためであると思います。OpenSLは中間ソースコードも生成しますが、LLVMを使用するためのアセンブリに近い低レベルのコードです。通常、保守することを目的としたコードではありません(代わりに、プログラマーがコードの生成に使用されるノードを保守します)。ほとんどの場合、これらのタイプのコードジェネレーターは、特にビルドプロセスの一環としてコードを絶えず再生成する必要がある場合、その価値を正当化するほど十分に有用ではなく、悪用される可能性が高いと思います。時々彼らはまだ欠点に対処するための本物の場所を持っています

...特定のドメインで使用する場合に使用可能な言語。QTには、メタオブジェクトコンパイラ(MOC)で物議を醸すものの1つがあります。MOCは、C ++で通常プロパティとリフレクション、信号とスロットなどを提供する必要があるボイラープレートを削減しますが、その存在を明確に正当化するほどではありません。MOCのコード生成の面倒な負担がなければ、QTの方がよかったと思うことがよくあります。

8

いいえ、中間コードの生成はアンチパターンではありません。あなたの質問の他の部分「なぜそれをするのか?」への答えは、非常に幅広い(そして別々の)質問ですが、とにかくいくつかの理由を述べます。

中間の人間が読めるコードを持たないことの歴史的な影響

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

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

中間の人間が読み取れるコードが生成されなかった場合 CまたはC ++ さえまったくないかもしれません。それは確かに可能性です。人々は目標への抵抗が最も少ない道を歩み、C開発の停滞のために他の言語が最初に勢いをつけた場合、Cはまだ若いうちに死んだかもしれません。もちろん、「しかし、その場合は、他の言語を使用している可能性があり、おそらくそれがより良いでしょう」と主張することができます たぶん、あるいはもっと悪いだろう。それとも、私たちはまだアセンブリで書いているでしょう。

なぜ人間が読める中間コードを使用するのですか?

  1. ビルドの次のステップの前に修正できるように、中間コードが必要な場合があります。この点が最も弱いことを認めます。
  2. オリジナルの作業が人間が読める言語で行われたのではなく、GUIモデリングツールで行われたことが原因である場合があります。
  3. 時々非常に反復的な何かをする必要があり、言語あなたがやっていることに応えるべきではありません君は。
  4. 時には非常に反復的なことをする必要があり、一般的な方法で言語に必要なものを取り込む方法はありません。言語の文法で表現できないか、それとも矛盾します。
  5. コンピューターの目標の1つは、人間の労力を削減することです。また、場合によっては二度と触れられない(保守の可能性が低い)コードは、10倍の時間で長いコードを生成するためにメタコードを書くことができます。2週間ではなく1日でそれを行うことができ、それが維持される可能性が低い場合、それを生成する方が良いでしょう-そして、5年後の誰かが実際にそれを維持する必要があるためいらいらする可能性があります必要に応じて2週間かけて完全に書き出すか、厄介なコードを1週間維持することに悩まされます(ただし、その時点ではまだ1週間先です)。それは、メンテナンスを行う必要がある場合です。 。
  6. 見落としている理由は他にもあると思います。

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

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

また、スプレッドシートにすべてのデータを保持することは、ある意味で優れていました。ソースコードファイルだけではデータを表現できませんでした。

例2

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

他にどのようにそれを行うべきでしたか?おそらく、別の方法を考え出すことができます。おそらく他の方法があります。しかし、その作業のために、他の方法は生成されたlex / parseコードよりも良くなかったでしょう。


2つのシステムに互換性はないが、非常に難解なスクリプト言語である種の安定したapiがあった場合、中間コードをファイル形式とデバッグトレースの一種として使用しました。手動で読むことを意図していませんでしたが、xmlがそうであったように同じ方法であったかもしれません。しかし、誰かが指摘したように、これはすべてのウェブページがこのように機能した後、youdが考えるよりも一般的です。
-joojaa

7

不足しているのは再利用です。

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

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

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

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

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


それでも、XMLのテキストベースの利点は、必要に応じて、人間が読み書きできることです(動作すると通常は気にしませんが、開発中は確かに行います)。パフォーマンスとスペース効率の観点から、一般的にバイナリ形式のほうがはるかに優れています(ただし、ボトルネックはどこか他の場所にあるため、ほとんど問題になりません)。
左辺

@leftaroundaboutそのパフォーマンスとスペース効率が必要な場合は、必ず。多くのアプリケーションが最近XMLベースの形式に移行した理由は、パフォーマンスとスペース効率がかつての最高の基準ではなく、履歴がバイナリファイル形式の維持がいかに不十分かを示しているためです。(古典的な例の古いMS Word文書!)ポイントは残っていますが、テキストはコンピューターが人間と同じように読むのに適しています。
グラハム

確かに、適切に設計されていないバイナリ形式は、実際にはテキスト形式で適切に考えられるよりも実際にパフォーマンスが低下する可能性があります。両方の世界のIMOは、代数データ型を介して人間が読める仕様を使用し、これらの型のASTから効率的なバイナリ表現を自動的に生成することです。たとえば、フラットライブラリを参照してください。
leftaroundabout

7

ソースコードとソースコードではなく、その理由に焦点を当てた、より実用的な答えです。ソースコードの生成は、このすべてのケースでビルドプロセスの一部であることに注意してください。そのため、生成されたファイルはソース管理への道を見つけるべきではありません。

相互運用性/シンプルさ

Googleのプロトコルバッファーを例にとると、単一の高レベルプロトコル記述を作成し、それを使用して複数の言語で実装を生成できます。多くの場合、システムの異なる部分は異なる言語で記述されます。

実装/技術的な理由

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

使いやすさ

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

編集

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


最初の理由から、OPのアイデアは、各言語での汎用的な実装(プロトコルバッファの説明を取得し、オンザワイヤ形式を解析/消費する)になることだと思います。なぜこれはコードを生成するよりも悪いのでしょうか?
パエロエベルマン

@PaŭloEbermannは、通常のパフォーマンス引数とは別に、このような一般的な解釈により、これらのメッセージをコンパイルされた(そしておそらくは解釈された)言語でファーストクラスオブジェクトとして使用することを不可能にします-たとえばC ++では、このようなインタープリターは必然的にキー値構造を返します。もちろん、そのkvをクラスに入れることができますが、多くの定型コードに変わる可能性があります。また、コード補完もあります。コンパイル時のチェック-コンパイラーはリテラルにタイプミスがないかどうかをチェックしません。
ヤンDorniak

私は同意します...答えにこれを追加できますか?
パエロエベルマン

@PaŭloEbermann完了
Jan Dorniak

6

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

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


3
また、より表現力のある言語のために、完全な、ネイティブオブジェクトオブジェクトコードコンパイラを作成する必要がある場合の回避策でもあります。Cを生成し、優れたオプティマイザーを備えたコンパイラーが残りを処理します。
-Blrfl

常にではない。バス上の信号などの定義を含む1つ以上のデータベースがある場合があります。次に、この情報をまとめて、整合性チェックを行い、バスからの信号とコード内にあると予想される変数との間のインターフェイスとなるコードを作成します。クライアントが提供するExcelシート、データベース、その他のデータソースを簡単に使用できるようにするメタプログラミングを備えた言語を表示でき、必要なコードを作成し、データの有効性と一貫性を確認する必要がある場合は、ぜひ見せてください。
CodeMonkey

@CodeMonkey:Ruby on RailsのActiveRecord実装のようなものが思い浮かびます。コード内でデータベーステーブルスキーマを複製する必要はありません。クラスをテーブルにマップし、列名をプロパティとして使用してビジネスロジックを記述するだけです。Rubyメタプログラミングでも管理できないコードジェネレーターによって生成されるパターンを想像することはできません。C ++テンプレートも、非常に強力ですが、少し難解です。Lispマクロは、もう1つの強力な言語内メタプログラミングシステムです。
ケビンクライン

@kevinclineとは、データベース自体ではなく、データベースの一部のデータに基づいたコード(それから構築できます)を意味していました。つまり、Excelの表Aで受信する信号に関する情報があります。これらの信号などに関する情報を含むデータベースBがあります。次に、これらの信号にアクセスするクラスが必要です。コードを実行するマシン上のデータベースまたはExcelシートへの接続はありません。単純なコードジェネレーターの代わりに、コンパイル時にこのコードを生成するために、本当に複雑なC ++テンプレートを使用します。codegenを選択します。
CodeMonkey

6

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

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


もちろん、ECMAScriptでコードを1回記述し、JVMのNashornまたはRhinoで実行することもできます。または、ECMAScriptでJVMを記述し(またはEmscriptenを使用してAvianをWebAssemblyにコンパイルして)、ブラウザでJavaコードを実行できます。私はそれらが素晴らしいアイデアだと言っているわけではありません(まあ、恐らく恐ろしいアイデアでしょう:-D)、しかし、少なくとも実行可能でなければ可能です。
ヨルグWミッターグ

理論的には可能ですが、一般的な解決策ではありません。ある言語を別の言語で実行できない場合はどうなりますか?たとえば、コード生成を使用して単純なNetlogoモデルを作成し、システムのインタラクティブなドキュメントを作成しました。これは、レコーダーとリプレーヤーと常に同期しています。そして一般的に、要件を作成してからコードを生成すると、意味的に同期して実行されるものが保持されます。
フリストヴリガゾフ

6

ここに何かが足りませんか?

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

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

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

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

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


はい、より良い方法を想像できます。HTMLは、テキストのみのWebブラウザーの迅速な作成を可能にするというTim Berners-Leeの決定の遺産です。当時はまったく問題ありませんでしたが、後知恵を利用して同じことを行うことはできませんでした。CSSは、さまざまなプレゼンテーション要素タイプ(DIV、SPAN、TABLE、ULなど)をすべて不要にしました。
ケビンクライン

@kevincline HTML自体に欠陥がないと言っているわけではありませんが、この場合、マークアップ言語(プログラムで生成可能)の導入が非常にうまくいったことを指摘していました。
ジュリス

したがって、HTML + CSSは単なるHTMLよりも優れています。HTML + CSS + MathJaxで直接作業したいくつかのプロジェクトの内部ドキュメントも作成しました。しかし、私が訪れるほとんどのWebページは、コードジェネレーターによって作成されたようです。
デビッドK

3

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

なぜなら、特に面倒で繰り返しの多いタスクの場合、コードを手動で書くよりも高速で簡単(エラーが発生しにくい)であるためです。また、高レベルのツールを使用して、1行のコードを記述する前に設計を検証および検証することもできます。

一般的な使用例:

  • RoseやVisual Paradigmなどのモデリングツール。
  • ERの組み込みSQLまたはコンパイル何かに前処理されなければならないインタフェース定義言語のようなレベルの言語。
  • flex / bisonのようなレクサーおよびパーサージェネレーター。

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


2

場合によっては、プログラミング言語に必要な機能がないだけで、必要な機能を実行する関数やマクロを書くことが実際には不可能になります。または、あなたやりたいことができるかもしれません、それを書くコードはcodeいでしょう。次に、単純なPythonスクリプト(または同様のスクリプト)を使用して、ビルドプロセスの一部として必要なコードを生成#includeし、実際のソースファイルに組み込むことができます。

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

デモ/サンプルソースコードが必要な場合は入手可能です。


1

テキスト形式は、人間が簡単に消費するために必要です。また、コンピューターはテキスト形式のコードを非常に簡単に処理します。したがって、生成されたコードは、コンピューターで最も簡単に生成および消費できる形式で生成される必要があります。これは非常に頻繁に読み取り可能なテキストです。

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


1

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

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

コードを1回だけ生成し、元のソースを破棄して、新しいソースを維持することもあります。

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

よくあるケースの1つは、これを行うためのコードジェネレーターを記述すると、実際にコードの90%しか正しく変換されない可能性があることです。その後、最後の10%を手作業で修正する必要があります。これは、手作業で100%翻訳するよりもはるかに高速です。

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

ウェブサイトのスクレイプなどからコードを一度だけ生成します。

密接に関連するのは、再度アクセスしたくないソースからコードを生成する場合です。たとえば、コードを生成するために必要なアクションが再現性がない、一貫性がない、またはそれらの実行に費用がかかる場合。現在、DataDeps.jlDataDepsGenerators.jlの 2つのプロジェクトに取り組んでい ます。

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

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

正しく生成されない場合があります。したがって、生成されたコードを使用する開発者は、それをチェックして修正する必要があります。オッズは、たとえばライセンス情報を間違えないようにしたいということです。

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

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


「レポート」は英語の単語で、「ポートアゲイン」以外のものを意味します。その文をより明確にするために「リポート」を試してください。(提案された編集には小さすぎるため、コメント。)
ピーターコーデス

@PeterCordesの言い回しは良い言い回しです。
リンドンホワイト

生成されたコードがどれほどひどいものであるかに応じて、高速ですが、潜在的保守性がはるかに低くなります。FortranからCへの移行は当時(Cコンパイラの方が広く利用されていたため、ユーザーはf2c+ を使用していましたcc)、結果のコードはCバージョンのプログラムAFAIKの良い出発点ではありませんでした。
ピーターコーデス

1
潜在的に、潜在的にそうではありません。一部のコードジェネレーターが保守不可能なコードを作成するのは、コードジェネレーターの概念の問題ではありません。特に、すべてのケースをキャッチする必要がない手作りのツールは、多くの場合、完全に素晴らしいコードを作成できます。たとえば、コードの90%が配列定数のリストにすぎない場合、それらの配列コンストラクターを1回限りとして生成することは、非常にうまく、少ない労力で行うことができます。(あることを意図していないので一方CythonによるCコード出力はあなたが言う同じように、人間によって維持することができません。。f2c日中バック)
リンドンホワイト

1
大きなテーブルは、最も単純な最も単純な引数です。同様のことは、たとえばforループまたは条件の変換についても言えます。確かsedに長い道のりがありますが、時にはもう少し表現力が必要な場合もあります。多くの場合、プログラムロジックとデータの境界線は良好です。区別が役に立たない場合があります。JSONは(/だった)単なるJavaScriptオブジェクトコンストラクターコードです。私の例では、オブジェクトコンストラクターコードも生成しています(データですか?多分(関数呼び出しがある場合はないかもしれません)。コードとして扱う方がいいですか?はい。)
リンドンホワイト

1

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

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

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

同様に、DSL(ドメイン固有言語)を使用することで、簡潔に記述できますが、特定のタスクに限定されたコードを作成できます。これにより、コードの正しい動作を生成するのが簡単になります。コードは手段であり終了であることに注意してください。開発者が探しているのは、動作を生成する効率的な方法です。

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

一方、生成されたコードのソースは(いつかためその維持または十分なユーザが間違ったツールを使用してのトラップに落ちて効率的でないコードを生成するために困難である場合ゴールデンハンマー)。


0

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

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

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

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


0

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

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

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

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


「Bjarne StroustrupがC ++を最初に実装したとき」の段落を削除したのはなぜですか?面白かったと思う。
-Utku

@Utkuその他の回答は、プロジェクトの残りの部分が完全に記述された、洗練された言語全体をコンパイルするという観点からこれをカバーしています。「コード生成」と呼ばれるものの大半を代表しているとは思わない。
カズ

0

私はこれがはっきりと述べられていなかった(私はそれが1つか2つの答えによって触れられたのを見たが、それはあまり明確に見えなかった)

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

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

せいぜい、生成されたコードを編集するだけで、多くの貧弱なコードがプロジェクトに引き込まれます(コードの完全なセットは、もはやソースコードであり、もはやデータではありません)。最悪の場合、プログラムに取り込まれたコードは冗長性が高く、名前が不適切なガベージであり、ほぼ完全に維持できません。

3番目のカテゴリは、1回使用するコード(guiジェネレーター?)であり、編集して開始/学習に役立つと思います。これは少しずつですが、開始するのに適した方法ですが、GUIジェネレーターは、プログラマーにとっては素晴らしい開始とはならない「生成可能な」コードを使用することを目標としています。さらに、冗長なソースコードをシステムに取り込むことを意味する2番目のGUIで再び使用したいと考えました。

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


0

コードとデータは両方とも情報です。

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

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

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

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

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


0

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

後でそのコードがデータであると規定しているので、命題は「何かを生成できるなら、そのことはコードではない」に帰着します。では、Cコンパイラによって生成されたアセンブリコードはコードではないと言いますか?手作業で書いたアセンブリコードと正確に一致した場合はどうなりますか?お望みならそこへ行くことはできますが、私はあなたと一緒にはいません。

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

それを考えると、ソースコード生成のこの考え全体は誤解ではないでしょうか?

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

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

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

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

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

また、「コードジェネレータ」が何を意味するのかを正確に考慮する必要があります。Cの前処理とC ++テンプレートのインスタンス化は、コード生成の演習であると考えています。これらに反対しますか?そうでない場合は、メンタル体操をいくつか実行して、それらを受け入れるがコード生成の他のフレーバーを拒否することを合理化する必要があると思います。

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

どうして?基本的には、ユーザーがデータをフィードするユニバーサルプログラムが必要であり、一部は「命令」に、その他は「入力」に分類され、計算を実行して「出力」と呼ばれるデータをさらに出力します。(特定の観点から、そのような汎用プログラムを「オペレーティングシステム」と呼ぶかもしれません。)プログラム?2つのプログラムには、異なる特性と異なる機能があります。

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

普遍的なものからある程度のインターフェースライブラリを持っていることは、必然的に良いことだと言います。おそらくそうなりますが、多くの場合、そのようなライブラリは大きくて記述や保守が難しく、さらには遅くなることさえあります。そして、実際にそのような獣が手元の特定の問題に対処するために存在しない場合、コード生成アプローチが問題をはるかに迅速かつ簡単に解決できる場合、誰が作成することを主張しますか?

ここに何かが足りませんか?

いくつかのことを考えます。

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

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

しかし、私はすでにそれに触れました。特定のタスクの明快さと表現力に一部基づいて、特定のタスクの言語を選択します。コードが小さいほど平均してバグが少なく、保守が容易であるため、少なくとも大規模な作業では、高レベルの言語へのバイアスもあります。しかし、複雑なプログラムには多くのタスクが関係しており、多くの場合、ある言語でより効果的に対処できるものもあれば、別の言語でより効果的または簡潔に対処できるものもあります。ジョブに適切なツールを使用することは、コード生成を使用することを意味する場合があります。


0

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

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

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

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

私も正反対を行いました。「コードを生成しない」という任務を持った、もう1つの仕事があります。それでも、ソフトウェアを使用している人の生活を楽にしたかったので、大量のテンプレートメタプログラミングを使用して、コンパイラがその場でコードを生成できるようにしました。したがって、私は自分の仕事をするために汎用C ++言語のみが必要でした。

ただし、キャッチがあります。エラーが読み取り可能であることを保証することは非常に困難でした。以前にテンプレートメタプログラミングコードを使用したことがある場合、1回の無害なミスが、何が間違っているかを理解するために100行の理解できないクラス名とテンプレート引数を必要とするエラーを生成することがあります。この効果は非常に顕著であったため、構文エラーの推奨されるデバッグプロセスは、「自分のファイルの1つにエラーが発生するまで、エラーログをスクロールします。間違っていました。」

コード生成を使用していた場合、人間が読み取れるエラーを含む、はるかに強力なエラー処理機能を使用できたはずです。C'est la vie。


0

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

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

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

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

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

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

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


0

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


-3

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

間違った方法で取得しました。読むべき

何かがインタープリタブルのジェネレーターに供給できる場合、それはデータではなくコードです。

これはそのコンパイル段階のソース形式であり、シンク形式はまだコードです。


1
ソースコードの間違った定義。ソースコードは、主に人間が作業するためのものです(そして、事実だけでソースコードが定義されています。FSFのフリーソフトウェアも参照してください)。で生成されたアセンブラコードgcc -fverbose-asm -O -Sは、ソースコードではなく(また、データだけではないか、ほとんどがデータです)、たとえそれが常にGNUにフィードされas、時には人間によって読まれるテキスト形式であってもです。
バジルスタリンケビッチ

また、多くの言語実装はCコードコンパイルさますが、生成されたCは真のソースコードではありません(たとえば、人間が簡単に処理することはできません)。
バジルスタリンケビッチ

最後に、ハードウェア(たとえば、AMDまたはIntelチップ、またはコンピューターのマザーボード)がマシンコード(明らかにソースコードではない)を解釈しています。ところで、IBM1620にはキーボード入力可能(BCD)マシンコードがありましたが、その事実から「ソースコード」にはなりませんでした。すべてのコードはソースではありません
バジルスタリンケビッチ

@BasileStarynkevitchああ、あなたはそこに着いた。私は機知に富んだ声明を圧縮しすぎたり、意味を変えたりしないでください。右、ソースコードは、最初のコンパイル段階に送られる最もオリジナルなコードでなければなりません。
ベルギ

ソースコードは人間向けのコードではありません。音楽(音)と定義することは、困難で主観的です。それを消費するソフトウェアを見つけようとすることは問題ではありません。
バジルスタリンケビッチ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.