【解決方法】C++ インタプリタがなぜこれほど少ないのか


私は個人的に、Root で使用される C++ インタプリタを 1 つだけ知っています (CINT、CERN で開発)。 なぜこれほど少ないのか不思議に思いましたが、明らかな答えは、C++ は解析が非常に複雑な言語であるということです。 正気の人間なら、わざわざ C++ パーサーを最初から作成する人はいないでしょう…

ただし、コンパイラの一部として C++ パーサーが存在しており、その一部はオープン ソースです。 パーサーによって出力されるアセンブリ コードを外出先でも解釈できるかもしれないと思いつきました。 これは、仮想マシンによって解釈されるバイトコードを出力する JIT コンパイラに似ています。 思い浮かぶ問題の 1 つは、異なるソース ファイルを使用することです。そうしないと、リンクする必要がある複数のオブジェクト ファイルが必要になります。 これは確かに制限ですが、私に言わせれば、それほど深刻な制限ではありません。

それでも、これを実行した人は誰もいないようで、これは通常、それが思っているほど簡単ではないことを意味します(私のアイデアが簡単であるというわけではありませんが、それでも)。 私のアイデアにはどんな問題があるだろうかと考えていました…誰かコメントしてくれる人はいますか?

解決策 1

ブリング (C++/CLI に関する良いアイデア) と私自身による質問に対するコメントを参照してください。

実際のところ、CINT は C++ インタプリタではありません。 CINT は C++ インタプリタではありません。 これはここで明確に述べられています:

[^]。

この言語は、C および C++ をベースにした簡略化された言語です。

以下も参照してください。 http://root.cern.ch/drupal/content/cling[^]。

上記で参照した Wikipedia の記事で言及されている代替手段の 1 つである「Ch」はどうでしょうか。 これは正確には C++ インタープリターではなく、何らかの特殊な言語のインタープリターです。
http://en.wikipedia.org/wiki/Ch_%28computer_programming%29[^]、

[^]。

他に何か? 「Pike」も C と C++ のみに基づいています (Wikipedia には「影響を受けた」とさえ書かれています):
http://en.wikipedia.org/wiki/Pike_%28programming_lang%29[^]、

[^]。

「本物の」C++ 言語については、インタプリタには適していないと考える人もいます。 このトピックは複雑すぎて、私の考察をこの記事で説明することはできません。 素早い 質問と回答。 ここでそれを証明することはできませんし、証明できると思いがちなので、100% 確実ではありません。 誰かが私が間違っていると証明したら、私は非常に驚くでしょう。

-SA

解決策 3

引用:

私は個人的に、Root で使用される C++ インタプリタを 1 つだけ知っています (CINT、CERN で開発)。 なぜこれほど少ないのか不思議に思いましたが、明らかな答えは、C++ は解析が非常に複雑な言語であるということです。 正気の人間なら、わざわざ C++ パーサーを最初から作成する人はいないでしょう…

ここにはいくつかの重要な質問があります。あなたの目標は何ですか、そしてなぜ C/C++ インタプリタがその問題に対する最良の (少なくとも適度に優れた) 解決策となるのですか? “あなたの目標は何ですか?” 「なぜ X が良い解決策なのか?」 一般に、解決策の実装で貴重な時間を無駄にする前に、最初に尋ねるのに非常に適した質問です。

C/C++ は、さまざまな理由から、コンソールベースのインタープリタ型言語としては実用的ではありません。また、人々は通常、実用的ではないツールの実装に時間を浪費することを好みません (おそらく、楽しんで学習する時間がたくさんある人や、非現実的な側面を誰が予測できないのか)。

動的に型付けされた変数と「動的にリンクされた関数」を使用した動的言語 (Python など) のインタープリターの実装は、C/C++ で同じことを行うよりも桁違いに簡単で、エラーが発生しにくくなります。 シンプルな動的言語のパーサー、コンパイラー、インタープリター全体を記述するのは非常に簡単かつ迅速です。 インタプリタ自体は、通常、高レベルのロジック、つまりコードを実行するためにのみ使用されるため、それほど高性能である必要はありません。 パフォーマンスが重要な場合は、それをインタプリタのネイティブ C/C++ モジュールとして実装し、動的に解釈される言語にバインドできます。 私の意見では、あなたは C/C++ インタプリタで発生する問題についてあまり考えていないのではないかと思います。 彼らについての小説が書けるかもしれません。 以下に、十分に大きな、最も興味深い問題を示します。

C/C++ には前方宣言が必要です。 実際のインタープリタ型言語には、前方宣言や分離された宣言/定義はありません。 しかし、これはより深刻な問題のほんの一部にすぎません。C/C++ はインタプリタ型言語として使用するには非現実的に冗長です。 これは、言語設計の問題と、インタープリター言語としての実際のユーザビリティの問題の両方を含む一連の問題です。 Python では、存在しない Y 関数を呼び出す X 関数を簡単に定義でき、後でこの Y 関数を定義することもできます。 その後、X を実行できます。後で必要に応じて、Y の定義を完全に変更できます (おそらく、以前の定義との下位互換性を保つために、デフォルトで初期化されたいくつかの新しいパラメーターを使用します)。X を再度実行すると、すぐに新しい Y 定義が使用されます。 まだ定義されていない Z 関数を呼び出す場合でも X を実行できます。実際の入力パラメーターを使用した X の実行が実際に Z を呼び出すブランチに到達しない場合は問題ありません。 C++ インタプリタ 以前に定義された多くの関数 (たとえば、インライン関数など) によって使用される非常に基本的なテンプレート (例: 動的配列) の定義を変更するとどうなりますか? 新しいインライン関数を使用するためにインタープリター内で再コンパイルが必要なコードブロックをどのように見つけますか? これらの関数の一部の再コンパイルが新しい定義で失敗した場合はどうなりますか? Python のような動的言語では、このような深刻な問題は発生しません。 非動的インタプリタでこのようなシナリオを扱うことは大きな問題になります。これらは 2 つの「単純/基本」問題にすぎず、これは氷山の一角にすぎません。 私は 10 年以上 C/C++ をプログラミングしてきましたが、C/C++ インタプリタの必要性を感じたことはありませんでした。

引用:

ただし、コンパイラの一部として C++ パーサーが存在しており、その一部はオープン ソースです。 パーサーによって出力されるアセンブリ コードを外出先でも解釈できるかもしれないと思いつきました。 これは、仮想マシンによって解釈されるバイトコードを出力する JIT コンパイラに似ています。 思い浮かぶ問題の 1 つは、異なるソース ファイルを使用することです。そうしないと、リンクする必要がある複数のオブジェクト ファイルが必要になります。 これは確かに制限ですが、私に言わせれば、それほど深刻な制限ではありません。

答えを書き始めましたが、問題をさまざまな側面から分析するため、長くて複雑になりすぎました。 その代わりに、私の結論の一部をここに書きます。

前の段落で投稿した質問に答えてください。 あなたの質問から、おそらくあなたの目標は単に作成することだと思います 最小限の作業で C++ インタープリター – 既存のコンパイラーからのもの (コンパイル ユニット、オブジェクト ファイル) を再利用したいため、これを想定しています。 これに関する私の問題は、人々が好きではないことをしなければならないときに、「最小限の労力」で何かを行うことです。 一方で、趣味として楽しむためだけに、または特定の目的を持って何かをしている場合は、多くの場合、多くの仕事をすることを気にしません。 C/C++ インタープリターの場合、すべての解決策には多くの作業が必要になるようです。

コンパイラとコンパイラ設定によっては、オブジェクト ファイルに必要なもの以外のものが含まれる場合があります。 これらは標準化されておらず、同じコンパイラであっても、いくつかの設定 (LTCG など) によってはまったく役に立たないゴミがそれらに挿入される可能性があります。 IMO では、オブジェクト ファイルから解析するのではなく、必要なものを出力するバックエンドを作成する方が現実的です。

解決策 4

私の推測的な理由は、それを行う努力に比べて、その必要性を感じている人は誰もいなかったようです。 つまり、スペース、材料、人材、十分な土地などがあり、自分の楽しみのためにそれを行うことができると仮定すると、庭にエッフェル塔を建てることができるかもしれません。 😉

C と C++ の違いは、C++ 標準に記載されているとおりです。

C によって提供される機能に加えて、C++ は追加のデータ型、クラス、テンプレート、例外、名前空間、演算子のオーバーロード、関数名のオーバーロード、参照、フリー ストア管理演算子、および追加のライブラリ機能を提供します。

C++11 に移行する場合は、ラムダ式と他のいくつかの部分を追加する可能性があります。これらはすべて、主にパーサー関連であり、(面倒ではないにしても) 他の手段でも実行できるものを抽象化するという意味で、主に構文糖衣を使用します。

上記の機能を解析した結果のランタイム項目は次のとおりです。
– 追加のデータ型: いくつかの組み込みの文字/数値型
– クラス: 拡張インスタンス関数 this パラメータ
– クラス: 派生クラスのサブオブジェクトとして基本クラスを使用した継承
– クラス: 特別な暗黙的な関数呼び出し (ctor、dtor など)
– クラス: 仮想関数呼び出し用の vtable
– クラス: クラスでは定義の順序は (ほとんど) 関係ありません – 名前の検索のみが影響を受けます
– テンプレート: 型または定数整数値に基づいて生成されたクラスまたは関数
– 例外: 追加の制御フロー、特にクリーンアップ簿記コード
– 名前空間: 「糖衣構文」 (固有の型、オブジェクト、関数が定義され、呼び出されます)
– オーバーロード: 「構文糖」 (パーサーは正しい関数への呼び出しを作成します)
– 参照: 「構文糖」、ポインターが生成されるか、実行時コードがまったく生成されません。
– 新規/削除: 低レベルのメモリ malloc/free および ctor/dtor 呼び出し
– ライブラリ: 言語機能に基づくアイテムのコレクション
– ラムダ式: 「糖衣構文」(暗黙的なファンクタークラス)
– …

克服すべき厄介な細部の問題はありますが、技術的には、これは C を大幅に拡張したものとみなすことができます。

したがって、機能的に同等の C に変換できる C++ フロントエンドがある場合は、CINT のレベルに到達したことになります。 複数のファイルやライブラリやオブジェクト ファイルの参照などの問題は C++ に特有のものではなく、すべての C* 言語に共通の事実です。 したがって、これらの言語のインタプリタ (CINT?) の概念を再利用できます。

しかし、繰り返しますが、努力を正当化するかどうかは、将来の「利益」に大きく依存します。

乾杯
そして私

解決策 5

C++ は大規模なプログラミング言語だからです。 あまりにも複雑すぎます。 インタプリタは、Python や JavaScript などの言語用に作成されます。インタプリタは単純であるだけでなく、解釈されることを目的としており、動的です。 最初の Python 実装はインタープリターでした。 コンパイラではありません。 理由はこれだけではありません。 C++ は、「コンパイル」時にインタープリタ型言語よりも高速であることがすでに知られています。 ただし、C++ インタープリターに常に同じことを期待できるわけではありません。 C++ はコンテキストに依存します。 C++ は巨大です。 C++ にはジェネリックスが含まれています。 したがって、言語の複雑さがソース コードの処理速度を遅くする原因となります。 たとえば、C++ のコンパイル速度を参照してください。 ソースを解釈するとどうなるでしょうか? そうは言っても、古典的な「hello world」プログラムにはそれほど時間はかからないかもしれません。 しかし、一般的なテクニックを多用するプログラムについて考えてみてはいかがでしょうか?

しかし、CINT、Ch. のようなインタープリターはまだ存在します。 次のページを参照してください。

[^]

解決策 2

この質問を本当に「解決」するのは難しいです – フォーラムの方が良かったかもしれませんが、これが私の 2c です。

言語定義によってインタープリターの作成が実際に妨げられるわけではありませんが、C++ は実際には、非常に効率的なコードに使用できる低レベル言語として設計されました。 この言語は非常に複雑ですが、解析が不可能ではありません (私の知る限り、実際には GLR パーサーまたは手書きのパーサーが必要です)。 そうは言っても、C++ の利点のほとんどは、マシンの近くで実行される効率的なコードに C++ を使用できるという事実から来ています。 インタプリタは通常、実行速度よりも開発中の迅速な反復が重要な高レベルのタスクに使用されます。

一言で言えば、とんでもなく大変な作業で、犬のように走り続けることになる。

コメント

タイトルとURLをコピーしました