LoginSignup
47
44

More than 5 years have passed since last update.

C++形式の動的リンク・ライブラリの書き方(msvc編)

Last updated at Posted at 2016-12-06

ADVENTARC++ Advent Calender 20166

1


Microsoft Visual C++使C++


HDD
HDD使exe使



使


使

C++Theolizerboost
boost使
Theolizerboost使Theolizer使boost

Theolizer2boost
boostTheolizerboost使

Theolizerboost3


exedll

exedlldlldllexeexe

dllnewdllexeexedeleteexe

C++deletedlldeleteexeexe
dllexe

dlldllexedelete1
C++
(注1)
確認までしてはいないのですが、std::unique_ptr<>を使ってもうまく管理できない筈です。
dll側でstd::make_unique<>したオブジェクトをexe側が受け取ってexe側で開放した場合、デリータは
exeとdllのどちらのものが使われるでしょうか?
STLはクラス・テンプレートですから、全てのメンバ関数をinline展開せざる得ません。(後述)
ですのでexe側で定義されたものが使われる筈です。

1-3.ならばどうするか?


exedll使

boostboostlink=sharedboostruntime-link=static
C:\boost_1_59_0>b2.exe link=shared runtime-link=static
error: link=shared together with runtime-link=static is not allowed
error: such property combination is either impossible
error: or too dangerious to be of any use

Theolizerboostboostexeexedllboost


使exedll使


使

2C++


Theolizer9
Microsoft Visual Studio 2015 Community.NETVC++

dllexe使export

dllのビルド時:  #define DLL_EXPORT  __declspec(dllexport)
exeのビルド時:  #define DLL_EXPORT  __declspec(dllimport)

export


DLL_EXPORTstaticexedllstaticexedllstatic
static

exportC++  dllimport  dllexport 使
class DLL_EXPORT Singleton
{
public:
    static Singleton& getInstance()
    {
        static Singleton instance;
        return instance;
    }
};

2-2.export出来ないクラス


export
exedllexportexport

Barget()dll.cppdllBar使get()dll
exeBaz<int>::get()
dllBaz<int>::get()
dll.h
template<typename tType>
class Bar
{
    tType mData;
    
public:
    tType get() { return mData; }
};
dll.cpp
template<typename tType>
tType Bar<tType>::get()
{
    return mData;
}

これを防ぐためには、下記のようにヘッダ側でget()を定義する必要があります。

exportできないクラス例
template<typename tType>
class Bar
{
    tType mData;
    
public:
    tType get() { return mData; }
};

BarDLL_EXPORTget()get()dlldlldllBar

export
dllexe使

C4251


exportprivateexport使C4251

C++  dllimport  dllexport 使dllexport

C4251Foo0Bar
BarsStaticIntexedll
Foo0Foo1static
dll.h
#if !defined(DLL_H)
#define DLL_H

#ifdef DLL_BODY
    #define DLL_EXPORT  __declspec(dllexport)
#else
    #define DLL_EXPORT  __declspec(dllimport)
#endif

// privateメンバがないのでC4251警告が出ない
class Foo0
{
public:
    int mData;
};

// privateメンバがあるC4251警告が出るが、特に危険なクラスではない
class Foo1
{
    int mData;
};

// privateメンバがないのでC4251警告が出ないが、危険なクラスである!!
class /*DLL_EXPORT*/ Bar
{
    static int& getStaticInt()
    {
        static int  sStaticInt=1;
        return sStaticInt;
    }
public:
    void set(int x) { getStaticInt()=x; }
    int get() { return getStaticInt(); }
};

// exportされたクラス
class DLL_EXPORT Baz
{
public:
    Foo0    mFoo0;
    Foo1    mFoo1;  // C4251警告
    Bar     mBar;

    void    set(int x) { mBar.set(x); }
    int     get() { return mBar.get(); }
};

#endif  // DLL_H

static


staticexedll
exportBazstaticsStaticIntexedll

main.cppexesub.cppdll
main.cpp
#include <iostream>
#include "dll.h"

int main(int argc, char* argv[])
{
    Baz aBaz;

    std::cout << "aBaz.get()=" << aBaz.get() << "\n";
    std::cout << "Bar::get()=" << Bar::get() << "\n";

    aBaz.set(10);
    Bar::set(20);

    std::cout << "aBaz.get()=" << aBaz.get() << "\n";
    std::cout << "Bar::get()=" << Bar::get() << "\n";

    return 0;
}
sub.cpp
#define DLL_BODY
#include "dll.h"

Barクラスはexportしていないため、set()/get()関数の実体はexeとdllの両方にありますが、exe側(main.cpp)から使っているので、aBar.set()/get()ではexe側が使われています。
Bazクラスはexportしているため、set()/get()関数の実体はdll側にだけあります。
C++の文法的にはこれらのset()/get()関数は、Bar, Bazの両方とも同じ一つのsStaticIntをアクセスする筈です。

しかし、実行結果は以下の通りです。

実行結果
 aBar.get()=1
 aBaz.get()=1
 aBar.get()=10
 aBaz.get()=20

staticsStaticIntexedll
BarC4251

BarexportsStaticInt
Barをexportした時の実行結果
 aBar.get()=1
 aBaz.get()=1
 aBar.get()=20
 aBaz.get()=20

2-3-3.更にSTLを使っていた場合


exportSTLFoo1C4251使STLexport

Bazpublicstd::vector<int> mVector;std::vectorstd::vectorexportC4251
C4251exportC4251
std
namespace std
{
        struct DLL_EXPORT _Container_base12;
template class DLL_EXPORT _Vector_val<std::_Simple_types<int>>;
template class DLL_EXPORT _Compressed_pair<std::_Wrap_alloc<std::allocator<int>>,std::_Vector_val<std::_Simple_types<int>>,true>;
template class DLL_EXPORT _Vector_alloc<std::_Vec_base_types<int,std::allocator<int>>>;
template class DLL_EXPORT _Vector_alloc<std::_Vec_base_types<int,allocator<int>>>;
template class DLL_EXPORT vector<int,allocator<int>>;
}


C4251


使
static

exedll.hdlldll.hFoo0int mData;short mData;

C4251C4251#pragma warning(disable:4251)__pragma(warning(disable:4251))使

thread_localexport


thread_localerrno

gGlobalIntexport
// dll.h
extern int DLL_EXPORT gGlobalInt;

// sub.cpp
int gGlobalInt=0;

マルチ・スレッド化するため、これをthread_localにするため、下記のように定義したとします。

// dll.h
extern int DLL_EXPORT gGlobalInt;

// sub.cpp
thread_local int gGlobalInt=0;




error C2370: 'gGlobalInt':  ;  



// dll.h
extern thread_local int DLL_EXPORT gGlobalInt;

// sub.cpp
thread_local int gGlobalInt=0;




error C2492: 'gGlobalInt':   dll 



dllgGlobalIntexe
thread_local
// dll.h
//extern thread_local int DLL_EXPORT gGlobalInt;
DLL_EXPORT int& getGlobalInt();

// sub.cpp
thread_local int gGlobalInt=0;
int& getGlobalInt()
{
    return gGlobalInt;
}

3.実験ソースのビルド方法(using CMake)


使CMakeLists.txtGist

CMake 3.5.0CMake

Gist
Foo1 mFoo1;C4251
Windows 10  Visual Studio 2015 Community


mkdir build
 cd build
 cmake ..
 cmake --build .
 cd Debug
 main.exe


CMake便使

4


dll







C++C綿
dllPASS

exedll
dllexe

5


dll


exedll

exedllexedll

static
exedllexport
export

C4251


thread_localdllexport
export

6



C4251

調

47
44
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

47
44