2016年7月17日日曜日

Singleton

存在の良し悪しは置いておいて、Singleton 。

宣言
#include <memory>
#include <functional>
#include <mutex>

template<typename T>
class Singleton
{
protected:
    Singleton() = default;
    ~Singleton() = default;

public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton& operator=(Singleton&&) = delete;

    template<typename... Arguments>
    static T& GetMutableInstance(Arguments&&... args);

    template<typename... Arguments>
    static const T& GetConstInstance(Arguments&&... args);

private:
    template<typename... Arguments>
    static T& GetInstance(Arguments&&... args);

    static std::once_flag& GetOnceFlag();

    static std::unique_ptr<T> instance;
};

定義
template<typename T> std::unique_ptr<T> Singleton<T>::instance = nullptr;

template<typename T>
template<typename... Arguments>
T& Singleton<T>::GetMutableInstance(Arguments&&... args)
{
    return GetInstance(std::forward<Arguments>(args)...);
}

template<typename T>
template<typename... Arguments>
const T& Singleton<T>::GetConstInstance(Arguments&&... args)
{
    return GetInstance(std::forward<Arguments>(args)...);
}

template<typename T>
template<typename... Arguments>
T& Singleton<T>::GetInstance(Arguments&&... args)
{
    std::call_once(
        GetOnceFlag(),
        [](Arguments&&... args)
            {
                std::cout << "initialize." << std::endl;
                instance.reset(new T(std::forward<Arguments>(args)...));
            },
        std::forward<Arguments>(args)...
        );
    return *instance;
}

template<typename T>
std::once_flag& Singleton<T>::GetOnceFlag()
{
    static std::once_flag once;
    return once;
}


class Foo final : public Singleton<Foo>
{
    friend class Singleton<Foo>;

public:
    using Vector = std::vector<int>;

private:
    Foo()
    {
        std::cout << __func__ << ": " << std::endl;
    }
    explicit Foo(const Vector& v)
    {
        std::cout << __func__ << ": const Vector& v" << std::endl;
    }
    explicit Foo(Vector&& v)
    {
        std::cout << __func__ << ": Vector&& v" << std::endl;
    }
    Foo(int value1, int value2, int value3)
    {
        std::cout << __func__ << ": int value1, int value2, int value3" << std::endl;
    }

public:
    ~Foo()
    {
        std::cout << __func__ << std::endl;
    }

public:
    void Func()
    {
        std::cout << __func__ << " Mutable func." << std::endl;
    }

    void Func() const
    {
        std::cout << __func__ << " Const func." << std::endl;
    }
};

例の 1~3行目は CRTP のお決まり。
初回呼び出し時のみお目当ての ctor 呼び出しを行いたいので、
GetInstance 系は Variadic templates に。

void main()
{
    Foo::Vector v;
    v.push_back(1);

    auto& ref1 = Foo::GetMutableInstance(v);
    auto& ref2 = Foo::GetConstInstance();
    assert(&ref1 == &ref2);
    ref1.Func(); // call mutable function.
    ref2.Func();  // call const function.
}

適当なコードではあるが、
Foo::GetMutableInstance(v) によりお目当てのFoo(const Vector& v) が呼ばれ
以降の Foo::GetConstInstance では引数に特別な意味はない。

assert はインスタンスの一意性を確認。
インスタンスの生成と取得はスレッドセーフなので問題はない。
※ 初期に引数毎にインスタンスが生成されてしまう凡ミスを犯してしまったことへの自戒

引数に rvalue reference を考慮しなければ GetInstance はもう少しシンプルにはなる。
std::call_once の代わりに std::bind を利用することになるが・・・。 

vs2015 では問題はないが、その他ではビルドエラーとなることは確認済。
例えばこのあたりとか。
std::call_once に渡しているラムダ式が不味いのかな。

0 件のコメント :

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。