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環境では前提条件をすべて満たしたことは確認。
コンパイラさんの最適化恐るべし。

2014年6月10日火曜日

Xperia Z2 Tablet

衝動買い。

コストパフォーマンスは現時点で最高と思うし満足なのだが難点が1つ。




海外モデルの頃から情報が錯綜していたため判断が付かず

ダメ元で購入しておいたらダメだったというお話。

ならばと、代替案などないものかというところではあるが

そりゃまぁ他社製品宣伝するわけにはいかんわな。

今時のタブレットでスタイラス抜きというのも考え難いし

開発中には市販品使ってテストをしていると思うんだが

これといって情報は無さげ。

安ければ色々試してみるのもありなんだけど、スタイラスってぼったくりが

多すぎるんだよなぁ。

追記:6/11

安かったのとタブはワコムだろうということで悪あがきをしてみた。
Bamboo Stylus の一番安いので試したところ反応あり。
その他、手持ちのNW-88xPaperwhiteも反応があり嬉しい誤算。

2014年4月4日金曜日

Redmine on TS-220 (4) Passenger のインストールと Apache 連携

既存の Apache Http Server を利用し Passenger をインストールするため
少々まわりくどいことをします。

■ httpd 2.2.26 のインストール

 既存のままでは Passenger のコンパイルが通らないことから
 足りない部分を補います。

current directory: /opt/local/src

httpd の取得
# wget http://archive.apache.org/dist/httpd/httpd-2.2.26.tar.gz
# tar xvfz httpd-2.2.26.tar.gz

apr のインストール
# cd httpd-2.2.26/srclib/apr
# ./configure --prefix=/opt/local/httpd-2.2.26-apr
# make
# make check
# make install
# cd ../apr-util

apr-util のインストール
# ./configure --prefix=/opt/local/httpd-2.2.26-apr-util --with-apr=/opt/local/httpd-2.2.26-apr
# make
# make check
# make install
# cd ../..

httpd のインストール
# ./configure --prefix=/opt/local/httpd-2.2.26 --with-apr=/opt/local/httpd-2.2.26-apr --with-apr-util=/opt/local/httpd-2.2.26-apr-util
# make
# make check
# make install
# cd ..


■ Passenger 3.0.0 のインストール

# gem install passenger --version 3.0.0

apxs の編集
# vi /usr/local/apache/bin/apxs

apxs 内の Perl の位置を変更
#!/opt/bin/perl -w

apxs 内の installbuilddir の位置を変更
my $installbuilddir = "/opt/local/httpd-2.2.26/build";

環境変数の設定
# export APXS2=/usr/local/apache/bin/apxs
# export PATH=/usr/local/apache/bin:$PATH

Passenger のコンパイル
# /opt/local/ruby/lib/ruby/gems/1.9.1/gems/passenger-3.0.0/bin/passenger-install-apache2-module


■ Passenger の設定

 Apache との連携を行うため、Passenger の設定ファイルを作成します。

passenger-3.0.0.conf の作成と編集
# touch /usr/local/apache/conf/extra/passenger-3.0.0.conf
# vi /usr/local/apache/conf/extra/passenger-3.0.0.conf

(a) redmine ディレクトリを Apache 権限にする(passenger-3.0.0.conf)
LoadModule passenger_module /opt/local/ruby/lib/ruby/gems/1.9.1/gems/passenger-3.0.0/ext/apache2/mod_passenger.so
PassengerRoot /opt/local/ruby/lib/ruby/gems/1.9.1/gems/passenger-3.0.0
PassengerRuby /opt/local/ruby-1.9.2-p320/bin/ruby
RackBaseURI /redmine

# chown -R httpdusr:administrators /opt/local/redmine-2.4.3

(b) redmine ディレクトリを admin 権限にする(passenger-3.0.0.conf)
LoadModule passenger_module /opt/local/ruby/lib/ruby/gems/1.9.1/gems/passenger-3.0.0/ext/apache2/mod_passenger.so
PassengerRoot /opt/local/ruby/lib/ruby/gems/1.9.1/gems/passenger-3.0.0
PassengerRuby /opt/local/ruby-1.9.2-p320/bin/ruby
RackBaseURI /redmine
PassengerDefaultUser admin
PassengerDefaultGroup administrators
PassengerAnalyticsLogUser admin

(a) と (b) はお好みで。
※ 今回の動作確認は (a) で行っています。


■ Apache の設定

apache.conf の編集
# vi /usr/local/apache/conf/apache.conf

Passenger の設定をインクルード(apache.conf)
Include /etc/config/apache/extra/passenger-3.0.0.conf

※ファイル下方に追記します。


■ ドキュメントルートの設定

# ln -s /opt/local/redmine-2.4.3/public /share/Web/redmine


■ Apache の再起動

# /etc/init.d/Qthttpd.sh restart


■ Redmine へのアクセス

 再起動後、ブラウザから http://'NAS-IP'/redmine へアクセスし、Redmine の画面が表示されていることを確認。


お疲れ様でした。

2014年2月24日月曜日

Redmine on TS-220 (3) Redmine のインストール

■ bundler のインストール
 Redmine に必要な Gem パッケージを一括取得するため

# gem install bundler --no-rdoc --no-ri
# ln -s /opt/local/ruby/bin/bundle /opt/bin

■ Redmine 2.4.3 のインストール

 current directory: /opt/local/src
# wget http://www.redmine.org/releases/redmine-2.4.3.tar.gz
# tar xvfz redmine-2.4.3.tar.gz
# mv redmine-2.4.3 ../redmine-2.4.3
# cd ../redmine-2.4.3

■ phpMyAdmin のインストール
 TS-220 にブラウザでログインし、App Center から phpMyAdmin をインストール

■ MySQL に Redmine 用 データベースとユーザを作成
 TS-220 にブラウザでログインし、MySQL を有効化
 ブラウザで http://'NAS-IP'/phpMyAdmin を開き、phpMyAdmin 上で操作を行う
 ※ 起動できないときは・・・

create database redmine character set utf8;
create user 'redmine'@'localhost' identified by 'my_password';
grant all privileges on redmine.* to 'redmine'@'localhost'; 

■ Redmine のデータベースへの接続設定

 current directory: /opt/local/redmine-2.4.3
# cp config/database.yml.example config/database.yml
# vi config/database.yml

 database.yml
production:
    adapter: mysql
    database: redmine
    host: localhost
    username: redmine
    password: my_password
    encoding: utf8

■ Redmine 用 Gem パッケージのインストール

 current directory: /opt/local/redmine-2.4.3
# bundle install --without development test

■ Redmine の初期設定とデータベースのテーブル作成

 current directory: /opt/local/redmine-2.4.3
# bundle exec rake generate_secret_token
# RAILS_ENV=production bundle exec rake db:migrate

■ Redmine インストール後の動作確認テスト

 current directory: /opt/local/redmine-2.4.3
# ruby script/rails server webrick -e production

 WEBrick 起動後にブラウザで http://'NAS-IP':3000 を開く
 Redmine のホーム画面が表示されれば、Redmine 自体のインストールは成功

# cd ../src

NEXT: Redmine on TS-220 (4) Passenger のインストールと Apache 連携

2014年2月23日日曜日

Redmine on TS-220 (2) ビルド環境の構築

Ruby が前提となるため、必要なものを用意する。

以降、すべての作業を次のユーザで行う。
 admin (TS-220 の管理者)

■ Optware(IPKG) のインストール
 (1) TS-220 にブラウザでログインし、App Center から Optware(IPKG) をインストール
 (2) TS-220 に SSH でログインし、Optware.sh の PATH 変数の優先順位を変更

# vi /opt/Optware.sh

 Optware.sh
[ $? -ne 0 ] && /bin/echo "export PATH=/opt/bin:/opt/sbin:\$PATH" >> /etc/profile

TS-220 を再起動し SSH でログイン

■ IPKG で色々とインストール
 ※参考先にもあるようにすべては不要(面倒だった)

# ipkg install man
# ipkg install man-pages
# ipkg install less
# ipkg install bash
# ipkg install grep
# ipkg install which
# ipkg install sed
# ipkg install gawk
# ipkg install perl
# ipkg install procps
# ipkg install coreutils
# ipkg install util-linux-ng
# ipkg install zip
# ipkg install bzip2
# ipkg install zlib
# ipkg install diffutils
# ipkg install patchutils
# ipkg install make
# ipkg install binutils
# ipkg install gcc
# ipkg install gdb
# ipkg install libtool
# ipkg install glib
# ipkg install openssl-dev
# ipkg install pcre-dev
# ipkg install ncursesw-dev
# ipkg install net-tools
# ipkg install wget-ssl
# ipkg install imagemagick
# ipkg install tcl

ここからは、下記を前提として作業を進める
・独自インストールするものはすべて 
 /opt/local/src 以下で make
 /opt/local に配置

# cd /opt
# mkdir -p local/src -m 755
# cd local/src

■ libyaml 0.1.5 のインストール

 current directory: /opt/local/src
# wget http://pyyaml.org/download/libyaml/yaml-0.1.5.tar.gz
# tar xvfz yaml-0.1.5.tar.gz
# cd yaml-0.1.5
# ./configure --prefix=/opt/local/yaml-0.1.5
# make 
# make check
# make install
# cd ..

■ libffi 3.0.13 のインストール

 current directory: /opt/local/src
# wget ftp://sourceware.org/pub/libffi/libffi-3.0.13.tar.gz
# tar xvfz libffi-3.0.13.tar.gz
# cd libffi-3.0.13
# ./configure --prefix=/opt/local/libffi-3.0.13
# make
# make check
# make install
# cd ..

■ Ruby 1.9.2-p320 のインストール

 current directory: /opt/local/src
# wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p320.tar.bz2
# tar xvfj ruby-1.9.2-p320.tar.bz2
# cd ruby-1.9.2-p320
# ./configure --prefix=/opt/local/ruby-1.9.2-p320 \
 --with-opt-dir=/opt:/opt/local/yaml-0.1.5:/opt/local/libffi-3.0.13 \
 --disable-install-doc --disable-install-rdoc --disable-install-capi
# make
# make check
# make install
# cd ..

 ※ 途中で /tmp 以下でエラーがでる場合、スティッキービットを予め立てておく

# chmod 1777 /tmp

■ Ruby のシンボリックリンクを張っておく
 ※後程の作業が捗る
# ln -s /opt/local/ruby-1.9.2-p320 /opt/local/ruby
# ln -s /opt/local/ruby/bin/ruby /opt/bin
# ln -s /opt/local/ruby/bin/gem /opt/bin
# ln -s /opt/local/ruby/bin/rake /opt/bin


参考: QNAP TS410 QTS4.0.1 で ruby を入れる手順~

NEXT: Redmine on TS-220 (3) Redmine のインストール

Redmine on TS-220 (1) 構成

仕事ではGitを使う場面が多くなり、スクラム開発も絡めてRedmineを検討していたところ
ALMiniumが条件に合っていたので採用した。
ディストリビューションが制限されるのが難点ではあるが、お手軽さは魅力である。

一方、自宅のNAS(QNAP TS-220)にも同様な環境が構築できないものかと
悪戦苦闘したので忘備録として残しておく。

いわゆる組込みLinuxであり、/以下の基本部分はすべてRAMに乗っている。
シンボリックリンクで様々な辻褄を合わせているため、普通の感覚でいると
再起動時に設定が消えていたなどということが頻繁に起こるため要注意。

まず結論から書くと、以下の構成となる。
※動作は確認済み

・Apache Http Server
Phusion Passenger 3.0.0
・MySQL
Ruby 1.9.2-p320
Redmine 2.3.4

※特に Passenger と Ruby のバージョンを指定したことには理由があり
 この組み合わせ以外では Passenger のコンパイルが成功しない
 ・Passenger 3.0.0 以外 → Passenger のコンパイルに失敗
 ・Ruby 2.x.x → Ruby の make に失敗 
 ・Passenger 3.0.0 + Ruby 1.9.3 → インストールには成功するが接続時にエラー

上記の内、TS-220 には標準で下記がインストール済みである。

・Apache Http Server(Customized)
・MySQL
・Ruby

Apache Http Server および MySQL はそのまま利用することにする。
Ruby は Redmine が対応していないバージョンのため、標準のものには手を加えずに
残すものとする。

NEXT: Redmine on TS-220 (2) ビルド環境の構築