4

WindowsでCMakeで生成されたDLLファイルで紛らわしい問題に遭遇しました。私のライブラリでは、不思議なことに繰り返し発生するテンプレートパターンを使用して、特定のクラスに一意のID番号を付けています。

// da/Attribute.h:

#ifndef DA_ATTRIBUTE_H
#define DA_ATTRIBUTE_H

namespace da {

typedef unsigned int AttributeId;

class AttributeBase {
public:
    virtual AttributeId getTypeId() const=0;

protected:
    /** Static ID counter.  Every class that derives da::AttributeBase is
        assigned an increment of this counter as its type ID number */
    static AttributeId sNextId;

};

template <class Derived>
class Attribute : public AttributeBase {
private:
    static AttributeId msTypeId;

public:
    Attribute() {
        if (msTypeId == 0) {
            msTypeId = ++sNextId;
        }
    }

    virtual ~Attribute() {

    }

    /** For static contexts */
    static AttributeId typeId() {
        if (msTypeId == 0) {
            msTypeId = ++sNextId;
        }

        return msTypeId;
    }

    AttributeId getTypeId() const {
        return typeId();
    }

};

template <class Derived> AttributeId Attribute<Derived>::msTypeId = 0;

}

#endif

問題は、DLLを実行可能プロジェクトにリンクすると、さまざまなIDメソッドとの間にいくつかの不整合があるように見えることです。例えば:

// Foo.h
struct Foo : public da::Attribute<Foo> {
    Foo() { }
};

..。

// main.cpp
Foo *foo = new Foo;    

Foo->getTypeId() == 1 // True
Foo::typeId() == 1 // Should be true, but isn't.  Foo::typeId() == 2

Foo :: getTypeID()を中断して、GDBを実行すると、「msTypeId」と「Foo::msTypeId」のメモリアドレスが異なることがわかりました。 なんてこったい。

ただし、これはFooがDLLで定義されている場合にのみ発生します。(そして、Windows 7でのみ、明らかに-Debianビルドではこの問題は発生しません)main.cpp内に派生クラスを作成する場合、またはライブラリからすべてのコードを実行可能ファイルにコンパイルする場合は、スキップします。 DLLステップは完全に、問題なく動作します。

すべてがMSYSとMinGWを使用してコンパイルされ、Windows 7HomePremiumではGCC4.7が使用されました。

ライブラリのCMakeLists.txtは、何かを台無しにした場合に備えて、次のとおりです。

cmake_minimum_required(VERSION 2.6)
project(foo)

add_definitions(-std=c++0x)
set(CMAKE_BUILD_TYPE Debug)

set(sources
    Foo.cpp
)

add_library(foo SHARED ${sources})
4

1 に答える 1

2

共有ライブラリから型をエクスポートする必要があります。これは、__declspec(dllexport)__declspec(dllimport)デコレータを使用して行われます。MSDNのドキュメントを読んでください。それはかなり複雑です。

__declspec(dllexport)ライブラリを構築するとき、およびそれを使用するコードをコンパイルするときにヘッダーが必要になるため__declspec(dllimport)、通常はシンボルを定義します。これは通常呼び出され、定義さLIBRARYNAME_EXPORTれているかどうかに応じて#ifdefsしLIBRARYNAME_EXPORTSます。

CMakeはtarget_EXPORTS、(共有)ライブラリを構築するときに自動的に定義します。DEFINE_SYMBOLターゲットプロパティを設定することで上書きできます。

Unixは別のパスを選択し、デフォルトですべてのシンボルを共有ライブラリからエクスポートおよびインポートします(静的および明示的に非表示のシンボルを除く)。より多くのシンボルを解決する必要があるため、パフォーマンスが少し低下しますが、使用がはるかに簡単で(静的ライブラリから共有ライブラリに切り替えるために変更は必要ありません)、柔軟性がはるかに高くなります(つまり、共有ライブラリのシンボルをオーバーライドできます。 Windowsではできません)。

于 2012-10-22T10:56:10.950 に答える