Boost.Xpressiveではcheckで正当性チェックができるのにBoost.Spirit.Qiではできないのかなーと思ったらできるようです。まずは関数指定をする基本的な書き方:
#include <iostream> #include <string> #include <boost/spirit/include/qi.hpp> namespace qi = boost::spirit::qi; void fail(int x, boost::spirit::unused_type, bool& pass) { pass = x == 2; } int main() { const std::string s = "3"; std::string::const_iterator it = s.begin(); if (!qi::parse(it, s.end(), qi::int_[fail])) { std::cout << "fail" << std::endl; } else { std::cout << "success" << std::endl; } }
ドキュメントを見てもよくわからなかったので、libs/spirit/test/qi/action.cppを見たらそれらしいコードがあったのでした。参照で渡されるpassパラメータをfalseにすればマッチ失敗として扱われます。
ここでは、"2"だったらsuccess、それ以外はfailになります。
次に、プレースホルダを使って書いてみます。PhoenixではなくBoost.Lambdaを使いました。
#include <iostream> #include <string> #include <boost/spirit/include/qi.hpp> #include <boost/lambda/lambda.hpp> namespace qi = boost::spirit::qi; using namespace boost::lambda; int main() { const std::string s = "3"; std::string::const_iterator it = s.begin(); if (!qi::parse(it, s.end(), qi::int_[_3 = _1 == 2])) { std::cout << "fail" << std::endl; } else { std::cout << "success" << std::endl; } }
_1は許せるけど、_3って書いても何者なのかわからないですね・・・。切り出しましょう。
#include <iostream> #include <string> #include <boost/spirit/include/qi.hpp> #include <boost/lambda/lambda.hpp> namespace qi = boost::spirit::qi; using namespace boost::lambda; template <class Expr> struct valid_check_t { Expr expr; valid_check_t(const Expr& expr_) : expr(expr_) {} template <class T> void operator()(const T& x, boost::spirit::unused_type, bool& pass) const { pass = expr(x); } }; template <class Expr> valid_check_t<Expr> valid_check(const Expr& expr) { return valid_check_t<Expr>(expr); } int main() { const std::string s = "3"; std::string::const_iterator it = s.begin(); if (!qi::parse(it, s.end(), qi::int_[valid_check(_1 == 2)])) { std::cout << "fail" << std::endl; } else { std::cout << "success" << std::endl; } }
これでだいぶ使いやすくなりました。