c++ - Boost Spirit int into string rule -
assuming have following rule expecting string value should convert integer:
integer %= attr_cast<int,std::string>( lexeme[ -(char_('+') | char_('-')) >> char_("1-9") >> *char_("0-9") ] ) ;
so defined transformation structure:
template <> struct transform_attribute<int, std::string, qi::domain> { // input type typedef std::string& type; static std::string pre(int & d) { return ""; } static void post(int & val, const type attr) { val = boost::lexical_cast<int>(attr); } static void fail(int &) {} };
unfortunately expects convert int reference of left rule string reference (i removed reference operator not work). on internet saw examples working other way int string don't use references int. how can define proper transformation customly?
first off, don't have write this:
integer %= qi::int_; // job done
next, write qi::attr_cast<std::string>(...)
easier , more efficient doing as_string[ ... ]
.
next, if want parse ints laborious way, try this:
bool ok = parse(f, l, (matches['-'] | -lit('+') >> attr(false)) [ phx::ref(negative) = _1 ] >> // sign eps [ _val = 0 ] >> +digit [ _val *= 10, _val += (_1 - '0') ], parsed);
see live demo on coliru testing (u)int(8,16,32,64,max)_t
on whole range:
the qi::int_parser<>
template used above (via qi::int_
) generalization of approach, more efficient.
now, can of course traits trick if insist:
namespace boost { namespace spirit { namespace traits { template <typename int> struct assign_to_attribute_from_value< int, std::string, typename std::enable_if<std::is_integral<int>::value, void>::type // enabler > { static void call(std::string const& val, int& attr) { //std::cout << __pretty_function__ << "('" << val << "')\n"; attr = boost::lexical_cast<int>(val); } }; } } }
now shooting fly canon. never mind boost::lexical_cast
doesn't handle uint8_t
, int8_t
correctly purpose (treating them specially char
, unsigned char
?), had also hard code exception these:
// boost lexical_cast not usefully support `char` types integrals... (sic) template <> struct assign_to_attribute_from_value<signed char, std::string> { static void call(std::string const& val, signed char& attr) { int tmp; assign_to_attribute_from_value<int, std::string>::call(val, tmp); attr = static_cast<signed char>(tmp); } }; template <> struct assign_to_attribute_from_value<unsigned char, std::string> { static void call(std::string const& val, unsigned char& attr) { unsigned int tmp; assign_to_attribute_from_value<unsigned int, std::string>::call(val, tmp); attr = static_cast<unsigned char>(tmp); } };
now test cases passed with
int parsed = 0; bool ok = parse(f, l, as_string [ -char_("-+") >> +digit ], parsed);
see live on coliru well.
now let me conclude "sane" approach: don't reinvent wheel
int parsed = 0; bool ok = qi::parse(f, l, qi::auto_, parsed);
full program live on coliru
#include <boost/spirit/include/qi.hpp> template <typename int> void do_test() { (int const testcase : { std::numeric_limits<int>::min(), int(), std::numeric_limits<int>::max() }) { auto const input = std::to_string(testcase); auto f(input.begin()), l(input.end()); int parsed = 0; bool ok = boost::spirit::qi::parse(f, l, boost::spirit::qi::auto_, parsed); if (!ok || f!=l) throw std::runtime_error("parse error"); std::cout << std::boolalpha << (testcase==parsed) << "\t" << testcase << " -> " << parsed << "\n"; } } int main() { do_test<int16_t>(); do_test<uint16_t>(); do_test<int32_t>(); do_test<uint32_t>(); do_test<int64_t>(); do_test<uint64_t>(); do_test<intmax_t>(); do_test<uintmax_t>(); }
Comments
Post a Comment