2016年7月20日水曜日

Singleton (2)

前回の続き。
Singleton の暗部。
幸運な人(?)は問題に遭遇しないかも知れない。


foo.h
#pragma once
#include <iostream>
#include "singleton.h"

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

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


bar.h
#pragma once
#include <iostream>
#include "singleton.h"

class Bar : public example::Singleton<Bar>
{
    friend class example::Singleton<Bar>;

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


main.cpp
#include "foo.h"
#include "bar.h"

void main()
{
    Bar::GetMutableInstance();
    Foo::GetMutableInstance();
}

水面下(プログラム終了処理)で起きていること。




Bar::Bar()
Foo::Foo()
Bar::~Bar()
Foo::~Foo()

Singleton が他の Singleton に依存していると破綻する。
Foo::~Foo() で Bar を参照している場合など。

というわけで終了時の処理を手法はどうあれ制御可能にする必要がある。

singleton_finalizer.h
class SingletonFinalizer final
{
    template<typename T>
    friend class Singleton;

public:
    using Callback = std::function<void()>;

public:
    SingletonFinalizer() = delete;
    ~SingletonFinalizer() = delete;
    SingletonFinalizer(const SingletonFinalizer&) = delete;
    SingletonFinalizer& operator=(const SingletonFinalizer&) = delete;
    SingletonFinalizer(SingletonFinalizer&&) = delete;
    SingletonFinalizer& operator=(SingletonFinalizer&&) = delete;

private:
    static void Register(const Callback& cb);
    static void Finalize();
};


singleton_finalizer.cpp
#include <iostream>
#include <mutex>
#include "singleton_finalizer.h"

static std::stack<SingletonFinalizer::Callback> cbs;
static std::once_flag once;

void SingletonFinalizer::Register(const Callback& cb)
{
    std::cout << __func__ << std::endl;
    std::call_once(once, []()
    {
        std::cout << __func__ << "atexit" << std::endl;
        std::atexit(SingletonFinalizer::Finalize);
    });
    cbs.push(cb);
}

void SingletonFinalizer::Finalize()
{
    std::cout << __func__ << std::endl;
    while(!cbs.empty()){
        auto& cb = cbs.top();
        cb();
        cbs.pop();
    }
}


Bar::Bar() → Foo::Foo() → Bar::~Bar() → Foo::~Foo()



Bar::Bar() → Foo::Foo() → Foo::~Foo() → Bar::~Bar()

初期化の逆順にする。

singleton.h
template<typename T>
template<typename... Arguments>
T& Singleton<T>::GetInstance(Arguments&&... args)
{
    std::call_once(
        GetOnceFlag(),
        [](Arguments&&... args)
        {
            instance.reset(new T(std::forward<Arguments>(args)...));
            SingletonFinalizer::Register(std::bind(&Singleton<T>::DestroyInstance));
        },
        std::forward<Arguments>(args)...
        );
    assert(instance);
    return *instance;
}

template<typename T>
void Singleton<T>::DestroyInstance()
{
    if(instance)
        instance.reset();
}


Singleton<T>::GetInstance で初回のみ SingletonFinalizer に破棄関数を登録。
SingletonFinalizer::Register は初回のみ std::atexit に
SingletonFinalizer::Finalize を登録。



これで初期化の逆順に破棄処理が行われる。
GetInstance の呼び出し順さえ注意しておけばいい。

今回は汚い部分を隠蔽するために SingletonFinalizer のメソッドは private にし
肝心の Finalize 処理を std::atexit に丸投げした。
std::atexit で登録された関数は、プログラム終了処理の際、他の static 変数の破棄より前に
呼び出されるので目的に合致したというわけである。

std::atexit 自体は最低 32個は登録できる仕様なので、他のケースで併用する場合は注意が要る。
その場合は std::atexit を利用するのはやめて、任意の場所で SingletonFinalizer::Finalize を
明示的に呼び出す必要がある。

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 に渡しているラムダ式が不味いのかな。

2016年7月11日月曜日

constexpr

今更ながら、constexpr の仕組みを考えれば手放しに使えるものでもないようだ。

 
Stack Overflow

Is it possible to declare constexpr class in a header and define it in a seperate .cpp file?

その仕組みからテンプレートメタプログラミングとは相性はいいというか、
同じ理屈で縛られているというか。

普通のクラスで利用すると設計に影響大だな。

2016年7月9日土曜日

[vs2015] Nameless union and struct problem. 2

前回エントリーに若干絡む。
gcc では通るが、vs2015 では 38行目でエラー。



しかし p3 にカーソルを合わせると・・・



非標準拡張だし文句言えないか・・・。
15 行目の #if 1 を 0 にすれば問題は解消するが色々作戦の練り直しだな。


#include <iostream>

class Point
{
public:
    constexpr Point(const Point& p)
        : x(p.x), y(p.y)
    {
    }
    constexpr Point(int x, int y)
        : x(x), y(y)
    {
    }
public:
#if 1
    union
    {
        struct
        {
            int x, y;
        };
        int v[2];
    };
#else
    int x, y;
#endif
};

constexpr Point operator + (const Point& lhs, const Point rhs)
{
    return Point(lhs.x + rhs.x, lhs.y + rhs.y);
}

int main()
{
    constexpr Point p1(1, 2);
    constexpr Point p2(3, 4);
    constexpr Point p3 = p1 + p2;
    std::cout << p3.x << ", " << p3.y << std::endl;
    return 1;
}

2016年7月7日木曜日

[vs2015] Nameless union and struct problem.

vs2015 で設定はデフォルト。

#include <iostream>

class Foo final
{
public:
    Foo() = default;
    ~Foo() = default;

private:
    int value;
    struct
    {
        int x;
        int y;
    };
    union
    {
        int a;
        float b;
    };
    union
    {
        int v[2];
        struct
        {
            int z;
            int w;
        };
    };
};

void main()
{
    Foo foo;
    //foo.value;  // NG.
    //foo.x = 10; // NG
    //foo.a = 10; // NG

    // foo.v[0] = 10;  // NG
    // foo.v[1] = 10;  // NG

    foo.z = 10; // !?
    foo.w = 11; // !?
    std::cout << foo.z << std::endl;    // !?
    std::cout << foo.w << std::endl;    // !?
}

Error(Active)



特に問題はなし。
※ 9行目の private を public にすれば表示はされない。


Error message




エラーメッセージが出力されない。
ビルドに成功し実行ファイルが生成される。

Console out




!?

この状態で他のエラー要因(なんでもよい)を挿入すれば、
それに誘発されて本件もエラーメッセージに出力はされる。
が、他のエラー要因がなくなると元通り(Debug / Release 関係なし)。


※ 追記
Intellisenseには表示されないので見落としてしまう上、
private としているので直感もあてにならない・・・。

3年程前の Stack Overflow に同問題があるのを見つけた。

Stack Overflow: C++ private modifier ignored on nested anonymous struct

放置かい!