はじめに
v8 絡みのコードで可変長引数テンプレートを扱いたくて困ってつぶやいていたら、アキラさん(id:faith_and_brave)が教えてくれました:
C++ で可変長引数テンプレートを tuple で受け取ってそれをまた展開するときに一番楽な方法ないかな
@hecomi Boost.Fusionのfusedを使うのが一番簡単ですね! URL URL
2013-02-27 23:42:17 via web to @hecomi
参考
可変長引数をタプルで受け取って格納、後で1個1個取り出して使う
1個1個使うときは boost::fusion::for_each を使います。
#include <iostream> #include <string> #include <boost/fusion/include/vector.hpp> #include <boost/fusion/include/for_each.hpp> namespace fusion = boost::fusion; template <class... Args> class X { public: X(Args&&... args) : args_(std::forward<Args>(args)...) {} template <class F> void use_each_arg_by(F&& f) const { fusion::for_each(args_, f); } private: fusion::vector<Args...> args_; }; template <class... Args> inline X<Args...> make_x(Args&&... args) { return X<Args...>(std::forward<Args>(args)...); } struct print { template <class T> void operator()(T&& val) const { std::cout << val << std::endl; } }; int main() { const auto x1 = make_x("hoge", 4); x1.use_each_arg_by(print()); const auto x2 = make_x(1.5, 4.2f, 'a'); x2.use_each_arg_by(print()); }
結果
hoge 4 1.5 4.2 a
可変長引数をタプルで受け取って格納、後でまとめて使う
boost::fusion::fused は関数の引数をパックして boost::fusion::vector で受け取れるものに変換してくれる感じです。そのヘルパ関数である boost::fusion::make_fused を使って以下の様な感じになります。
#include <iostream> #include <string> #include <boost/fusion/include/vector.hpp> #include <boost/fusion/include/make_fused.hpp> namespace fusion = boost::fusion; template <class... Args> class X { public: X(Args&&... args) : args_(std::forward<Args>(args)...) {} template <class F> void use_all_args_by(F&& f) const { fusion::make_fused(f)(args_); } private: fusion::vector<Args...> args_; }; template <class... Args> inline X<Args...> make_x(Args&&... args) { return X<Args...>(std::forward<Args>(args)...); } int main() { const auto x1 = make_x("hoge", 4); x1.use_all_args_by([](const std::string& s, int i) { std::cout << s << ' ' << i << std::endl; }); const auto x2 = make_x(1.5, 4.2f, 'a'); x2.use_all_args_by([](double d, float f, char c) { std::cout << d << ' ' << f << ' ' << c << std::endl; }); }
結果
hoge 4 1.5 4.2 a
可変長引数をタプルで受け取って格納、後で可変長引数に戻して処理
可変長引数の型 Args を保持しているクラス内ならそのまま Args を利用して処理出来ます。再帰で展開して先ほどと同じ処理をしてみます。先程は lambda 内で受け取る引数の型をわざわざ書きましたが、こうすれば後で型を覚えていなくても OK です。
#include <iostream> #include <string> #include <boost/fusion/include/vector.hpp> #include <boost/fusion/include/make_fused.hpp> namespace fusion = boost::fusion; template <class... Args> class X { public: X(Args&&... args) : args_(std::forward<Args>(args)...) {} template <class F> void use_all_args_by(F&& f) const { fusion::make_fused(f)(args_); } void print() const { use_all_args_by([&](const Args&... args){ print_impl(args...); }); } private: fusion::vector<Args...> args_; template <class Arg, class... RestArgs> void print_impl(const Arg& arg, const RestArgs&... rest_args) const { std::cout << arg << ' '; print_impl(rest_args...); } void print_impl() const { std::cout << std::endl; } }; template <class... Args> inline X<Args...> make_x(Args&&... args) { return X<Args...>(std::forward<Args>(args)...); } int main() { const auto x1 = make_x("hoge", 4); x1.print(); const auto x2 = make_x(1.5, 4.2f, 'a'); x2.print(); }
結果
hoge 4 1.5 4.2 a
おわりに
もっとこうすると楽だよ!みたいな方法があったら教えてください m(_ _)m。