2016年11月16日水曜日

This site has been moved.

This site has been moved to the following site.

コード断片を Gist に統一させようと試みたが分量が多いので諦めて仕切り直し。
旧サイトはこのままの状態で。

Expression templates 2

スカラとベクトル、スカラと行列については実装の目途が立ってきた。

ベクトルと行列に関しては、設計を煮詰める必要性があるようだ。
Eigen みたいにスカラもベクトルも行列もすべて行列として扱うのが合理的なんだろうな。

話は変わってテストケースを追加している際に、最適化(-O2)を有効にすると segfault で
vc14 / g++ / clang で全滅に遭遇した。
下記のようなケースが一例。

    auto exp = (va + vb) + (vc + vd);
    vector v(exp);

expression をコンストラクタに渡すと中でお亡くなりになっているようだった。
単純なケースでは発生しなかったので、expression が複雑になると何かが起きているようだ。

template<L, Op, R>   
class expression
{
public:
    expression(const L& l, const R& r) : l_(l), r_(r) {}
    // ...
private:
    const L& l_;
    const R& e_;
}    

上記は ETs(Expression templates) のサンプルに頻繁に記載されているような
単純な expression クラステンプレート。

const reference から猛烈な激臭がしてくる。

    expression exp_temp1 = va + vb;
    expression exp_temp2 = vc + vd;
    expression exp = exp_temp1 + exp_temp2;
    vector v(exp);

と考えると、最適化の影響で一時変数が消されている可能性が高い。
行きつく先は dangling reference となる。

というわけで const reference となっている箇所をスカラと expression については
実体としてもつようにすると案の定というか必然というか問題は解消した。
※ 併せてスカラ(ラッパー)内の const reference も実体としてもつように


vc14 の release build の結果.

2016年11月13日日曜日

Expression templates

Expression templates である。

Blitz++Eigen を大人しく使っておけである。

が、そこはやはりさわり程度は知っておくべきなんだろう。

単純に動くだけの簡単なものを作ってみることにした。

二次元ベクトルの加算、減算、スカラー倍、除算。

スカラーの扱いで随分悩んだのと、オペレータ各種がかなりゴリ押し気味。

行列とか絡むと今は想像できない。

要修行。


追記:

寝て起きたらオペレータ周りがすっきりした。
なにやら錯乱していたようだ。

2016年11月8日火曜日

[CMake] 静的ライブラリのリンク

大抵の外部ライブラリには CMake 対応がされており、
気軽に好みのビルド環境を構築することができるようになっている。 (できるとは限らないが)

なんであれ自作アプリケーションに組み込んで利用する前には必ずお世話になるはずだ。

私もよくお世話になっているわけだが、ふと中で何をしているのかが気になった。

というわけで動作を追ってみることにした。

まとめきれないので GitHub に。


おそらく頻繁に使うケースは、ライブラリをビルドツリーの

中に置く

外に置く

のどちらかだと思われる。


いづれにせよ重要なのは、CMakeの時間軸概念ではないだろうか。

(1) Generate-Time
  CMake で任意のプロジェクトを生成

(2) Build-Time
  生成されたプロジェクトを msbuild / make などで実際にビルド

(3) Install-Time
  ビルドされたものをインストール

中に置く場合は、ライブラリ/アプリともに (1) → (2) → (3) と進む(依存関係は解決済みという前提)
外に置く場合は、ライブラリ (1) → (2) → (3)、アプリ (1) → (2) → (3) と進める

時間軸の進め方に大きな違いがあるため、内部で必要となる準備も異なってくる。

所感としては、中に置くケースは一括で構築できるので便利である反面、複雑になる気がしてならない。






2016年10月4日火曜日

rvo と nrvo と API 設計

十数年も経過するので、今時のコンパイラなら意識するまでもなく実装している。

特に教科書然としたものなどで例に挙げられるような単純なケースなら確実に最適化がされる。


実際問題どんな状況なのかを調べてみることにした。


test_rvo_nrvo

これは vc だが相変わらず分岐を挟むと残念なことになるようだ。

しかし、gcc と clang で確認を行うと分岐があるにも関わらずきっちり最適化がされる。

---

const correctness について、ここでは関数の戻り型が焦点となる。

const なポインタや参照を返す場合、やはり一抹の不安が残る。

回避するにはコピーを返せばいいのだが、rvo または nrvo が効くか否かが

重要な場面も多々ある。

要はパフォーマンス問題に帰着する。


API の設計に統一性を持たせたいのだが、クロスプラットフォームだと

足を引っ張ってる方に合わせるしかないかな。

---

(追記)

と少し錯乱していたが、vc だけ少し特殊なことをすれば恩恵を授かることができた。
面倒くさい・・・。

2016年9月24日土曜日

shared_from_this を ctor から呼んでみる


言うまでもなく常識的に考えれば不味いことは一目瞭然。

構築が完了していないので strong_ref もクソもない。

あるクラスを RAII で設計していた際に遭遇したが

バッドノウハウからも得られるものがあるだろうということで興味本位に探してみる。

https://forum.libcinder.org/topic/solution-calling-shared-from-this-in-the-constructor

unfortunately this is not a reliable solution. See post below.

と注意書きがあるので真似しちゃダメではあるが

参照カウンタを騙す・・・ああ、その発想自体に脱帽だよ!


2016年9月13日火曜日

How to Survive 2 を ps3 コントローラでプレイ

表題の件の前提として、
Steam の他ゲームは x360ce 経由で普通に動作しているが
このゲームだけは動作しないという人向け。


先ずは原因から記載すると、

・ 64bit ゲームなので x360ce for 64-bit games が必要
動作させるには xinput9_1_0.dll が必要なのに最新版では生成ができない


なければ生成すればいいじゃないということで本題に。


(1) x360ce 3.2.8.76(for 64-bit games) をダウンロード(use google!)
(2) x360ce でコントローラ設定を行う(ここはいつも通り)
(3) Game settings タブを開く
(4) Add ボタンで HowToSurvive2.exe を登録
(5) 64-bit v9.1を選択

fig1.

このタイミングで Synchronize Settings が表示される。
※最新バージョンでは表示されない

(6) Synchronize Settings を押す

fig2.
ここでOKを押せば、(5) でチェックした xinput9_1_0.dll が (4) で登録したパスに生成される。

fig3.

(7) x360ce_x64.exe と同じ場所に x360ce.ini が生成されるので、上記のパスに手動コピー。


Windows10 x64 環境での動作は確認。

ドイツ人とがっつり遊んだ!

2016年9月12日月曜日

std::codecvt と vs2015

string, u16string, u32string の相互変換。

vs2015 では愉快なバグが放置されているため楽しくないコードを書く必要がある。

#include <locale>
#include <codecvt>

static constexpr std::codecvt_mode mode = std::codecvt_mode::little_endian;
//static constexpr std::codecvt_mode mode = (std::codecvt_mode)0;

// utf8 to utf16
// gcc7.0.0 ... ---
// gcc6.1.0 ... ---
// gcc5.3.0 ... ---
// gcc5.2.0 ... need endianness.
// gcc5.1.0 ... ---
// clang4.0.0 . ---
static std::u16string utf8_to_utf16(const std::string& s)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
    std::wstring_convert<std::codecvt_utf8_utf16<std::uint16_t, 0x10ffff, mode>, std::uint16_t> conv;
    auto temp = conv.from_bytes(s);
    return std::u16string(temp.cbegin(), temp.cend());
#else
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t, 0x10ffff, mode>, char16_t> conv;
    return conv.from_bytes(s);
#endif
}

// utf16 to utf8
// gcc7.0.0 ... ---
// gcc6.1.0 ... ---
// gcc5.3.0 ... ---
// gcc5.2.0 ... ---
// gcc5.1.0 ... ---
// clang4.0.0 . ---
static std::string utf16_to_utf8(const std::u16string& s)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
    std::wstring_convert<std::codecvt_utf8_utf16<std::uint16_t, 0x10ffff, mode>, std::uint16_t> conv;
    auto p = reinterpret_cast<const std::uint16_t*>(s.c_str());
    return conv.to_bytes(p, p + s.length());
#else
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t, 0x10ffff, mode>, char16_t> conv;
    return conv.to_bytes(s);
#endif
}

// utf8 to utf32
// gcc7.0.0 ... ---
// gcc6.1.0 ... ---
// gcc5.3.0 ... ---
// gcc5.2.0 ... ---
// gcc5.1.0 ... ---
// clang4.0.0 . ---
static std::u32string utf8_to_utf32(const std::string& s)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
    std::wstring_convert<std::codecvt_utf8<std::uint32_t, 0x10ffff, mode>, std::uint32_t> conv;
    auto temp = conv.from_bytes(s);
    return std::u32string(temp.cbegin(), temp.cend());
#else
    std::wstring_convert<std::codecvt_utf8<char32_t, 0x10ffff, mode>, char32_t> conv;
    return conv.from_bytes(s);
#endif
}

// utf32 to utf8
// gcc7.0.0 ... ---
// gcc6.1.0 ... ---
// gcc5.3.0 ... ---
// gcc5.2.0 ... ---
// gcc5.1.0 ... ---
// clang4.0.0 . ---
static std::string utf32_to_utf8(const std::u32string& s)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
    std::wstring_convert<std::codecvt_utf8<std::uint32_t, 0x10ffff, mode>, std::uint32_t> conv;
    auto p = reinterpret_cast<const std::uint32_t*>(s.c_str());
    return conv.to_bytes(p, p + s.length());
#else
    std::wstring_convert<std::codecvt_utf8<char32_t, 0x10ffff, mode>, char32_t> conv;
    return conv.to_bytes(s);
#endif
}

// utf16 to utf32
// gcc7.0.0 ... need endianness.
// gcc6.1.0 ... need endianness.
// gcc5.3.0 ... need endianness.
// gcc5.2.0 ... need endianness.
// gcc5.1.0 ... need endianness.
// clang4.0.0 . need endianness.
static std::u32string utf16_to_utf32(const std::u16string &s)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
    std::wstring_convert<std::codecvt_utf16<std::uint32_t, 0x10ffff, mode>, std::uint32_t> conv;
    const char16_t* data = s.c_str();
    auto bytes = conv.from_bytes(reinterpret_cast<const char*>(data), reinterpret_cast<const char*>(data + s.length()));
    return std::u32string(bytes.cbegin(), bytes.cend());
#else
    std::wstring_convert<std::codecvt_utf16<char32_t, 0x10ffff, mode>, char32_t> conv;
    const char16_t* data = s.c_str();
    return conv.from_bytes(reinterpret_cast<const char*>(data), reinterpret_cast<const char*>(data + s.length()));
#endif
}

// utf32 to utf16
// gcc7.0.0 ... need endianness.
// gcc6.1.0 ... need endianness.
// gcc5.3.0 ... need endianness.
// gcc5.2.0 ... need endianness.
// gcc5.1.0 ... ---
// clang4.0.0 . need endianness.
static std::u16string utf32_to_utf16(const std::u32string& s)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
    std::wstring_convert<std::codecvt_utf16<std::uint32_t, 0x10ffff, mode>, std::uint32_t> conv;
    auto p = reinterpret_cast<const std::uint32_t*>(s.c_str());
    auto bytes = conv.to_bytes(p, p + s.length());
    return std::u16string(reinterpret_cast<const char16_t*>(bytes.c_str()), bytes.length() / sizeof(char16_t));
#else
    std::wstring_convert<std::codecvt_utf16<char32_t, 0x10ffff, mode>, char32_t> conv;
    auto bytes = conv.to_bytes(s);
    return std::u16string(reinterpret_cast<const char16_t*>(bytes.c_str()), bytes.length() / sizeof(char16_t));
#endif
}

// ucs2 to utf8
// gcc7.0.0 ... ---
// gcc6.1.0 ... ---
// gcc5.3.0 ... ---
// gcc5.2.0 ... ---
// gcc5.1.0 ... ---
// clang4.0.0 . ---
static std::string ucs2_to_utf8(const std::u16string& s)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
    std::wstring_convert<std::codecvt_utf8<std::uint16_t, 0x10ffff, mode>, std::uint16_t> conv;
    auto p = reinterpret_cast<const std::uint16_t*>(s.c_str());
    return conv.to_bytes(p, p + s.length());
#else
    std::wstring_convert<std::codecvt_utf8<char16_t, 0x10ffff, mode>, char16_t> conv;
    return conv.to_bytes(s);
#endif
}

// utf8 to ucs2
// gcc7.0.0 ... ---
// gcc6.1.0 ... need endianness.
// gcc5.3.0 ... need endianness.
// gcc5.2.0 ... need endianness.
// gcc5.1.0 ... ---
// clang4.0.0 . ---
static std::u16string utf8_to_ucs2(const std::string& s)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
    std::wstring_convert<std::codecvt_utf8<std::uint16_t, 0x10ffff, mode>, std::uint16_t> conv;
    auto temp = conv.from_bytes(s);
    return std::u16string(temp.cbegin(), temp.cend());
#else
    std::wstring_convert<std::codecvt_utf8<char16_t, 0x10ffff, mode>, char16_t> conv;
    return conv.from_bytes(s);
#endif
}

// ucs2 to utf16
// gcc7.0.0 ... need endianness.
// gcc6.1.0 ... need endianness.
// gcc5.3.0 ... need endianness.
// gcc5.2.0 ... need endianness.
// gcc5.1.0 ... need endianness.
// clang4.0.0 . need endianness.
static std::u16string ucs2_to_utf16(const std::u16string &s)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
    std::wstring_convert<std::codecvt_utf16<std::uint16_t, 0x10ffff, mode>, std::uint16_t> conv;
    auto p = reinterpret_cast<const std::uint16_t*>(s.c_str());
    auto bytes = conv.to_bytes(p, p + s.length());
    return std::u16string(reinterpret_cast<const char16_t*>(bytes.c_str()), bytes.length() / sizeof(char16_t));
#else
    std::wstring_convert<std::codecvt_utf16<char16_t, 0x10ffff, mode>, char16_t> conv;
    auto bytes = conv.to_bytes(s);
    return std::u16string(reinterpret_cast<const char16_t*>(bytes.c_str()), bytes.length() / sizeof(char16_t));
#endif
}

// utf16 to ucs2
// gcc7.0.0 ... need endianness.
// gcc6.1.0 ... need endianness.
// gcc5.3.0 ... need endianness.
// gcc5.2.0 ... need endianness.
// gcc5.1.0 ... need endianness.
// clang4.0.0 . need endianness.
static std::u16string utf16_to_ucs2(const std::u16string &s)
{
#if defined(_MSC_VER) && (_MSC_VER <= 1900)
    std::wstring_convert<std::codecvt_utf16<std::uint16_t, 0x10ffff, mode>, std::uint16_t> conv;
    const char16_t* data = s.c_str();
    auto bytes = conv.from_bytes(reinterpret_cast<const char*>(data), reinterpret_cast<const char*>(data + s.length()));
    return std::u16string(bytes.cbegin(), bytes.cend());
#else
    std::wstring_convert<std::codecvt_utf16<char16_t, 0x10ffff, mode>, char16_t> conv;
    const char16_t* data = s.c_str();
    auto temp = conv.from_bytes(reinterpret_cast<const char*>(data), reinterpret_cast<const char*>(data + s.length()));
    return std::u16string(temp.cbegin(), temp.cend());
#endif
}

関数のコメントは、gcc と clang の各バージョンでエンディアン指定の有無を確認した結果。
std::codecvt_utf16 以外は不要なはずなのだが、あるバージョンでは指定の有無で
動作が変わって混乱したので付記しておいた。
動作環境はいづれも Little-Endian。

#include <iostream>
#include <assert.h>
#include <vector>

class TestItem
{
public:
    TestItem(std::string utf8, std::u16string utf16, std::u32string utf32)
        : utf8(std::move(utf8)), utf16(std::move(utf16)), utf32(std::move(utf32))
    {
    }
    std::string utf8;
    std::u16string utf16;
    std::u32string utf32;
};

void Test(const TestItem& item)
{
    // u8 <-> u16
    if(utf8_to_utf16(item.utf8) != item.utf16)
        std::cout << "Failed to convert: utf8_to_utf16" << std::endl;
    if(utf16_to_utf8(item.utf16) != item.utf8)
        std::cout << "Failed to convert: utf16_to_utf8" << std::endl;
    // u8 <-> u32
    if(utf8_to_utf32(item.utf8) != item.utf32)
        std::cout << "Failed to convert: utf8_to_utf32" << std::endl;
    if(utf32_to_utf8(item.utf32) != item.utf8)
        std::cout << "Failed to convert: utf32_to_utf8" << std::endl;
    // u16 <-> u32
    if(utf16_to_utf32(item.utf16) != item.utf32)
        std::cout << "Failed to convert: utf16_to_utf32" << std::endl;
    if(utf32_to_utf16(item.utf32) != item.utf16)
        std::cout << "Failed to convert: utf32_to_utf16" << std::endl;
    // ucs2 <-> utf8
    try{
        if(ucs2_to_utf8(item.utf16) != item.utf8)
            std::cout << "Failed to convert: ucs2_to_utf8" << std::endl;
    }
    catch(const std::range_error&){
        std::cout << "range_error: ucs2_to_utf8" << std::endl;
    }
    try{
        if(utf8_to_ucs2(item.utf8) != item.utf16)
            std::cout << "Failed to convert: utf8_to_ucs2" << std::endl;
    }
    catch(const std::range_error&){
        std::cout << "range_error: utf8_to_ucs2" << std::endl;
    }
    // ucs2 <-> utf16
    try{
        if(ucs2_to_utf16(item.utf16) != item.utf16)
            std::cout << "Failed to convert: ucs2_to_utf16" << std::endl;
    }
    catch(const std::range_error&){
        std::cout << "range_error: ucs2_to_utf16" << std::endl;
    }
    try{
        if(utf16_to_ucs2(item.utf16) != item.utf16)
            std::cout << "Failed to convert: utf16_to_ucs2" << std::endl;
    }
    catch (const std::range_error&){
        std::cout << "range_error: utf16_to_ucs2" << std::endl;
    }
}

int main(void)
{
#if defined(_MSC_VER)
    std::cout << "_MSC_VER=" << _MSC_VER << std::endl;
    std::cout << "_MSC_FULL_VER=" << _MSC_FULL_VER << std::endl;
#endif
    std::cout << "char16_t is signed? " << std::boolalpha << std::is_signed<char16_t>::value << std::endl;
    std::cout << "char16_t size = " << sizeof(char16_t) << std::endl;
    std::cout << "char32_t is signed? " << std::boolalpha << std::is_signed<char32_t>::value << std::endl;
    std::cout << "char32_t size = " << sizeof(char32_t) << std::endl;
    std::cout << "wchar_t is signed? " << std::boolalpha << std::is_signed<wchar_t>::value << std::endl;
    std::cout << "wchar_t size = " << sizeof(wchar_t) << std::endl;

    std::vector<TestItem> items = {
        TestItem(u8"abcABCあいうえお", u"abcABCあいうえお", U"abcABCあいうえお"),
        TestItem("\x61", u"\x0061", U"\x00000061"),                                // a 
        TestItem("\xEF\xBD\x81", u"\xFF41", U"\x0000FF41"),                        // a 
        TestItem("\xC4\x8D", u"\x010D", U"\x010D"),                                // č̌
        TestItem("\x63\xCC\x8C", u"\x0063\x030C", U"\x00000063\x0000030C"),        // c 
        TestItem("\xC4\xB3", u"\x0133", U"\x00000133"),                            // ij 
        TestItem("\x69\x6A", u"\x0069\x006A", U"\x00000069\x0000006A"),            // ij 
        TestItem("\xCE\xA9", u"\x03A9", U"\x000003A9"),                            // Ω 
        TestItem("\xE2\x84\xA6", u"\x2126", U"\x00002126"),                        // Ω 
        TestItem("\xF0\x9D\x93\x83", u"\xD835\xDCC3", U"\x0001D4C3")            // 𝓃 
    };
    for(auto item : items){
        std::cout << "* " << item.utf8 << "" << std::endl;
        Test(item);
    }
    return 0;
}

そしてテストコード。

"𝓃"の ucs2 との相互変換が各種コンパイラで動作が異なる。

vs2015 では
    ucs2_to_utf8 -> Failed to convert
    utf8_to_ucs2 -> Failed to convert
    ucs2_to_utf16 -> range_error
    utf16_to_ucs2 -> Failed to convert

gcc 7.0.0 では
    ucs2_to_utf8 -> N/A
    utf8_to_ucs2 -> N/A
    ucs2_to_utf16 -> range_error
    utf16_to_ucs2 -> Failed to convert

clang 4.0.0 では
    ucs2_to_utf8 -> range_error
    utf8_to_ucs2 -> range_error
    ucs2_to_utf16 -> range_error
    utf16_to_ucs2 -> range_error

あとはそう、char16_t と char32_t というネーミングは死角を突かれるのでやめて頂きたい・・・。

2016年9月4日日曜日

Signed distance field font rendering.

ペーパーは随分前に読んでほったらかしだったのでやってみたくなった。
ひとまず動けばいいや版を作ってみることに。
輪郭線はシェーダで付加。



smoothness 0%



smoothness 50%

smoothness 100%


単純なビットマップフォント描画があほらしくなる。

オフラインリソース生成部分に比重が傾いているので

頂点/フラグメントシェーダも数行程度で済むのも有難い。

とはいうものの肝心のリソース生成部分はやることが多すぎて食指が動かず。

ガチのビンパッキングとかそれだけでも面白そうだろうけどね。

というわけで、今回は Hiero というツールで楽をさせて貰った。


出力されるフォーマットを見ていると欲がでてくるもので
個人的に使う分には複数ページなんて使う機会ない癖についやってみたくなる。

単純に考えるとコンテキストの切り替えを抑えるためにテクスチャ毎にまとめて描画するハメに
なるんだろうけど、この辺り普通に実装すると泥臭くて投げ出したくなる。

何かいい方法(とにかく楽な方向) がないかと調べていると Array Texture があった。
シェーダ側は texture2DArray でレイヤを指定すればいい。


> 2D Texture Arrays are supported on Geforce 8000 series and higher video cards.

拡張命令で制限が付いているものの問題はなかろう。



1パスになり描画周りが驚くほどスッキリして思わず感動してしまった。

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

放置かい!

2016年6月24日金曜日

Custom assert macro

少し前からホビープログラムをC++主体に戻した。
1x系は刺激的で楽しい。

そんな刺激的な中で何も変わっていないものが普通の assert。
コンパイルタイムで利用できる static_assert が追加され、メッセージも別途出力できるのに
こいつだけは何も変わっていない!

assert(false && "hoge");
assert(!"foo");

泥臭くていやなんです。
ということで、何番煎じかは置いといて「無ければ作ればよろし」。

前提としては、
(1) メモリがシビアではない環境
(2) メッセージの別途出力(stream風に記述したい)
(3) リリースモードで一命令も吐かせない
(4) リリースモードでコンパイラを黙らせる(C4101対策)

bool cond = false;
int value = 10;
assert(cond, "hoge: " << value);

上記のような感じが目標。

#pragma once
#include <iostream>
#include <sstream>

#undef ASSERT_MSG
#undef ASSERT

#ifdef NDEBUG
 #define ASSERT_MSG(expression, message)\
    do{\
        (void)(true ? (void)0 : ((void)(expression)));\
        (void)(true ? (void)0 : ((void)(std::ostringstream() << message)));\
    }while(false)
 #define ASSERT(expression)\
    ASSERT_MSG(expression, "")
#else
 #define ASSERT_MSG(expression, message)\
    do{\
        if(!(expression)){\
            std::cerr << "Assertion `" #expression "` failed in " << __func__ << "() " << __FILE__ << "(" << __LINE__ << "): " << message << std::endl;\
            std::terminate();\
        }\
    }while(false)
 #define ASSERT(expression)\
    ASSERT_MSG(expression, "")
#endif

そしてこんな感じ。

bool cond = false;
int value = 10;
ASSERT_MSG(cond, "hoge" << value);

STL(std::ostringstream())はコンパイルタイムで対処しないとコードが生成されるため
当初は、(void)sizeof() などを利用したが、その他のケースを両立できないため
シンプルにコンパイラに丸投げすることに。

msvs2015環境では前提条件をすべて満たしたことは確認。
コンパイラさんの最適化恐るべし。