ここで 3 つの異なる質問をしています。
最初の質問は、MSVC が名前を生成しないようにする方法があるかどうか、または他のコンパイラで可能かどうか、またはそれが失敗した場合、問題を壊さずに生成された type_info から名前を取り除く方法があるかどうかを尋ねます。
次に、名前を削除できるように、MS ABI を変更できるかどうかを知りたいと考えています (おそらくあまり過激ではありません)。
最後に、名前のない ABI を設計できるかどうかを知りたいと考えています。
質問 1 自体が複雑な質問です。私の知る限り、MSVC に名前を生成させないようにする方法はありません。また、他のほとんどのコンパイラは、 typeid(foo).name() が返す必要があるものを明確に定義する ABI を対象としているため、名前を生成しないようにすることもできません。
さらに興味深いのは、名前を削除するとどうなるかということです。MSVC については、答えがわかりません。ここで行う最善の方法は、おそらく試してみることです。DLL に移動し、各名前の最初の文字を \0 に変更して、dynamic_cast が壊れるかどうかなどを確認します (Mac および Linux の x86_64 実行可能ファイルでこれを実行できることはわかっています)。 g++ 4.2 によって生成され、動作しますが、今は脇に置きましょう。)
質問 2 については、名前の空白化が機能しないと仮定すると、名前ベースのシステムを名前を必要としないように変更することはそれほど難しくありません。些細な解決策の 1 つは、名前のハッシュ、または ROT13 でエンコードされた名前を使用することです (ここでの本来の目的は、「私のクラスの恥ずかしい名前をカジュアルなユーザーに見られたくない」ということを思い出してください)。しかし、それがあなたが探しているものに当てはまるかどうかはわかりません。もう少し複雑なソリューションは次のとおりです。
- 「dllexport」されたクラスの場合、UUID を生成し、それを typeinfo に入れ、DLL と共に生成される .LIB インポート ライブラリにも入れます。
- 「dllimport」されたクラスの場合、.LIB から UUID を読み取り、代わりにそれを使用します。
したがって、dllexport/dllimport を正しく取得できれば、exe は dll と同じ UUID を使用するため、機能します。しかし、そうしないとどうなりますか?1 つを dllexport としてマークし、もう 1 つを dllimport としてマークせずに、DLL と EXE で "誤って" 同一のクラス (たとえば、同じパラメーターを使用した同じテンプレートのインスタンス化) を指定した場合はどうなるでしょうか? RTTI はそれらを同じタイプとして認識しません。
これは問題ですか?まあ、C++ 標準はそうは言っていません。また、MS のドキュメントもありません。実際、ドキュメントには、これを行うことは許可されていないと明示的に記載されています。あるモジュールから明示的にエクスポートして別のモジュールにインポートしない限り、2 つの異なるモジュールで同じクラスまたは関数を使用することはできません。これがクラス テンプレートで行うのが非常に難しいという事実は問題であり、彼らが解決しようとしていない問題です。
現実的な例を見てみましょうlinkedlist
。すべてのリストの最後のノードがそのセンチネルを指し、end() 関数がそれへのポインターを返すだけの、グローバルな静的センチネルを持つノードベースのクラス テンプレートを作成します。(マイクロソフト独自の std::map の実装は、これを正確に行うために使用されていました。それがまだ正しいかどうかはわかりません。) linkedlist<int>
exe で a を新規作成し、それを dll 内の関数に参照渡ししますl.begin()
。l.end()
. exeによって作成されたノードはどれもdll内のセンチネルのコピーを指していないため、決して終了しません。もちろん、自分自身を渡す代わりに DLL にl.begin()
andを渡すと、この問題は発生しません。あなたは通常、を渡すことで逃げることができますl.end()
l
std::string
または、壊れるものに依存しないという理由だけで、参照によって他のさまざまなタイプ。しかし、実際にそうすることが許されているわけではありません。運が良かっただけです。したがって、名前をリンク時に検索する必要がある UUID に置き換えることは、リンクローダー時に型を照合できないことを意味しますが、リンクローダー時に型を照合できないという事実は、これが無関係。
これらの問題のない名前ベースのシステムを構築することは可能です。ARM C++ ABI (およびそれに基づく iOS および Android ABI) は、プログラマーが MS よりもはるかに少ない費用で実現できるものを制限しており、リンクローダーがそれを機能させる方法について非常に具体的な要件があります (3.2.5)。これは、次のように設計で明示的に選択されていたため、名前ベースでないように変更できませんでした。
• type_info::operator== と type_info::operator!= は、RTTI オブジェクトへのポインターとその名前だけでなく、type_info::name() によって返される文字列を比較します。
• type_info::name() によって返されるアドレスには依存しません。(つまり、t1.name() != t2.name() は t1 != t2 を意味するものではありません)。
最初の条件では、これらの演算子 (および type_info::before()) をアウトオブラインで呼び出す必要があり、実行環境がそれらの適切な実装を提供する必要があります。
しかし、この問題がなく、名前を使用しない ABI を構築することも可能です。これは#3にうまく続きます。
Itanium ABI (とりわけ、x86_64 および i386 上の OS X と最近の Linux の両方で使用される) はlinkedlist<int>
、あるオブジェクトで生成されたオブジェクトと、linkedlist<int>
別のオブジェクトで同じヘッダーから生成されたオブジェクトを実行時にリンクできることを保証します。つまり、同じ type_info オブジェクトを持つ必要があります。2.9.1 から:
2 つの type_info ポインターが等しい場合にのみ、2 つの type_info ポインターが同等の型記述を指すように意図されています。実装は、たとえば、シンボル プリエンプション、COMDAT セクション、またはその他のメカニズムを使用して、この制約を満たす必要があります。
コンパイラ、リンカー、およびリンクローダーが連携して、実行可能ファイルで作成された が、共有オブジェクトで作成されたlinkedlist<int>
とまったく同じ type_info オブジェクトを指すようにする必要があります。linkedlist<int>
したがって、すべての名前を削除しても、まったく違いはありません。(そして、これは非常に簡単にテストおよび検証できます。)
しかし、どうすればこの ABI 仕様を実装できるでしょうか? j_kubik は、リンク時の情報を .so ファイルに保存する必要があるため、事実上不可能であると主張しています。これは明らかな答えを示しています。リンク時の情報を .so ファイルに保存します。実際、ロード時の再配置などを処理するために、すでにそれを行う必要があります。これは、保存する必要があるものを拡張するだけです。実際、Apple も GNU/linux/g++/ELF もまさにそれを行っています。(これは、複雑な Linux システムを構築するすべての人が、数年前にシンボルの可視性と漠然としたリンケージについて学ばなければならなかった理由の一部です。)
この問題を解決するには、さらに明白な方法があります。C++ コンパイラとリンカーを連携させて C ベースのリンク ローダーをだますのではなく、C++ ベースのリンク ローダーを作成します。しかし、私の知る限り、Be 以来、誰もそれを試みていません。