myoukakuのブログ

C++でゲームエンジンを作っていきます。

Gitのブランチモデルを変えようかな

これまで、Gitのブランチモデルとしてgit-flowを使っていたのですが、developブランチで作業するメリットも感じられないしめんどくさくなってきたので、GitHub Flowに変えようかなと思うようになってきました。

まあ一人で作業しているので、きちっとブランチモデルを決める必要はないのですが。

git-flow

見えないチカラ: A successful Git branching model を翻訳しました

GitHub Flow

GitHub Flow (Japanese translation)

Boost.ConstrainedValueはどうなった

ゲームプログラミングをしていると、取りうる値の範囲が限られている変数がよくあります。ゲームプログラミングに限らず、いろいろなプログラムで必要になると思います。

なのでBoost.ConstrainedValueには期待していました。 Boost.ConstrainedValueのレビュー結果 - Faith and Brave - C++で遊ぼう

しかしさっぱりBoostに採用される気配もないし、開発が続いているのかどうかもよくわかりません。 今どうなっているのでしょうか?

テンプレートパラメータパックのインデックスアクセス

Variadic Template をいじくりまわしていて、テンプレートパラメータパックのインデックスアクセスがほしいと思いましたが標準に無いんですね。素直に線形オーダーで実装すると数が多いときに使い物にならなかったのですが、対数オーダーで実装できるようです。

対数オーダーでTemplate Parameter Packから要素を取り出す - ここは匣

(コードを読んでもほとんど理解できん…)

やっぱり標準に入ってほしいですね~。世のC++erの92%が喜ぶんじゃないでしょうか。コンパイラマジックを使えば定数オーダーで実装できると思いますし。

make_static_map で pull request 出そうと思っていたら先を越された

make_static_mapでAkira Takahashi (id:faith_and_brave)さんからpull requestどうぞって言われたのでpull requestのやりかたを調べたりしていたら先にAkiraさんがmake_static_mapをコミットしていた!

static_map : add helper function. · be3624a · faithandbrave/Shand · GitHub

copyrightに名前も入れていただいて本当にうれしいです!

引数のpairのところ、そういうふうに書けたのか…。ひとつ勉強になった。自分のリポジトリにも反映しておこう。

static_mapのヘルパー関数、make_static_mapを書いた(3)

static_mapのヘルパー関数、make_static_mapを書いた(2) - myoukakuのブログ の続き

テンプレートパラメータパックをtupleに受けるのでなく、直接要素を取り出せば行けるかと思い、 VSでも動くパラメーターパックヘルパーを作った - TXT.TXTを参考にやってみました。

結果的に、正しく動作することはしましたが、やはり要素数が多くなると*1Internal Compiler Error(fatal error C1060: ヒープの領域を使い果たしました。)となってしまいました。

少なくとも要素数は200以上必要だったのと、エラーにならなくてもコンパイル時間が長くなってしまったので、この方法は断念し、最初に書いたstd::pairを渡す方法で行くことにしました。

(common_typeやtuple_elementを使うようにしたりと少し修正してあります) bksge/make_static_map.hpp at develop · myoukaku/bksge · GitHub

色々と回り道をした結果、最初に戻ってしまった感じですが、そのなかでVariadic Templatesに関連して、index_tuple技法など様々な知見を得ることができました。 今後に活かしていきたいです。

*1:120を超えたあたり

static_mapのヘルパー関数、make_static_mapを書いた(2)

static_mapのヘルパー関数、make_static_mapを書いた(1) - myoukakuのブログ の続き

static const auto m = make_static_map(
    Color::Red,    std::string("Red"),
    Color::Green,  std::string("Green"),
    Color::Blue,   std::string("Blue"));

と書けるmake_static_map関数を実現する方法を考えます。

方針としては、与えられた引数をtupleにし、そのtupleの偶数番目と奇数番目をpairにしてそれをstatic_mapに渡すようにします。 コードのイメージとしてはこんな感じです。

template <typename ... Types>
/* 戻り値の型は省略 */ make_static_map(Types...args)
{
    const auto t = std::make_tuple(args...);

    return
    {
        std::make_pair(std::get<0>(t), std::get<1>(t)),
        std::make_pair(std::get<2>(t), std::get<3>(t)),
        std::make_pair(std::get<4>(t), std::get<5>(t)),
                            .
                            .
                            .
    };
}

このためには偶数のリスト(0, 2, 4, ...)と奇数のリスト(1, 3, 5, ...)が必要です。 それにはindex_tuple技法を使います。

参考

コンパイル時に配列を結合する - iorateの日記

要点だけを抜き出したコードがこちら

template <
    typename TupleType,
    std::size_t ... EvenIndexes,
    std::size_t ... OddIndexes,
>
/* 省略 */ make_static_map_aux(
    TupleType t,
    index_tuple<EvenIndexes...>,
    index_tuple<OddIndexes...>)
{
    return
    {
        std::make_pair(
            std::get<EvenIndexes>(t),
            std::get<OddIndexes>(t)
        )...
    };
}

template <
    typename ... Types,
    std::size_t N = sizeof...(Types)
>
/* 省略 */ make_static_map(Types...args)
{
    return make_static_map_aux(
        std::make_tuple(args...),
        index_range<0, N, 2>::type(),
        index_range<1, N, 2>::type());
}

index_range<0, N, 2>::type()で偶数のリストを、index_range<1, N, 2>::type()で奇数のリストを作って渡します。 make_static_map_aux内でのパラメータパックの展開の仕方がミソです。

これで望み通りの記述が可能になりました。メデタシメデタシ。

…とはいかず、引数が多くなるとmake_tupleの部分でInternal Compiler Error(fatal error C1060: ヒープの領域を使い果たしました。)になってしまいました*1。私の環境では引数の数が80を超えたあたりでエラーになりました。

これでは使い物になりません。

続く。

static_mapのヘルパー関数、make_static_mapを書いた(1)

static_mapのヘルパー関数、make_static_mapを書いた(0) - myoukakuのブログ の続き

まず、以下のように書いてみました。

template <typename T, typename ... Types>
struct head
{
    using type = T;
};

template <typename ... Types>
struct key_type
{
    using type = typename head<Types...>::type::first_type;
};

template <typename ... Types>
struct value_type
{
    using type = typename head<Types...>::type::second_type;
};

template <
    typename ... Types,
    typename Key = key_type<Types...>::type,
    typename Value = value_type<Types...>::type,
    std::size_t N = sizeof...(Types)
>
static_map<Key, Value, N> make_static_map(Types&& ... args)
{
    return static_map<Key, Value, N> {{ args ... }};
}

はじめはmake_static_mapの引数をinitializer_listにしていたんですが、initializer_listのサイズがコンパイル時に取得できないので*1Variadic Templateを使うようにしました。

key_typeとvalue_typeのところはcommon_typeを使ったほうがいいんでしょうが、要素数が多くなるとエラーになったので*2先頭の型だけを使うようにしました。

これで

static const auto m = make_static_map(
    std::make_pair(Color::Red,    "Red"),
    std::make_pair(Color::Green,  "Green"),
    std::make_pair(Color::Blue,   "Blue"));

のように書けるようになりました。

型名と要素数を書く必要はなくなったので目的はほぼ達成できましたが、make_pairをいちいち書くのが面倒です。

static const auto m = make_static_map(
    { Color::Red,    "Red" },
    { Color::Green,  "Green" },
    { Color::Blue,   "Blue" });

と書けないかと試行錯誤しましたが、上手く行きませんでした。

続く。

*1:C++14からはsize()がconstexprになるのでたぶんOK

*2:500ぐらい?効率の良いcommon_typeを書けば回避できるかも。