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


14

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

Don Boxが書いています(彼の本Essential COMのCOM As A Better C ++から引用)

C ++と移植性


C ++クラスをDLLとして配布することを決定すると、C ++ の基本的な弱点の 1つ、つまりバイナリレベルでの標準化の欠如に直面します。ISO / ANSI C ++ドラフトワーキングペーパーは、どのプログラムをコンパイルし、それらを実行することのセマンティック効果をコード化しようとしますが、C ++のバイナリランタイムモデルの標準化は試みません。この問題が初めて明らかになるのは、クライアントがFastString DLLのビルドに使用した環境以外の C ++開発環境からFastString DLLのインポートライブラリにリンクしようとしたときです。

このようなバイナリ標準化の欠如により多くの利点または損失がありますか?


これは、それがより主観的な質問であるとみなして、programmers.stackexchange.comでよりよく尋ねられますか?
スティーブンフルラニ

1
実際に私の関連質問:stackoverflow.com/questions/2083060/...
アラク

4
ドンボックスは熱狂的です。彼を無視してください。
ジョンダイブリング

8
Cは、バイナリレベルでもANSI / ISOによって標準化されていません。大藤Cは持っているデファクトスタンダードABIではなく、デジュールものを。C ++には、そのような標準化されたABIがありません。メーカーによって実装の目標が異なるためです。たとえば、Windows SEH上のVC ++ピギーバックの例外。POSIXにはSEHがないため、そのモデルを使用しても意味がありません(したがって、G ++およびMinGWはそのモデルを使用しません)。
ビリーONeal

3
これは弱点ではなく機能だと考えています。実装を特定のABIにバインドすると、イノベーションが発生することはなく、新しいハードウェアは言語の設計にバインドされます(ハードウェア業界では長い間、各新しいバージョンの間に15年あります)。コードをより効率的に実行するための新しいアイデアを革新することはできません。代償として、実行可能ファイル内のすべてのコードは同じコンパイラ/バージョンでビルドする必要があります(主要な問題ではなく問題です)。

回答:


16

バイナリ互換のコンパイルされた形式の言語は、JVMや.NETランタイムなどの比較的新しいフェーズ[*]です。CおよびC ++コンパイラは通常、ネイティブコードを出力します。

利点は、JIT、バイトコードインタープリター、VM、またはその他のそのようなものが必要ないことです。たとえば、マシンがネイティブにJavaバイトコードを実行できる場合、またはJavaから非バイナリ互換のネイティブへの何らかのコンバーターがない限り、マシンの起動時に実行されるブートストラップコードを移植性の高いJavaバイトコードとして作成することはできません実行可能コード(理論的には、これが実際にブートストラップコードに推奨されるかどうかはわかりません)。ソースレベルでも移植性のある C ++ではありませんが、マジックハードウェアアドレスをいじり回すことが多いため、C ++で多かれ少なかれ書くことができます。

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

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

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

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

LLVMなどを使用してバイナリポータブルのCまたはC ++環境を作成できる可能性もあります。しかし、広範な例が出てきたことは知りません。

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

その後、任意のプラットフォーム上で、実装は通常、まだ標準がそれらを停止されていないが、オプションの異なるセット間のバイナリ互換性を提供していません。コンパイラオプションに従って、Microsoftのコンパイラが同じソースから互換性のないバイナリを生成できることをDon Boxが気に入らない場合は、文句を言う必要があるコンパイラチームです。C ++言語は、コンパイラまたはOSが必要な詳細をすべて特定することを禁止していません。そのため、Windowsに制限すれば、C ++の根本的な問題にはなりません。マイクロソフトはそうしないことを選択しました。

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

[*]アイデアが最初に発明されたのはいつか、おそらく1642年かどうかはわかりませんが、C ++がバイナリポータビリティの定義を妨げる設計決定にコミットしたときと比較して、現在の人気は比較的新しいです。


@Steveしかし、Cにはi386とAMD64のABIが明確に定義されているため、GCCバージョンXでコンパイルされた関数へのポインターをMSVCバージョンYでコンパイルされた関数に渡すことができます。C++関数でそれを行うことはできません。
ユーザー877329

7

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

Stroustrupの「C ++の設計と進化」から:

「明示的な目的は、実行時、コードのコンパクト化、データのコンパクト化の点でCに一致させることでした。...達成された理想は、Cが使用できるあらゆるものに使用できることでした。」


1
+1-正確に。ARMとIntelの両方のボックスで動作する標準のABIをどのように構築しますか?意味がわからない!
ビリーONeal

1
残念ながら、これで失敗しました。実行時にC ++モジュールを動的にロードすることを除き、Cが行うすべてのことを実行できます。公開されたインターフェイスでC関数を使用するように「戻す」必要があります。
gbjbaanb

6

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


6

見積書に記載された問題は、私は「考えるシンボル名前の符号化スキームの標準化(のかなり意図的な回避によって引き起こされるバイナリレベルでの標準化は問題がコンパイラのに関連しているものの、」この点で誤解を招くというフレーズであるアプリケーションバイナリインタフェース( ABI)。

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

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

標準化の故意の欠如の理由もここで説明されます


3

ISO / ANSIの目的は、C ++言語を標準化することでした。この問題は、言語標準とコンパイラサポートの更新に何年もかかるほど複雑だと思われます。

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


確かに、引用に記載されている問題は、実際には「バイナリレベルの互換性」とは関係ありません(作成者がこの用語を使用しているにもかかわらず)。実際、彼は互換性のない名前マングリングスキームの問題について説明しています。

@Clifford:名前マングリングスキームは、バイナリレベルの互換性のサブセットにすぎません。後者はより包括的な用語のようなものです!
ナワズ

WindowsマシンでLinuxバイナリを実行しようとすると問題があるとは思いません。プラットフォームごとにABIがあれば、少なくともスクリプト言語が同じプラットフォームでバイナリを動的にロードして実行したり、アプリが異なるコンパイラーでビルドされたコンポーネントを使用したりできるため、状況はずっと良くなります。今日、LinuxでC dllを使用することはできず、誰も文句を言うことはありませんが、そのC dllは、利益が得られるpythonアプリでロードできます。
-gbjbaanb

2

Andyが述べたように、クロスプラットフォームの互換性は大きな目標ではありませんでしたが、幅広いプラットフォームとハードウェアの実装が目標であり、最終的には非常に幅広いシステムの選択に適合する実装を書くことができます。バイナリの標準化により、これは事実上達成できませんでした。

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

その後、実装のサブセット用にABI標準化するための努力がいくつか行われました。


くそー、私はCの互換性を忘れていました。良い点、+ 1!
アンディトーマス

1

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

彼らの正しい考えの誰も、バイナリの実装またはプラットフォームを定義することを望みません。したがって、x86 Windows dllをx86_64 Linuxプラットフォームで使用し始めることはできません。それは少し多くなります。

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

したがって、標準化団体がモジュールが公開するインターフェイスを定義していれば、C ++モジュールをロードする際の柔軟性がはるかに高くなり、C ++コードをCコードとして公開する必要がなくなります。スクリプト言語でのC ++の。

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


1
+1。ええ、私は同意します。ここでの他の答えは、基本的に、バイナリ標準化によりアーキテクチャ固有の最適化が禁止されると言って、問題を手放します。しかし、それはポイントではありません。クロスプラットフォームのバイナリ実行形式について議論している人はいません。問題は、C ++モジュールを動的にロードする標準インターフェースがないことです。
チャールズサルビア

1

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

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

関数のオーバーロード、vtableの非互換性、モジュールの境界を越えてスローされる例外との非互換性などに起因する名前のマングリングに進むことができます。これらはすべて非常に苦痛です。

しかし、ABI規格は、あるコンパイラで作成されたC ++ dylibを、別のコンパイラで作成された別のバイナリで使用できるようにすることだけではありません。ABIは言語間で使用されます。彼らが少なくとも最初の部分をカバーできればいいのですが、C ++が最も広く互換性のあるdylibを作るために非常に重要な普遍的なABIレベルで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の一部としてエクスポートできます。しかし、異なるモジュールでエクスポートされた場合はどうFooなりBarますか?このdylibに関連付けられているシンボルと実装を複製する必要がありますか?ここでオブジェクトを作成し、ここに渡し、そこにコピーして、ここでそれを破棄するために複数のdylibインターフェイスに絡まり始めなければならないのは、本当に速く非常に迷惑になるかもしれないので、そうすることをお勧めします。同じ基本的な懸念がCでも多少適用される可能性がありますが(よ​​り手動/明示的に)、Cは、人々がそれをプログラムする方法の性質上、これを避ける傾向があります。

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

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

推奨される解決策

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

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

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

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

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


1
「Cを使用してこれらのユニバーサルABIを作成し、実装にC ++を使用します。」...他の多くの人と同じように、私も同じことをしています!
ナワズ

-1

バイナリレベルで標準化されない理由はわかりません。しかし、私はそれについて私が何をするかを知っています。Windowsでは、関数extern "C" BOOL WINAPIを宣言します。(もちろん、BOOLを関数のタイプに置き換えます。)そして、それらはきれいにエクスポートされます。


2
ただし、宣言するとextern "C"、C ABIが使用されます。これは、いかなる委員会によって課されていなくても、一般的なPCハードウェアの事実上の標準です。
ビリーONeal

-3

unzip foo.zip && make foo.exe && foo.exeソースの移植性が必要な場合に使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.