【Boost Advent Calendar 2011】phoenix::progress_display【6日目】
これは Boost Advent Calendar 2011 6日目の記事です。
さて、個人的に3回目の Advent Calendar です。
Boost Advent は個人的に気になっている Boost.TTI や Boost.QVM について書こうと思ったんですが、時期的に今しかチャンスが無いのでネタに走ります。
progress_display は滅びぬ、何度でも蘇るさ。
[Boost.Phoenix とは]
Boost.Phoenix は、Boost 1.47.0 で公開されたライブラリです。
Boost.Lambda と同様に 無名関数の定義する事が出来ます。
元々は Boost.Spirit のサブライブラリとして、公開されていましたが、Boost 1.47.0 から V3 という形で公開されました。
基本的な使い方は Boost.Lambda と変わらないので Boost.Lambda を使ったことがあれば、使い方はそんなに難しくないと思います。
#include <boost/phoenix.hpp> #include <boost/range/irange.hpp> #include <boost/range/adaptor/filtered.hpp> using boost::phoenix::arg_names::arg1; using boost::phoenix::arg_names::arg2; arg1(1, 2, 3); // == 1 arg2(1, 2, 3); // == 2 (arg1 + arg2)(3, 2); // == 5 using boost::adaptors::filtered; boost::irange(0, 9)|filtered(arg1 % 2 == 0); // => 0, 2, 4, 6, 8
また、ユーザ側で簡単に Boost.Phoenix の構文としてアダプトすることも出来ます。
#include <boost/phoenix/function/adapt_function.hpp> int minus_impl(int a, int b){ return a - b; } // Boost.Phoenix へアダプト BOOST_PHOENIX_ADAPT_FUNCTION(int, minus, minus_impl, 2); minus(arg1, arg2)(6, 2); // == 4 // アダプトしない場合は bind を使用 boost::phoenix::bind(&minus_impl, arg1, arg2)(6, 2); // == 4
ということで、この不死鳥の名を持つライブラリで progress_display を復活させようではありませんか!
[phoenix::progress_display]
#include <boost/progress.hpp> #include <boost/thread.hpp> #include <boost/phoenix/core.hpp> #include <boost/phoenix/operator.hpp> #include <boost/phoenix/function.hpp> BOOST_PHOENIX_DEFINE_EXPRESSION( (boost)(phoenix)(progress_display), (boost::phoenix::meta_grammar) // Count (boost::phoenix::meta_grammar) // OS (boost::phoenix::meta_grammar) // s1 (boost::phoenix::meta_grammar) // s2 (boost::phoenix::meta_grammar) // s3 (boost::phoenix::meta_grammar) // Do ) namespace boost{ namespace phoenix{ struct progress_display_eval{ typedef void result_type; template< typename Count, typename OS, typename S1, typename S2, typename S3, typename Do, typename Context > result_type operator ()( Count const& count, OS& os, S1 const& s1, S2 const& s2, S3 const& s3, Do const& do_, Context const ctx ) const{ boost::progress_display show_progress( boost::phoenix::eval(count, ctx), boost::phoenix::eval(os, ctx), boost::phoenix::eval(s1, ctx), boost::phoenix::eval(s2, ctx), boost::phoenix::eval(s3, ctx) ); for(unsigned long i = 0 ; i < boost::phoenix::eval(count, ctx) ; ++i){ boost::phoenix::eval(do_, ctx); ++show_progress; } } }; template<typename Dummy> struct default_actions::when<rule::progress_display, Dummy> : call<progress_display_eval, Dummy>{}; template< typename Count, typename OS, typename S1, typename S2, typename S3 > struct progress_display_gen{ progress_display_gen( Count const& count, OS os, S1 const& s1, S2 const& s2, S3 const& s3 ) : count(count) , os(os) , s1(s1) , s2(s2) , s3(s3) {} template<typename Do> typename expression::progress_display<Count, OS, S1, S2, S3, Do>::type const operator [](Do const& do_) const{ return expression::progress_display<Count, OS, S1, S2, S3, Do>::make (count, os, s1, s2, s3, do_); } private: Count const& count; OS os; S1 const& s1; S2 const& s2; S3 const& s3; }; template<typename Count> inline progress_display_gen< Count, boost::reference_wrapper<std::ostream>, std::string, std::string, std::string > const progress_display( Count const& count, std::ostream& os = std::cout, std::string const& s1 = "\n", std::string const& s2 = "", std::string const& s3 = "" ){ return progress_display_gen< Count, boost::reference_wrapper<std::ostream>, std::string, std::string, std::string >(count, boost::ref(os), s1, s2, s3); } } // namespace phoenix } // namespace boost #include <boost/phoenix/bind.hpp> #include <boost/phoenix/stl.hpp> #include <boost/phoenix/operator.hpp> #include <boost/phoenix/scope/let.hpp> #include <boost/phoenix/function.hpp> #include <boost/phoenix/function/adapt_function.hpp> #include <boost/range/algorithm/for_each.hpp> #include <boost/lexical_cast.hpp> #include <boost/function.hpp> #include <iostream> #include <vector> #include <string> int fibonacci_impl(int n){ return n == 0 ? 0 : n == 1 ? 1 : fibonacci_impl(n - 1) + fibonacci_impl(n - 2); } BOOST_PHOENIX_ADAPT_FUNCTION(int, fibonacci, fibonacci_impl, 1); template<typename Target> struct lexical_cast_impl{ typedef Target result_type; template<typename Source> result_type operator ()(Source const& source) const{ return boost::lexical_cast<Target>(source); } }; template<typename Target, typename T> typename boost::result_of< boost::phoenix::function<lexical_cast_impl<Target> >(T const&) >::type lexical_cast(T const& t){ return boost::phoenix::function<lexical_cast_impl<Target> >()(t); } BOOST_PHOENIX_ADAPT_FUNCTION(void, sleep, boost::this_thread::sleep, 1); int main(){ namespace phx = boost::phoenix; using boost::phoenix::arg_names::arg1; using phx::local_names::_n; // // [構文] // boost::phoenix::progress_display(count, ostream, s1, s2, s3)[ // statement, // ... // ] // using boost::phoenix::arg_names::arg1; // auto f = boost::phoenix::progress_display(arg1)[ // // arg1 回実行される // sleep(boost::posix_time::milliseconds(100)) // ]; // f(100ul); // // FibBuzz を処理してみる // std::vector<std::string> result; int i = 1; boost::function<void(unsigned long)> f = boost::phoenix::progress_display( arg1, std::cout, "FibBuzz:", " ", " " )[ phx::let(_n = fibonacci(phx::ref(i)))[ phx::push_back(phx::ref(result), phx::if_else(_n % 15 == 0, "FizzBuzz", phx::if_else(_n % 5 == 0, "Fizz", phx::if_else(_n % 3 == 0, "Buzz", ::lexical_cast<std::string>(_n) )))) ], ++phx::ref(i), // それっぽく見せるための wait sleep(boost::posix_time::milliseconds(100)) ]; f(40ul); std::cout << "result:" << std::endl; boost::for_each(result, std::cout << arg1 << ", "); return 0; }
[出力]
こんな感じで Boost.Phoenix を使えば簡単にオレオレ構文を定義することが出来ます。
やったね☆(ゝω・)vキャピ
単に progress_display をラップしただけとは言ってはいけない
あ、ちなみに『バン(∩`・ω・)バンバンバンバン゙ン』は、Vim で出力されているのでプログラムとは関係ありません。
かわいいですね。
[まとめ]
構文を定義する場合は、Boost.Phoenix の各 statement(for_ とか while_ とか)のコードが参考になると思います。
Boost.Phoenix は用法用量を守って正しくお使い下さい。
[次回予告]
明日の Boost Advent Calendar 2011 は、@fjnli さんです。
明日を楽しみにして1日を過ごしましょう:)
ところで、Boost Advent は2週目をやってもいいのかしら。
[動作確認]
- boost 1.48.0
- gcc 4.7
- clang 3.0
- msvc 2010