Boost.Fusionのアダプト機構を言語サポートする提案

N3326 - Sequential access to data members and base sub-objects


Boost.Fusionのアダプト機構を言語サポートする提案が標準化委員会に提出されました。
Boost.Fusionは、ユーザー定義型をシーケンスと見なしてfor_eachやtransform, accumulateといった操作を可能にするための機構が用意されていますが、そのためには、ユーザー定義型をFusion Sequenceとして登録するためのコードが必要になります。

namespace demo {
    struct employee {
        std::string name;
        int age;
    };
}

BOOST_FUSION_ADAPT_STRUCT(
    demo::employee,
    (std::string, name)
    (int, age)
)

// demo::employee is now a Fusion sequence

このような機構があることで、構文解析の結果をユーザー定義型に一発変換したりすることができるので、シリアライズを自動化するために利用できたりするのですが、やはり利用する型を登録するためのコードが邪魔です。


今回提案されたのは、std::meta::members()という特殊な関数にユーザー定義型を渡すことで対応するstd::tupleを得る、というものです。

demo::employee x = {"Akira", 26};
std::tuple<std::string&, int&> tup = std::meta::members(x).value;

これがあれば、0番目のメンバ変数、1番目のメンバ変数を取得する、といった操作が簡単に書けます。

std::string& name = std::get<0>(tup);
int& age = std::get<1>(tup);

Boost.Fusionにあるように、タプルのイテレータを実装してしまえば、リストのように扱うこともできます。

demo::employee x = {"Akira", 26};
for_each(std::meta::members(x).value, std::cout << _1 << std::endl); // メンバ変数を列挙する


それと、この機構があるとユーザー定義型を比較する演算子が簡単に定義できます。
std::tupleが比較演算子を持っているので、ユーザー定義型をstd::tupleに変換して単に演算子で比較すればいいのです。

struct Complex {
    double x, y;

    constexpr Complex(double x, double y) noexcept
        : x{x}, y{y} {}
};

constexpr bool operator==(Complex a, Complex b) noexcept
{
    return std::meta::members(a).value == std::meta::members(b).value;
}

他にも、基本クラスの型リストを取得するstd::meta::bases()関数や、再帰的なタプルを得るためのrecursive_members()関数なども一緒に提案されています。


これは非常に楽しみですね。metaという名前空間名が微妙なので、何かいい名前を思いつきたい。