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という名前空間名が微妙なので、何かいい名前を思いつきたい。