Rokiのチラ裏

学生による学習のログ

Variadic templateへのあらゆる操作

※追記
このエントリーにおけるコードはその後改善が施され、それについての新たなエントリが書かれた(See also )。



variadic templateで遊ぶ - in neuroにインスパイアされて、私も少し遊ぶ事にした。折角なのでリンク先の内容とは異なるヘルパを作る。まず簡単なものから。

Length

Variadic templateの長さを取得する。

template<class Head,class... Tail>
constexpr std::size_t Length_v=1+Length_v<Tail...>;
template<class Tail>
constexpr std::size_t Length_v<Tail> =1;

Length_v<int,int,int,int,int,int,int>; // 7

Erase

最初に合致する指定の型を削除する。まず下準備として、かの有名なTypeListをVariadic templateから生成してみる。

using std::experimental::nullopt_t;
template<class L,class R>
struct TypeList{
    using Head=L;
    using Tail=R;
};
template<class L,class... R>
struct ToTypeList{
    using type=TypeList<L,typename ToTypeList<R...>::type>;
};
template<class Tail>
struct ToTypeList<Tail>{
    using type=nullopt_t;
};

これらを用いて、最初に合致した型を削除する。

template<class Target,class... TpL>
class Erase{
    template<class,class>
    struct Erasure;
    template<class Tar,class Head,class Tail>
    struct Erasure<Tar,TypeList<Head,Tail>>{
        using type=TypeList<Head,typename Erasure<Tar,Tail>::type>;
    };
    template<class Tar,class Tail>
    struct Erasure<Tar,TypeList<Tar,Tail>>{
        using type=Tail;
    };
    template<class T>
    struct Erasure<T,nullopt_t>{
        using type=nullopt_t;
    };
public:
    using type=typename Erasure<Target,typename ToTypeList<TpL...>::type>::type;
};

typedef typename Erase<int,char,char,char,float,int,char,char>::type type; 
// TypeList<char, TypeList<char, TypeList<char, TypeList<float, TypeList<char, std::experimental::fundamentals_v1::nullopt_t> > > > >

....と思ったのだが、TypeListに入ったままだと扱いにくいので、この場合初めからVariadic templateを持つPackのみを受け付けるようにしてしまった方が良い。連結とPackからの展開も実装してそれらを利用する。

// Transfer
template<template<class...>class,class...>
struct Transfer;
template<template<class...>class Range,class... All>
struct Transfer<Range,pack<All...>>{
    using type=Range<All...>;
};

// Concat
template<class...>
struct pack{};

template<class,class...>
struct Concat;
template<class... L,class... R>
struct Concat<pack<L...>,pack<R...>>{
    using type=pack<L...,R...>;
};
template<class...L,class... R>
struct Concat<pack<L...>,R...>{
    using type=pack<L...,R...>;
};
template<class L,class... R>
struct Concat<L,pack<R...>>{
    using type=pack<L,R...>;
};
template<class L,class... R>
using Concat_t=typename Concat<L,R...>::type;

// Erase
template<class,class> 
struct Erase;
template<class Key,class Head,class... Tail>
struct Erase<Key,pack<Head,Tail...>>{
    using type=Concat_t<Head,typename Erase<Key,pack<Tail...>>::type>;
};
template<class Key,class... Tail>
struct Erase<Key,pack<Key,Tail...>>{
    using type=pack<Tail...>;
};
template<class Tail>
struct Erase<Tail,pack<>>{
    using type=pack<>;
};
template<class Key,class Pack>
using Erase_t=typename Erase<Key,Pack>::type;

using result_type=typename Transfer<std::tuple,Concat_t<Pack<int,int,int,int>,Pack<char,char,char,char>>>::type; 
// std::tuple<int,int,int,int,char,char,char,char>

using result_type2=typename Transfer<std::tuple,Erase_t<char,Pack<int,int,char,int,char>>>::type;
// std::tuple<int,int,int,char>

飛ばし読みをしているだけだ。

EraseAll

指定の型と合致する全ての型を削除する。

template<class,class>
struct EraseAll;
template<class Key,class Head,class... Tail>
struct EraseAll<Key,pack<Head,Tail...>>{
    using type=Concat_t<Head,typename EraseAll<Key,pack<Tail...>>::type>;
};
template<class Key,class... Tail>
struct EraseAll<Key,pack<Key,Tail...>>{
    using type=typename EraseAll<Key,pack<Tail...>>::type;
};
template<class Tail>
struct EraseAll<Tail,pack<>>{
    using type=pack<>;
};
template<class Key,class Pack>
using EraseAll_t=typename EraseAll<Key,Pack>::type;

using result_type = typename Transfer<
    std::tuple,
    EraseAll_t<char,Pack<int,char,char,float,char,double,char,char,char,bool,int,char,char,float,int,char>>    >::type;
// std::tuple<int, float, double, bool, int, float, int>

No Duplicate

重複を削除する。

template<class,class...>
struct NoDuplicate;
template<class Head,class... Tail>
struct NoDuplicate<pack<Head,Tail...>>{
private:
    using inner_result=Erase_t<Head,typename NoDuplicate<pack<Tail...>>::type>;
public:
    using type=Concat_t<Head,inner_result>;
};
template<>
struct NoDuplicate<pack<>>{
    using type=pack<>;
};
template<class Head,class... Tail>
using NoDuplicate_t=typename NoDuplicate<Head,Tail...>::type;

using result_type = typename Transfer<
    std::tuple,
    NoDuplicate_t<Pack<int,char,char,float,char,double,char,char,char,bool,int,char,char,float,int,char>>
>::type;
// std::tuple<int, char, float, double, bool>

Indexof

Variadic templateから指定された型を検索、そのインデックス値を返す。見つからなかった場合、不正値として-1を返す。これは、パックを使う必要はない。

template<class,class...>
struct Indexof;
template<class Key,class Head,class...Tail>
struct Indexof<Key,Head,Tail...>{
private:
    enum{tmp=Indexof<Key,Tail...>::value};
public:
    enum{value=tmp==-1?tmp:1+tmp};
};
template<class Head,class... Tail>
struct Indexof<Head,Head,Tail...>{
    enum{value=0};
};
template<class Tail>
struct Indexof<Tail>{
    enum{value=-1};
};
template<class Key,class... Args>
constexpr std::size_t Indexof_v=Indexof<Key,Args...>::value;

Indexof_v<int,char,char,char,char> // -1
Indexof_v<int,char,char,char,int> // 3
Indexof_v<int,int,char,char,char> // 0

ざっと書いてこんなものだろうか。よくよく考えればこの主軸となっている概念は2001年頃からある