NEUROMANTIC

自分でC/C++/UE4/Graphics/ゲームなどをやったことをメモするブログ

Chapter 3 Typelists メモ (1)

Typelists

  • Typelistというのは支援する値に関した一連の型をリスト化して提供する道具である。3章ではこのTypelistを実装する。
  • Typelistが有用に使用されるところは、Abstract Factoryか、Visitorパターンでよく使われる。

3.2 Defining Tyeplists ~ 3.6 Indexed Access

  • Typelistは2つのタイプを持つことができる。この2つのタイプの別称をHeadTailと呼ぶ。そしてTypelistTailにまた別のTypelistをつけて複数のタイプを持つように調整することもできる。
  • しかし、タイプが複数存在する場合には、最後の型をもう以上締めるということでNullTypeという別のタイプを使って閉まる。これによってHalf-opened rangeが構成できる。
  • Typelistに複数の型を用意するには一つ一つ書く方法もあるけど、本にはMacroを使って個数ごとにマクロを使用して簡単にTypelistリストを作成できるようにした。しかし、僕はマクロを使わずにTypelistPackage::typeを実装して、可変長型引数を入れてリストを作るようにした。
  • length変数テンプレートはTypelistリストに対していくつかの型を貯めているかを示す。NullTypeの場合には0を返す。
#include <type_traits>

struct NullType final {};

template <class T, class U>
struct Typelist final {
    using Head = T;
    using Tail = U;
};

template <class... TArgs>
struct TypelistPackage final {
private:
    template <class... TTypeArgs>
    struct Constructor;

    template <class TType>
    struct Constructor<TType> final { using type = Typelist<TType, NullType>; };

    template <class TType, class... TTypeArgs>
    struct Constructor<TType, TTypeArgs...> final { 
        using type = Typelist<TType, typename Constructor<TTypeArgs...>::type>; 
    };

public:
    using type = typename TypelistPackage::Constructor<TArgs...>::type;
};

template <class TList> 
constexpr unsigned int length = 0;
template <> 
constexpr unsigned int length<NullType>  = 0;
template <class TT, class TU> 
constexpr unsigned int length<Typelist<TT, TU>> = 1 + length<TU>;
using Charlist = TypelistPackage<char, signed char, unsigned char>::type;
using SingedInts = TypelistPackage<signed char, short int, int, long int>::type;

static_assert(length<SingedInts> == 4, "");
  • TypeAtTypelistのリストからインデックス番地にある型を取り出すために作られたものである。
  • 本で主要に扱われているライブラリーのLokiでは、このTypeAtのデフォルト値を返すバージョンであるTypeAtNonStrictも実装されている。
  • Typelistを使用し番地を探索することはリストや番地の数が多いばあい、ビルドタイムの低下を招きやすい。
template <class TList, unsigned int TIndex>
struct TypeAt;
template <class THead, class TTail>
struct TypeAt<Typelist<THead, TTail>, 0> final { 
    using type = THead; 
};
template <class THead, class TTail, unsigned int TIndex>
struct TypeAt<Typelist<THead, TTail>, TIndex> final { 
    using type = typename TypeAt<TTail, TIndex - 1>::type; 
};

static_assert(std::is_same_v<TypeAt<SingedInts, 1>::type, short int>, "");

3.7 Searching Typelists ~ 3.9 Erasing a Type from a Typelist

  • IndexOfは任意のTypelistに任意の型Tを入れると、Typelistにある型リストから当てはまる型のインデックスを返すようにする。
  • もしNullTypeか型を探せなかったら, -1を返す。
template<class TList, class TTarget> struct IndexOf;
template <class TTarget>
struct IndexOf<NullType, TTarget> { enum { value = -1 }; };
template <class TTarget, class TTail>
struct IndexOf<Typelist<TTarget, TTail>, TTarget> final {
    enum { value = 0 };
};
template <class THead, class TTail, class TTarget> 
struct IndexOf<Typelist<THead, TTail>, TTarget> final {
private:
    enum { temp = IndexOf<TTail, TTarget>::value };
public:
    enum { value = temp == -1 ? -1 : temp + 1 };
};
using SignedIntegrals = SingedInts;
static_assert(IndexOf<SingedIntegrals, int>::value == 2, "");
  • Appendを使用してTypelistを既存のTypelistに入れたり、単一タイプをTypelistに入れるようにする。再起を用いて既存のTypelistNullType部分を入れ替えるようにした。
template <class TList, class TType> struct Append;
template <>
struct Append<NullType, NullType> { using type = NullType; };
template <class TTarget>
struct Append<NullType, TTarget> { using type = Typelist<TTarget, NullType>; };
template <class THead, class TTail>
struct Append<NullType, Typelist<THead, TTail>> {
    using type = Typelist<THead, TTail>;
};
template <class THead, class TTail, class TTarget>
struct Append<Typelist<THead, TTail>, TTarget> {
    using type = Typelist<THead, typename Append<TTail, TTarget>::type>;
};
template <class TList, class TType>
using Append_T = typename Append<TList, TType>::type;
using Charlist          = TypelistPackage_T<char, signed char, unsigned char>;
using OneTypeOnly       = TypelistPackage_T<int>;
using SingedIntegrals   = TypelistPackage_T<signed char, short int, int, long int>;

using SignedIntegralsAdded = Append_T<SingedIntegrals, char>;
static_assert(IndexOf_V<SignedIntegralsAdded, char> == 4, "");
static_assert(IndexOf_V<SignedIntegralsAdded, int> == 2, "");

using Integrals = Append_T<
    SingedIntegrals, 
    TypelistPackage_T<unsigned char, unsigned short, unsigned, unsigned long>
>;
static_assert(IndexOf_V<Integrals, unsigned> == 6, "");
static_assert(IndexOf_V<Integrals, int> == 2, "");
  • EraseTypelistから特定のタイプを取り外す役割をする。本ではEraseと再帰を行うEraseAllが実装しているが私の場合にはbool定数を使ってEraseだけで処理を行うようにした。
template <class TList, class TType, bool TIsAll> struct Erase;
template <class TType, bool TIsAll>
struct Erase<NullType, TType, TIsAll> { using type = NullType; };
template <class TTail, class TType>
struct Erase<Typelist<TType, TTail>, TType, false> { using type = TTail; };
template <class TTail, class TType>
struct Erase<Typelist<TType, TTail>, TType, true> { 
    using type = typename Erase<TTail, TType, true>::type; 
};
template <class THead, class TTail, class TType, bool TIsAll>
struct Erase<Typelist<THead, TTail>, TType, TIsAll> { 
    using type = Typelist<THead, typename Erase<TTail, TType, TIsAll>::type>; 
};
template <class TList, class TType, bool TIsAll = false>
using Erase_T = typename Erase<TList, TType, TIsAll>::type;
using IntegralsExceptChar = Erase_T<SingedIntegrals, char>;
static_assert(IndexOf_V<IntegralsExceptChar, char> == -1, "");
using CharChunk = TypelistPackage_T<char, char, int, int, char, char>;
static_assert(length<Erase_T<CharChunk, char, true>> == 2, "");

3.10 Erasing Duplicates ~ 3.11 Replacing an Element in a Typelist

  • Typelistに存在する重複する型を一つに締め付けるために、AlignTypelistを実装する。実装するに以前に実装sしたEraseを活用する。
  • しかしEraseを活用するときに注意するところはTIsAllfalseというところである。なぜなら、__type1を型引数として使用してEraseを行う。そして__type1は再帰的にAlignTypelistを既に行ってから結果として出すTypelist型を使用する。そのため、再帰的にEraseする必要が無くなる。
template <class TList> struct AlignTypelist;
template <>
struct AlignTypelist<NullType> { using type = NullType; };
template <class THead, class TTail>
struct AlignTypelist<Typelist<THead, TTail>> {
private:
    using __type1 = typename AlignTypelist<TTail>::type;
    using __type2 = Erase_T<__type1, THead>;
public:
    using type = Typelist<THead, __type2>;
};
template <class TList>
using AlignTypelist_T = typename AlignTypelist<TList>::type;
using CharChunk = TypelistPackage_T<char, char, int, int, char, char>;
using AlignedCharChunk = AlignTypelist_T<CharChunk>;
static_assert(1
    && length<AlignedCharChunk> == 2
    && std::is_same_v<TypeAt_T<AlignedCharChunk, 0>, char>
    && std::is_same_v<TypeAt_T<AlignedCharChunk, 1>, int>, "MUST BE SUCCEEDED."
);
  • Replaceは特定の型を探して別の型に入れ替えることを受け取っている。そしてEraseの再帰バージョンのようにReplaceIsApplyAll<false> IsApplyAll<true>を使用して再帰かないかを定められる。
template <class TList, class TTarget, class TReplace, class TIsApplyAll>
struct Replace;
template <class TList, class TTarget, class TReplace, class TIsApplyAll>
using  Replace_T = typename Replace<TList, TTarget, TReplace, TIsApplyAll>::type;
template <class TTarget, class TReplace, class TIsApplyAll>
struct Replace<NullType, TTarget, TReplace, TIsApplyAll> { 
    using type = NullType; 
};
template <class TTail, class TTarget, class TReplace>
struct Replace<Typelist<TTarget, TTail>, TTarget, TReplace, IsApplyAll<false>> {
    using type = Typelist<TReplace, TTail>;
};
template <class TTail, class TTarget, class TReplace>
struct Replace<Typelist<TTarget, TTail>, TTarget, TReplace, IsApplyAll<true>> {
    using type = Typelist<
        TReplace, 
        Replace_T<TTail, TTarget, TReplace, IsApplyAll<true>>
    >;
};
template <class THead, class TTail, class TTarget, class TReplace, class TIsApplyAll>
struct Replace<Typelist<THead, TTail>, TTarget, TReplace, TIsApplyAll> {
    using type = Typelist<
        THead, 
        Replace_T<TTail, TTarget, TReplace, TIsApplyAll>
    >;
};
using CharChunk = TypelistPackage_T<char, char, int, int, char, char>;
static_assert(
    std::is_same_v<
        TypeAt_T<
            AlignTypelist_T<Replace_T<CharChunk, char, short, IsApplyAll<true>>
        >, 0>
    , short>, 
    "MUST BE SUCCEEDED."
);