web-dev-qa-db-ja.com

DLLインターフェイスまたはABIで標準ライブラリ(STL)クラスを使用するにはどうすればよいですか?

Visual Studioの警告C4251に関連してstlクラスを含むクラスをエクスポートする前に、いくつかの質問がありました。この質問またはこの質問。私はすでにUnknownRoadで優れた説明を読みました。

オプションであるかもしれませんが、警告を盲目的に無効にすることは少し危険に見えます。これらすべてのstdクラスをラップしてエクスポートすることも、実際にはオプションではありません。それは結局Standardテンプレートライブラリと呼ばれます...つまり、これらの標準クラスとのインターフェースを提供したいと考えています。

DLLインターフェイスでstl-classesを使用するにはどうすればよいですか?一般的な方法は何ですか?

62
André

先に進む前に、1つのことを覚えておいてください。私の答えは、さまざまなコンパイラーでコンパイルされたモジュールで構成されるアプリケーションで使用できる移植可能なコードを書くという観点から来ています。これには、同じコンパイラの異なるバージョンまたは異なるパッチレベルを含めることができます。

DLLインターフェイスでstl-classesを使用するにはどうすればよいですか?

Answer:できないことが多い1

理由: STLはコードライブラリであり、DLLのようなバイナリライブラリではありません。どこで使用しても同じであることが保証されている単一のABIはありません。確かに、STLは "Standard Template Library"を表していますが、ここで標準以外の重要な有効な単語はTemplateです。

標準は、各STLクラスが提供する必要のあるメソッドとデータメンバーを定義し、それらのメソッドが何をするかを定義します。しかし、もうありません。特に、標準では、コンパイラの作成者が標準で定義された機能を実装する方法を指定していません。コンパイラーの作成者は、メンバー関数とメンバー変数を追加するSTLクラスの実装を自由に提供できますnot標準にリストされているメンバーであれば、areが標準で定義されていますまだそこにあり、標準が言うことを行います。

多分例が正しいです。 _basic_string_クラスは、特定のメンバー関数と変数を持つものとして規格で定義されています。標準では実際の定義はほぼ4ページですが、以下はその抜粋です。

_namespace std {
  template<class charT, class traits = char_traits<charT>,
    class Allocator = allocator<charT> >
  class basic_string {
[snip]
  public:
    // 21.3.3 capacity:
    size_type size() const;
    size_type length() const;
    size_type max_size() const;
    void resize(size_type n, charT c);
    void resize(size_type n);
    size_type capacity() const;
    void reserve(size_type res_arg = 0);
    void clear();
    bool empty() const;
[snip]
};
_

size()およびlength()メンバー関数を検討してください。この情報を保持するためのメンバー変数を指定した標準には何もありません。実際、文字列自体を保持するためであっても、メンバー変数はまったく定義されていません。それでは、これはどのように実装されますか?

答えは、さまざまな方法です。一部のコンパイラは、_size_t_メンバー変数を使用してサイズを保持し、_char*_を使用して文字列を保持する場合があります。もう1つは、そのデータを保持する他のデータストアへのポインタを使用する場合があります(これは、参照カウント実装の場合に該当する場合があります)。実際、同じコンパイラの異なるバージョンまたはパッチレベルでさえ、これらの実装の詳細を変更する可能性があります。あなたはそれらに依存することはできません。したがって、MSVC 10の実装は次のようになります。

_namespace std {
  template<class charT, class traits = char_traits<charT>,
    class Allocator = allocator<charT> >
  class basic_string {
[snip]
char* m_pTheString;
};

size_t basic_string::size() const { return strlen(m_pTheString;) }
_

... MSVC 10 SP1は次のようになります。

_namespace std {
  template<class charT, class traits = char_traits<charT>,
    class Allocator = allocator<charT> >
  class basic_string {
[snip]
vector<char> m_TheString;
};

size_t basic_string::size() const { return m_TheString.size(); }
_

私は彼らがdoがこのように見えると言っているのではなく、そうかもしれないと言っていますここでのポイントは、実際の実装はプラットフォームに依存しており、他の場所がどうなるかを実際に知る方法がないということです。

ここで、MSVC10を使用して、このクラスをエクスポートするDLLを作成するとします。

_class MyGizmo
{
public:
  std::string name_;
};
_

sizeof(MyGizmo)とは何ですか?

上記で提案した実装を想定すると、MSVC10ではsizeof(char*)になりますが、SP1ではsizeof(vector<char>)になります。 DLLを使用するVC10 SP1でアプリケーションを作成する場合、オブジェクトのサイズは実際とは異なります。バイナリインターフェースが変更されました。


これの別の扱いについては、C++コーディング標準(Amazon link )issue#63を参照してください。


1: "できない場合が多い"ツールチェーンとライブラリを完全に制御できる場合、標準ライブラリコンポーネントまたは他のコードライブラリコンポーネント(Boostなど)をかなりの信頼性で実際にエクスポートできます。 。

基本的な問題は、ソースコードライブラリでは、コンパイラの種類やライブラリのバージョンによって、サイズや定義が異なる可能性があることです。コードが使用されているすべての場所でこれらの両方を制御する環境で作業している場合は、おそらく問題はありません。たとえば、すべてのシステムが社内で作成され、社内でのみ使用されている商社では、これを実行できる可能性があります。

88
John Dibling