構造体を tuple-like としてアダプトするマクロを書いた

C++11 の規格には,tuple-like という言葉が登場します.これは所謂コンセプトで,tuple-like コンセプトを満たす型 T は,std::tuple_size<T>,std::tuple_element<I, T>,get<I>(t) を持ちます.標準で定義されたものでは,std::pair<T1, T2>,std::tuple<Types...>,std::array<T, N> がこれを満たします.

typedef std::pair<int, double> pair_t;

// std::tuple_size
static_assert(std::tuple_size<pair_t>::value == 2, "");

// std::tuple_element
static_assert(std::is_same<std::tuple_element<0, pair_t>::type, int>::value, "");

// get
pair_t p = { 42, 3.14 };
assert((&std::get<0>(p) == &p.first));

さて,ユーザー定義型をこれにアダプトするマクロを書いてみました.
https://github.com/iorate/std-tuple-adapted
使い方は BOOST_FUSION_ADAPT_STRUCT などと似ています.

namespace NS {

    struct A
    {
        int i;
        double d;
    };

}

IORATE_STD_TUPLE_ADAPT_STRUCT((NS), (A), (i)(d))

void example1()
{
    // std::tuple_size
    static_assert(std::tuple_size<NS::A>::value == 2, "");

    // std::tuple_element
    static_assert(std::is_same<std::tuple_element<0, NS::A>::type, int>::value, "");

    // get
    NS::A a = { 42, 3.14 };
    using std::get;
    assert((&get<0>(a) == &a.i));
}

注意すべき点として,get<I> は ADL で呼び出すわけですが,その前に get という名前の関数テンプレートを見えるようにしておかなければなりません*1.もっとも,予め using std::get; としておけばよいので,swap と使い方は同じになります.boost::swap に相当するものがあると便利でしょう.上の所に一緒においてあります.

void example2()
{
    std::pair<int, double> p = { 42, 3.14 };
    assert((&iorate::get<0>(p) == &p.first);

    NS::A a = { 42, 3.14 };
    assert((&iorate::get<0>(a) == &a.i));
}

また,クラステンプレート用のマクロもあります.

namespace NS {

    template <class T>
    struct X
    {
        T t;
    };

}

IORATE_STD_TUPLE_ADAPT_TPL_STRUCT((NS), (class T), (X<T>), (t))

最後に,このアダプトが何の役に立つかということですが*2,tuple-like コンセプトを利用したライブラリを使う際に役に立ちます.そのままですね.現在あるライブラリでは,std::tuple_cat などがあります.…それくらいしかありませんが.

*1:14.8.1/8 参照

*2:そういうことは最初に書くべきです