DLLでLoki::SingletonHolderを使う場合の注意点

C++でSingletonクラスを作成するのに便利なのがLoki::SingletonHolderクラス。しかし、Loki::SingletonHolderはテンプレートクラスかつ静的メンバ変数でオブジェクトを管理しているため、ダイナミックリンクライブラリ(DLL)で使う場合には注意が必要だ。

直接Loki::SingletonHolderクラスを使った場合

package1/Foo.h
#ifndef PACKAGE1_FOO_H
#define PACKAGE1_FOO_H

#include "package1/package1.h"
#include <loki/Singleton.h>

namespace package1
{

class PACKAGE1_API Foo
{
    friend Loki::CreateUsingNew<Foo>;
    friend Loki::DefaultLifetime<Foo>;

private:
    Foo();
    Foo(const Foo &);
    Foo &operator=(const Foo &);
    ~Foo();

public:
    void func();
};

typedef Loki::SingletonHolder<Foo> FooHolder;

}

#endif
package1/Foo.cpp
#include "package1/Foo.h"
#include <iostream>

namespace package1
{

Foo::Foo()
{
    std::cout << "Foo::Foo() called, this: " << this << '\n';
}

Foo::~Foo()
{
    std::cout << "Foo::~Foo() called, this: " << this << '\n';
}

void Foo::func()
{
    std::cout << "Foo::func() called, this: " << this << '\n';
}

}
package1/use_foo.cpp
#include "package1/use_foo.h"
#include "package1/Foo.h"
#include <iostream>

namespace package1
{

void use_foo()
{
    std::cout << "-> package1::use_foo()\n";

    Foo &foo = FooHolder::Instance();
    foo.func();

    std::cout << "<- package1::use_foo()\n";
}

}
package2/use_foo.cpp
#include "package2/use_foo.h"
#include "package1/Foo.h"
#include <iostream>

using namespace package1;

namespace package2
{

void use_foo()
{
    std::cout << "-> package2::use_foo()\n";

    Foo &foo = FooHolder::Instance();
    foo.func();

    std::cout << "<- package2::use_foo()\n";
}

}
main/main.cpp
#include "package1/Foo.h"
#include "package1/use_foo.h"
#include "package2/use_foo.h"
#include <tchar.h>
#include <iostream>

using namespace package1;

int _tmain(int argc, _TCHAR* argv[])
{
    std::cout << "-> main()\n";

    Foo &foo = FooHolder::Instance();
    foo.func();

    package1::use_foo();
    package2::use_foo();

    std::cout << "<- main()\n";
    return 0;
}
main.exeの実行結果
-> main()
Foo::Foo() called, this: 003D80B8
Foo::func() called, this: 003D80B8
-> package1::use_foo()
Foo::Foo() called, this: 003D8160
Foo::func() called, this: 003D8160
<- package1::use_foo()
-> package2::use_foo()
Foo::Foo() called, this: 003D8190
Foo::func() called, this: 003D8190
<- package2::use_foo()
<- main()
Foo::~Foo() called, this: 003D80B8
Foo::~Foo() called, this: 003D8190
Foo::~Foo() called, this: 003D8160

パッケージ毎にFooオブジェクトが生成されてしまっていることが確認できる。
これを防ぐには、Loki::SingletonHolderを直接使うのではなく、Loki::Singletonクラスを経由して使う。

Loki::Singletonクラスを経由してLoki::SingletonHolderを使った場合

package1/Foo.h
#ifndef PACKAGE1_FOO_H
#define PACKAGE1_FOO_H

#include "package1/package1.h"
#define LOKI_SINGLETON_EXPORT PACKAGE1_API
#include <loki/Singleton.h>
#undef LOKI_SINGLETON_EXPORT

namespace package1
{

class PACKAGE1_API Foo
{
    friend Loki::CreateUsingNew<Foo>;
    friend Loki::DefaultLifetime<Foo>;

private:
    Foo();
    Foo(const Foo &);
    Foo &operator=(const Foo &);
    ~Foo();

public:
    void func();
};

}

#endif
package1/Foo.cpp
#include "package1/Foo.h"
#include <iostream>

namespace package1
{

Foo::Foo()
{
    std::cout << "Foo::Foo() called, this: " << this << '\n';
}

Foo::~Foo()
{
    std::cout << "Foo::~Foo() called, this: " << this << '\n';
}

void Foo::func()
{
    std::cout << "Foo::func() called, this: " << this << '\n';
}

typedef Loki::SingletonHolder<Foo> FooHolder;

}

LOKI_SINGLETON_INSTANCE_DEFINITION(package1::FooHolder);
template class Loki::Singleton<package1::FooHolder>;
package1/use_foo.cpp
#include "package1/use_foo.h"
#include "package1/Foo.h"
#include <iostream>

namespace package1
{

void use_foo()
{
    std::cout << "-> package1::use_foo()\n";

    Foo &foo = Loki::Singleton<Foo>::Instance();
    foo.func();

    std::cout << "<- package1::use_foo()\n";
}

}
package2/use_foo.cpp
#include "package2/use_foo.h"
#include "package1/Foo.h"
#include <iostream>

using namespace package1;

namespace package2
{

void use_foo()
{
    std::cout << "-> package2::use_foo()\n";

    Foo &foo = Loki::Singleton<Foo>::Instance();
    foo.func();

    std::cout << "<- package2::use_foo()\n";
}

}
main/main.cpp
#include "package1/Foo.h"
#include "package1/use_foo.h"
#include "package2/use_foo.h"
#include <tchar.h>
#include <iostream>

using namespace package1;

int _tmain(int argc, _TCHAR* argv[])
{
    std::cout << "-> main()\n";

    Foo &foo = Loki::Singleton<Foo>::Instance();
    foo.func();

    package1::use_foo();
    package2::use_foo();

    std::cout << "<- main()\n";
    return 0;
}
main.exeの実行結果
-> main()
Foo::Foo() called, this: 003D80B8
Foo::func() called, this: 003D80B8
-> package1::use_foo()
Foo::func() called, this: 003D80B8
<- package1::use_foo()
-> package2::use_foo()
Foo::func() called, this: 003D80B8
<- package2::use_foo()
<- main()
Foo::~Foo() called, this: 003D80B8

こうすることで、Fooオブジェクトが一つだけ生成される。

Modern C++ Design―ジェネリック・プログラミングおよびデザイン・パターンを利用するための究極のテンプレート活用術 (C++ In‐Depth Series)

Modern C++ Design―ジェネリック・プログラミングおよびデザイン・パターンを利用するための究極のテンプレート活用術 (C++ In‐Depth Series)

  • 作者: アンドレイアレキサンドレスク,Andrei Alexandrescu,村上雅章
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2001/12
  • メディア: 単行本
  • 購入: 12人 クリック: 214回
  • この商品を含むブログ (102件) を見る