web-dev-qa-db-ja.com

C ++:バイナリレベルでの標準化の欠如

ISO/ANSIがC++をバイナリレベルで標準化しなかったのはなぜですか? C++には多くの移植性の問題がありますが、これはバイナリレベルでの標準化が欠けているためです。

ドンボックス 書き込み、(彼の本からの引用 エッセンシャルCOM 、チャプターCOM As A Better C++

C++と移植性


C++クラスをDLLとして配布する決定が下されると、1つはC++の基本的な弱点、つまりバイナリレベル。 ISO/ANSI C++草案ワーキングペーパーは、コンパイルするプログラムとそれらを実行することの意味上の影響を体系化しようとしますが、C++のバイナリランタイムモデルを標準化しようとするものではありません。この問題が初めて明らかになるのは、クライアントがC++開発環境からのFastString DLLのインポートライブラリにリンクしようとしたとき/ FastString DLLのビルドに使用されたもの。

より多くの利点がありますか、それともバイナリ標準化の欠如の損失ですか?

14
Nawaz

バイナリ互換のコンパイル済み形式の言語は、比較的新しいフェーズです[*]。たとえば、JVMおよび.NETランタイムです。 CおよびC++コンパイラは通常、ネイティブコードを生成します。

利点は、JIT、バイトコードインタープリター、VM、またはその他のそのようなものを必要としないことです。たとえば、マシンがネイティブにbootstrapバイトコードを実行できる場合を除いて、マシンの起動時に実行されるJavaコードを、移植性の高いJavaバイトコードとして記述することはできません。 、またはJavaから非バイナリ互換のネイティブ実行可能コードへのある種のコンバーターがあります(理論的には、これがbootstrapコードで実際に推奨できるかどうかは不明です)。ソースレベルでもportableC++ではありませんが、魔法のハードウェアアドレスを大量に処理するため、C++で記述できます。

欠点はもちろん、ネイティブコードはコンパイルされたアーキテクチャ上でのみ実行され、実行可能ファイルは、その実行可能形式を理解し、同じアーキテクチャの他の実行可能ファイルとのみリンクして呼び出すローダーによってのみロードできることですおよびABI。

それでも、2つの実行可能ファイルをリンクすると、実際には正しく機能するだけです。(a)1つの定義ルールに違反していない場合、異なるコンパイラ/オプション/何を使用してコンパイルされていても簡単に実行できます。同じクラスの異なる定義を使用していた(ヘッダー内、またはそれぞれが異なる実装に対して静的にリンクされているため) (b)構造レイアウトなど、関連するすべての実装の詳細は、それぞれがコンパイルされたときに有効なコンパイラオプションに従って同じです。

C++標準でこれをすべて定義すると、現在実装者が利用できる多くの自由が取り除かれます。特にC++(および同じ問題のあるC)で非常に低レベルのコードを記述する場合、実装者はこれらの自由を使用しています

バイナリポータブルターゲット用にC++に少し似たものを書きたい場合は、.NETを対象とするC++/CLIとMonoがあり、Windows以外の場所で.NETを(うまくいけば)実行できます。 MSのコンパイラを説得して、Monoで実行される純粋なCILアセンブリを作成することは可能だと思います。

また、たとえばLLVMを使用してバイナリポータブルCまたはC++環境を作成できる可能性のあるものもあります。とはいえ、広範囲にわたる例が浮上したことはわかりません。

しかし、これらはすべて、C++が実装に依存すること(タイプのサイズなど)の多くを修正することに依存しています。次に、ポータブルバイナリを理解する環境が、コードを実行するシステムで使用できる必要があります。移植性のないバイナリを許可することにより、CおよびC++はポータブルバイナリができない場所に行くことができます。そのため、標準はバイナリについて何も述べていません。

次に、特定のプラットフォームでの実装では通常、stillはオプションの異なるセット間のバイナリ互換性を提供しませんが、標準はそれらを停止しません。 Don Boxが嫌いな場合、コンパイラオプションによると、Microsoftのコンパイラが同じソースから互換性のないバイナリを生成できる場合、それは彼が不平を言う必要があるコンパイラチームです。 C++言語は、コンパイラーまたはOSが必要な詳細をすべて禁止しないので、Windowsに限定すると、C++の根本的な問題にはなりません。 Microsoftは選択しないようにしています。

違いは、多くの場合、間違ってプログラムをクラッシュさせる可能性のあるもう1つのこととして現れますが、たとえば、互換性のないデバッグバージョンとdllのリリースバージョンとの間で効率が大幅に向上する可能性があります。

[*]アイデアが最初に発明された時期はおそらく1642年か何かはわかりませんが、現在の人気は、C++がバイナリの移植性の定義を妨げる設計上の決定にコミットしたときと比較すると、比較的新しいものです。

16
Steve Jessop

クロスプラットフォームおよびクロスコンパイラーの互換性は、CおよびC++の主な目的ではありませんでした。それらは時代に生まれ、時間と空間のプラットフォーム固有とコンパイラ固有の最小化が重要である目的のために意図されました。

Stroustrupの「C++のデザインと進化」から:

「明示的な目的は、ランタイム、コードのコンパクト性、およびデータのコンパクト性の点でCと一致させることでした。..理想は、達成されたものですが、クラスを使用するCは、どんなCにも使用できるということです。」

7
Andy Thomas

これはバグではなく、機能です。これにより、実装者はバイナリレベルで実装を最適化する自由が得られます。リトルエンディアンのi386とその子孫だけが、存在するまたは存在するCPUではありません。

6
Raedwald

引用に記載されている問題は、シンボル名のマングリング方式の標準化を意図的に回避したために発生します(「バイナリレベルでの標準化」はこの問題はコンパイラーのApplication Binary Interface(ABI)に関連していますが、この点で誤解を招く表現です。

C++は、関数またはデータオブジェクトのシグネチャと型情報、およびそのクラス/名前空間のメンバーシップをシンボル名にエンコードします。異なるコンパイラーは、異なるスキームを使用できます。その結果、静的ライブラリ、DLL、またはオブジェクトファイル内のシンボルは、別のコンパイラ(または同じコンパイラの別のバージョン)を使用してコンパイルされたコードとリンクしません。

この問題は、おそらく私ができるよりも here よりも適切に説明および説明されており、さまざまなコンパイラで使用されているスキームの例が示されています。

標準化が意図的に欠如している理由についても説明します here

6
Clifford

ISO/ANSIの目的は、C++言語を標準化することでした。この問題は、言語標準とコンパイラサポートの更新にyearsを必要とするほど複雑であるようです。

バイナリは異なるCPUのアーキテクチャと異なるOS環境で実行する必要があるため、バイナリ互換性ははるかに複雑です。

3
dirac3000

Andyが言ったように、クロスプラットフォームの互換性は大きな目標ではありませんでしたが、幅広いプラットフォームとハードウェアの実装が目標でした。バイナリ標準化は、これを実質的に達成不可能にするでしょう。

Cの互換性も重要であり、これは非常に複雑になります。

その後、実装のサブセットに対して ABIを標準化する への取り組みが行われました。

2
Flexo

C++には多くの移植性の問題がありますが、これはバイナリレベルでの標準化が欠けているためです。

こんなに簡単なことではないと思います。提供された回答は、標準化への焦点の欠如に関して優れた理論的根拠をすでに提供していますが、C++は、真に適している言語の多すぎるかもしれませんABI標準としてCと競合します。

関数のオーバーロード、vtableの非互換性、モジュールの境界を越えてスローされる例外との非互換性などに起因する名前のマングリングに入ることができます。これらはすべて本当に苦痛であり、少なくともvtableレイアウトを標準化できればいいのですが。

しかし、ABI標準は、あるコンパイラで作成されたC++ dylibを、別のコンパイラで構築された別のバイナリで使用できるようにするだけのものではありません。 ABIが使用されます言語間。彼らが少なくとも最初の部分をカバーできればいいのですが、最も広く互換性のあるdylibを作成するために非常に重要な、普遍的なABIレベルでC++がCと本当に競合することはありません。

次のようにエクスポートされた単純な関数のペアを想像してください:

void f(Foo foo);
void f(Bar bar, int val);

...と想像してくださいFooBarは、パラメーター化されたコンストラクター、コピーコンストラクター、移動コンストラクター、および重要なデストラクタを持つクラスでした。

次に、Python/Lua/C#/ Java/Haskell/etcのシナリオを取り上げます。このモジュールをインポートして自分の言語で使用しようとする開発者。

最初に、関数のオーバーロードを利用してシンボルをエクスポートする方法の名前変換標準が必要です。これは簡単な部分です。それでも、名前を「マングリング」にするべきではありません。 dylibのユーザーはシンボルを名前で検索する必要があるため、ここでのオーバーロードは完全な混乱のように見えない名前につながるはずです。シンボル名は"f_Foo""f_Bar_int"またはそのようなもの。開発者が実際に定義した名前と競合しないようにする必要があります。おそらく、ABIで使用するためにいくつかの記号/文字/規則を予約しています。

しかし、今はより厳しいシナリオです。 Python開発者は、たとえば、移動コンストラクタ、コピーコンストラクタ、デストラクタをどのように呼び出しますか?おそらく、これらをdylibの一部としてエクスポートできます。しかし、FooBarは異なるモジュールでエクスポートされますか?このdylibに関連付けられているシンボルと実装を複製する必要がありますか?そうしないと、複数の問題に巻き込まれ始めて、本当に面倒になる可能性があります。 dylibは、ここでオブジェクトを作成し、ここに渡し、そこにコピーし、それを破棄するためだけにインターフェースします。同じ基本的な懸念がCにもいくらか当てはまる可能性がありますが(手動で/明示的に)、Cは本来、これを回避する傾向があります人々はそれでプログラムします。

これはぎこちなさのほんの一部です。上記のf関数の1つがJavaScriptにBazException(コンストラクタとデストラクタを持ち、std :: exceptionを派生するC++クラスでもある)をスローするとどうなりますか?

せいぜい、あるC++コンパイラによって生成された1つのバイナリから、別のC++コンパイラによって生成された別のバイナリに機能するABIを標準化することしか期待できないと思います。もちろんそれは素晴らしいことですが、私はこれを指摘したかっただけです。一般に、クロスコンパイラーで機能する汎用ライブラリーを配布するためにそのような懸念を伴うことは、実際に汎用化され、互換性のあるクロス言語にすることを望む場合もよくあります。

推奨ソリューション

COMスタイルのインターフェイスでAPI/ABIのC++インターフェイスを何年も使用する方法を探すのに苦労した後の私の提案する解決策は、単に "C/C++"(しゃれ)開発者になることです。

Cを使用して、実装用のC++でこれらのユニバーサルABIを作成します。ヒープ上のそのようなオブジェクトを作成および破棄する明示的な関数を使用して、不透明なC++クラスへのポインターを返すエクスポート関数などの処理を実行できます。実装に完全にC++を使用している場合でも、ABIの観点からそのCの美学に恋するようにしてください。抽象インターフェースは、関数ポインターのテーブルを使用してモデル化できます。これをC APIにラップするのは面倒ですが、付属のディストリビューションの利点と互換性は、非常に価値のあるものになる傾向があります。

次に、このインターフェイスをあまり直接使用したくない場合は(少なくともRAIIの理由で、おそらく使用しないほうがよいでしょう)、SDKに付属する静的にリンクされたC++ライブラリに必要なすべてをラップできます。 C++クライアントはそれを使用できます。

これらのpythoniqueを作成する方法がないため、PythonクライアントはCまたはC++インターフェースを直接使用することを望まないでしょう。彼らはそれを独自のpythoniqueインターフェースにラップしたいので、できる限り簡単にするために最小限のC API/ABIをエクスポートしているだけで実際に良いことです。

多くのC++業界は、COMスタイルのインターフェースなどを頑固に出荷しようとするよりも、これを行うほうがより利益を得ると思います。また、これらのdylibのユーザーが厄介なABIに煩わされる必要がないため、私たちのすべての生活が楽になります。 Cはそれを単純にし、ABIの観点からは単純であるため、あらゆる種類のFFIに対して自然に、最小限で機能するAPI/ABIを作成できます。

1
user204677

C++の標準がないことは、分離されたモジュール式プログラミングの今日の世界の問題だと思います。しかし、私たちはそのような標準から私たちが望むものを定義する必要があります。

バイナリーの実装やプラットフォームを定義することを望んでいる人はいません。したがって、x86 Windows dllを取得して、x86_64 Linuxプラットフォームで使用することはできません。それは少し多いでしょう。

しかし、人々が望んでいるのは、Cモジュールで持っているのと同じことです-バイナリレベルでの標準化されたインターフェース(つまり、コンパイルされた後)。現在、モジュラーアプリにDLLをロードする場合は、C関数をエクスポートして、実行時にそれらにバインドします。 C++モジュールではそれはできません。できればすばらしいでしょう。これは、あるコンパイラで作成されたdllが別のコンパイラによってロードされる可能性があることも意味します。もちろん、互換性のないプラットフォーム用にビルドされたdllをロードすることはできませんが、修正する必要のある問題ではありません。

したがって、標準化団体がモジュールが公開するインターフェイスを定義した場合、C++モジュールをロードする際の柔軟性が大幅に向上し、C++コードをCコードとして公開する必要がなくなり、おそらくより多くの使用が可能になります。スクリプト言語でのC++.

また、この問題の解決策を提供しようとするCOMなどの問題に苦しむ必要もありません。

1
gbjbaanb