Typelists
- Typelistというのは支援する値に関した一連の型をリスト化して提供する道具である。3章ではこのTypelistを実装する。
- Typelistが有用に使用されるところは、Abstract Factoryか、Visitorパターンでよく使われる。
3.2 Defining Tyeplists ~ 3.6 Indexed Access
Typelist
は2つのタイプを持つことができる。この2つのタイプの別称をHead
、Tail
と呼ぶ。そしてTypelist
のTail
にまた別の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, "");
TypeAt
はTypelist
のリストからインデックス番地にある型を取り出すために作られたものである。
- 本で主要に扱われているライブラリーの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
に入れるようにする。再起を用いて既存のTypelist
のNullType
部分を入れ替えるようにした。
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, "");
Erase
はTypelist
から特定のタイプを取り外す役割をする。本では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
を活用するときに注意するところはTIsAll
がfalse
というところである。なぜなら、__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
の再帰バージョンのようにReplace
もIsApplyAll<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."
);