使用 boost::spirit::qi 解析带分隔符的数字
Using boost::spirit::qi to parse numbers with separators
我正在尝试使用 boost::spirit::qi 进行一些解析。它实际上进展顺利,我成功地根据后缀成功地解析了各种基数中的数字。示例:123、c12h、777o、110101b。
然后我想添加允许完全忽略分隔符的功能,以允许解析 123_456 或 1101_0011b 等值。我尝试使用跳过解析器,但我高度怀疑我完全误解了它的使用方式。它编译得很好,但我试图让它忽略下划线根本没有任何作用。任何有关如何使它做我想做的事情的建议都将不胜感激。我的测试代码如下:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using qi::_val;
using qi::_1;
using qi::skip;
using qi::uint_parser;
using ascii::char_;
template <typename Iterator>
struct unsigned_parser : qi::grammar<Iterator, uint64_t()> {
unsigned_parser() : unsigned_parser::base_type(start) {
uint_parser<uint64_t, 10> dec_parser;
uint_parser<uint64_t, 16> hex_parser;
uint_parser<uint64_t, 8> oct_parser;
uint_parser<uint64_t, 2> bin_parser;
start = skip(char_('_'))[
/* binary with suffix */
(bin_parser[_val=_1] >> char_("bByY"))
/* octal with suffix */
| (oct_parser[_val=_1] >> char_("qQoO"))
/* hexadecimal with suffix */
| (hex_parser[_val=_1] >> char_("hHxX"))
/* decimal with optional suffix */
| (dec_parser[_val=_1] >> -char_("dDtT"))
];
}
qi::rule<Iterator, uint64_t()> start;
};
int main(int argv, const char *argc[]) {
typedef std::string::const_iterator iter;
unsigned_parser<iter> up;
uint64_t val;
if (argv != 2) {
std::cerr << "Usage: " << argc[0] << " <input>" << std::endl;
return 1;
}
std::string test(argc[1]);
iter i = test.begin();
iter end = test.end();
bool rv = parse(i, end, up, val);
if (rv && i == end) {
std::cout << "Succeeded: " << val << std::endl;
return 0;
}
if (rv) {
std::cout << "Failed partial parse: " << val << std::endl;
return 1;
}
std::cout << "Failed." << std::endl;
return 1;
}
如果您真的想要以"nice"的方式进行操作,则必须将其破解成numeric_utils.hpp中的extract_int
。
更好的是,您希望将其作为一种策略 class,就像 real_parser
使用的 real_policies
一样。因为只是将更多分支与现有的 通用 整数处理代码混合只会使事情复杂化,并有可能减慢 any 整数解析。
我没有这样做过。但是,我 在这里有一个概念验证方法:
请注意,由于上述原因,这没有经过充分测试,不适合认真使用,但您可以将其用作灵感。您可能只想复制整个 uint_parser
指令并将其粘贴到您的 Spirit Repository 位置 .
补丁
比较简单。如果您定义 ALLOW_SO_UNDERSCORE_HACK
,您将绕过下划线插入循环展开宏:
#if defined(ALLOW_SO_UNDERSCORE_HACK)
# define SPIRIT_SO_SKIP_UNDERSCORE_HACK() \
if ('_' == *it) { \
++it; \
continue; \
}
#else
# define SPIRIT_SO_SKIP_UNDERSCORE_HACK()
#endif
唯一真正的复杂性来自“透视:在该翻译单元中进行的优化。
有一个相当随意的选择(禁止)允许在前导零中使用下划线。我选择这样做:
#if defined(ALLOW_SO_UNDERSCORE_HACK)
// skip leading zeros
for(;it != last;++it) {
if ('0' == *it && leading_zeros < MaxDigits) {
++leading_zeros;
continue;
} else if ('_' == *it) {
continue;
}
break;
}
#else
最后,uderscores 不计入MinDigits
和MaxDigits
限制
演示
下面的测试程序演示了一些东西。 注意分支的重新排序。
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct unsigned_parser : qi::grammar<Iterator, uint64_t()> {
unsigned_parser() : unsigned_parser::base_type(start) {
using namespace qi;
uint_parser<uint64_t, 10> dec_parser;
uint_parser<uint64_t, 16> hex_parser;
uint_parser<uint64_t, 8> oct_parser;
uint_parser<uint64_t, 2> bin_parser;
start = eps(false)
| (hex_parser >> omit[ char_("hHxX")]) /* hexadecimal with suffix */
| (oct_parser >> omit[ char_("qQoO")]) /* octal with suffix */
| (bin_parser >> omit[ char_("bByY")]) /* binary with suffix */
| (dec_parser >> omit[-char_("dDtT")]) /* decimal with optional suffix */
;
}
qi::rule<Iterator, uint64_t()> start;
};
int main(int argv, const char *argc[]) {
typedef std::string::const_iterator iter;
unsigned_parser<iter> up;
for (auto const& test : std::vector<std::string>(argc+1, argc+argv)) {
iter i = test.begin(), end = test.end();
uint64_t val;
bool rv = parse(i, end, up, val);
std::cout << (rv?"Successful":"Failed") << " parse: '" << test << "' -> " << val << "\n";
if (i != end)
std::cout << " ** Remaining unparsed: '" << std::string(i,end) << "'\n";
}
}
如果您使用命令行参数调用它 123_456 123456 1_bh 0_010Q 1010_1010_0111_0111_b
它将打印:
Successful parse: '123_456' -> 123456
Successful parse: '123456' -> 123456
Successful parse: '1_bh' -> 27
Successful parse: '0_010Q' -> 8
Successful parse: '1010_1010_0111_0111_b' -> 43639
上市
用于在 SO:
上保存的完整补丁(在 boost-1.57.0
标签上)
commit 24b16304f436bfd0f6e2041b2b7be0c8677c7e75
Author: Seth Heeren <sgheeren@gmail.com>
Date: Thu Mar 19 01:44:55 2015 +0100
rough patch for exposition of my answer only
diff --git a/include/boost/spirit/home/qi/numeric/detail/numeric_utils.hpp b/include/boost/spirit/home/qi/numeric/detail/numeric_utils.hpp
index 5137f87..1ced164 100644
--- a/include/boost/spirit/home/qi/numeric/detail/numeric_utils.hpp
+++ b/include/boost/spirit/home/qi/numeric/detail/numeric_utils.hpp
@@ -262,10 +262,21 @@ namespace boost { namespace spirit { namespace qi { namespace detail
///////////////////////////////////////////////////////////////////////////
// extract_int: main code for extracting integers
///////////////////////////////////////////////////////////////////////////
+#if defined(ALLOW_SO_UNDERSCORE_HACK)
+# define SPIRIT_SO_SKIP_UNDERSCORE_HACK() \
+ if ('_' == *it) { \
+ ++it; \
+ continue; \
+ }
+#else
+# define SPIRIT_SO_SKIP_UNDERSCORE_HACK()
+#endif
+
#define SPIRIT_NUMERIC_INNER_LOOP(z, x, data) \
if (!check_max_digits<MaxDigits>::call(count + leading_zeros) \
|| it == last) \
break; \
+ SPIRIT_SO_SKIP_UNDERSCORE_HACK() \
ch = *it; \
if (!radix_check::is_valid(ch) || !extractor::call(ch, count, val)) \
break; \
@@ -301,12 +312,25 @@ namespace boost { namespace spirit { namespace qi { namespace detail
std::size_t leading_zeros = 0;
if (!Accumulate)
{
+#if defined(ALLOW_SO_UNDERSCORE_HACK)
+ // skip leading zeros
+ for(;it != last;++it) {
+ if ('0' == *it && leading_zeros < MaxDigits) {
+ ++leading_zeros;
+ continue;
+ } else if ('_' == *it) {
+ continue;
+ }
+ break;
+ }
+#else
// skip leading zeros
while (it != last && *it == '0' && leading_zeros < MaxDigits)
{
++it;
++leading_zeros;
}
+#endif
}
typedef typename
@@ -366,6 +390,7 @@ namespace boost { namespace spirit { namespace qi { namespace detail
#define SPIRIT_NUMERIC_INNER_LOOP(z, x, data) \
if (it == last) \
break; \
+ SPIRIT_SO_SKIP_UNDERSCORE_HACK() \
ch = *it; \
if (!radix_check::is_valid(ch)) \
break; \
@@ -399,12 +424,25 @@ namespace boost { namespace spirit { namespace qi { namespace detail
std::size_t count = 0;
if (!Accumulate)
{
+#if defined(ALLOW_SO_UNDERSCORE_HACK)
+ // skip leading zeros
+ for(;it != last;++it) {
+ if ('0' == *it) {
+ ++count;
+ continue;
+ } else if ('_' == *it) {
+ continue;
+ }
+ break;
+ }
+#else
// skip leading zeros
while (it != last && *it == '0')
{
++it;
++count;
}
+#endif
if (it == last)
{
@@ -472,6 +510,7 @@ namespace boost { namespace spirit { namespace qi { namespace detail
};
#undef SPIRIT_NUMERIC_INNER_LOOP
+#undef SPIRIT_SO_SKIP_UNDERSCORE_HACK
///////////////////////////////////////////////////////////////////////////
// Cast an signed integer to an unsigned integer
噢。没有人应该为 Spirit 解析器上下文等实现细节操心 ,除非您正在扩展库并实现自己的解析器指令。
在那之前,phoenix::function<>
、phoenix::bind
甚至 BOOST_PHOENIX_ADAPT_FUNCTION
对任何人来说都足够了。
这里有两种方法可以解决您的问题,无需对库进行任何修补。
直接解析 Live On Coliru
这可以看作 "naive" 仅使用 Qi 和简单的语义操作来解析不同样式的整数的方法:
start =
eps [_val=0] >> +(char_("0-9a-fA-F") [ _val = _val*16 + _decode(_1) ] | '_')>> char_("hHxX") /* hexadecimal with suffix */
| eps [_val=0] >> +(char_("0-7") [ _val = _val* 8 + _decode(_1) ] | '_')>> char_("qQoO") /* octal with suffix */
| eps [_val=0] >> +(char_("01") [ _val = _val* 2 + _decode(_1) ] | '_')>> char_("bByY") /* binary with suffix */
| eps [_val=0] >> +(char_("0-9") [ _val = _val*10 + _decode(_1) ] | '_')>> -char_("dDtT") /* decimal with optional suffix */
;
当然,你会想知道_decode
是什么样子的。那你自己定义吧:
struct decode {
template <typename> struct result { typedef int type; };
template <typename Ch> int operator()(Ch ch) const {
if (ch>='0' && ch<='9') return ch - '0';
if (ch>='a' && ch<='z') return ch - 'a' + 10;
if (ch>='A' && ch<='Z') return ch - 'A' + 10;
assert(false);
}
};
boost::phoenix::function<decode> _decode;
使用 BOOST_PHOENIX_ADAPT_FUNCTION
宏 Live On Coliru
您可以使用宏来代替定义函数对象
int decode(char ch) {
if (ch>='0' && ch<='9') return ch - '0';
if (ch>='a' && ch<='z') return ch - 'a' + 10;
if (ch>='A' && ch<='Z') return ch - 'A' + 10;
assert(false);
}
BOOST_PHOENIX_ADAPT_FUNCTION(int, _decode, decode, 1)
使用std::strtoul
Live On Coliru
当然,上面的内容可能有点"complex",因为它要求您处理整数运算和数字解码的具体细节。
此外,"naive" 方法会做一些重复的工作,以防文字是像“101_101”这样的十进制值。它将计算 子结果 的十六进制、八进制 和 二进制分支,然后才意识到它是十进制。
所以我们可以改变顺序:
start =
(raw[+char_("_0-9a-fA-F")] >> char_("hHxX")) [ _val = _strtoul(_1,16) ] /* hexadecimal with suffix */
| (raw[+char_("_0-7")] >> char_("qQoO")) [ _val = _strtoul(_1, 8) ] /* octal with suffix */
| (raw[+char_("_01")] >> char_("bByY")) [ _val = _strtoul(_1, 2) ] /* binary with suffix */
| (raw[+char_("_0-9")] >> -char_("dDtT")) [ _val = _strtoul(_1,10) ] /* decimal with optional suffix */
;
你又会好奇我们是如何实现的_evaluate
?它是一个函数,它从 raw
(这是一个迭代器范围)和基础中获取合成属性,这在那时肯定是已知的:
struct strtoul_f {
template <typename, typename> struct result { typedef uint64_t type; };
template <typename Raw, typename Int> uint64_t operator()(Raw raw, Int base) const {
std::string s(raw.begin(), raw.end());
s.erase(std::remove(s.begin(), s.end(), '_'), s.end());
char *f(&s[0]), *l(f+s.size());
return std::strtoul(f, &l, base);
}
};
boost::phoenix::function<strtoul_f> _strtoul;
如您所见,唯一复杂的是首先从范围中删除 _
。
我正在尝试使用 boost::spirit::qi 进行一些解析。它实际上进展顺利,我成功地根据后缀成功地解析了各种基数中的数字。示例:123、c12h、777o、110101b。
然后我想添加允许完全忽略分隔符的功能,以允许解析 123_456 或 1101_0011b 等值。我尝试使用跳过解析器,但我高度怀疑我完全误解了它的使用方式。它编译得很好,但我试图让它忽略下划线根本没有任何作用。任何有关如何使它做我想做的事情的建议都将不胜感激。我的测试代码如下:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using qi::_val;
using qi::_1;
using qi::skip;
using qi::uint_parser;
using ascii::char_;
template <typename Iterator>
struct unsigned_parser : qi::grammar<Iterator, uint64_t()> {
unsigned_parser() : unsigned_parser::base_type(start) {
uint_parser<uint64_t, 10> dec_parser;
uint_parser<uint64_t, 16> hex_parser;
uint_parser<uint64_t, 8> oct_parser;
uint_parser<uint64_t, 2> bin_parser;
start = skip(char_('_'))[
/* binary with suffix */
(bin_parser[_val=_1] >> char_("bByY"))
/* octal with suffix */
| (oct_parser[_val=_1] >> char_("qQoO"))
/* hexadecimal with suffix */
| (hex_parser[_val=_1] >> char_("hHxX"))
/* decimal with optional suffix */
| (dec_parser[_val=_1] >> -char_("dDtT"))
];
}
qi::rule<Iterator, uint64_t()> start;
};
int main(int argv, const char *argc[]) {
typedef std::string::const_iterator iter;
unsigned_parser<iter> up;
uint64_t val;
if (argv != 2) {
std::cerr << "Usage: " << argc[0] << " <input>" << std::endl;
return 1;
}
std::string test(argc[1]);
iter i = test.begin();
iter end = test.end();
bool rv = parse(i, end, up, val);
if (rv && i == end) {
std::cout << "Succeeded: " << val << std::endl;
return 0;
}
if (rv) {
std::cout << "Failed partial parse: " << val << std::endl;
return 1;
}
std::cout << "Failed." << std::endl;
return 1;
}
如果您真的想要以"nice"的方式进行操作,则必须将其破解成numeric_utils.hpp中的extract_int
。
更好的是,您希望将其作为一种策略 class,就像 real_parser
使用的 real_policies
一样。因为只是将更多分支与现有的 通用 整数处理代码混合只会使事情复杂化,并有可能减慢 any 整数解析。
我没有这样做过。但是,我 在这里有一个概念验证方法:
请注意,由于上述原因,这没有经过充分测试,不适合认真使用,但您可以将其用作灵感。您可能只想复制整个 uint_parser
指令并将其粘贴到您的 Spirit Repository 位置 .
补丁
比较简单。如果您定义
ALLOW_SO_UNDERSCORE_HACK
,您将绕过下划线插入循环展开宏:#if defined(ALLOW_SO_UNDERSCORE_HACK) # define SPIRIT_SO_SKIP_UNDERSCORE_HACK() \ if ('_' == *it) { \ ++it; \ continue; \ } #else # define SPIRIT_SO_SKIP_UNDERSCORE_HACK() #endif
唯一真正的复杂性来自“透视:在该翻译单元中进行的优化。
有一个相当随意的选择(禁止)允许在前导零中使用下划线。我选择这样做:
#if defined(ALLOW_SO_UNDERSCORE_HACK) // skip leading zeros for(;it != last;++it) { if ('0' == *it && leading_zeros < MaxDigits) { ++leading_zeros; continue; } else if ('_' == *it) { continue; } break; } #else
最后,uderscores 不计入
MinDigits
和MaxDigits
限制
演示
下面的测试程序演示了一些东西。 注意分支的重新排序。
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
template <typename Iterator>
struct unsigned_parser : qi::grammar<Iterator, uint64_t()> {
unsigned_parser() : unsigned_parser::base_type(start) {
using namespace qi;
uint_parser<uint64_t, 10> dec_parser;
uint_parser<uint64_t, 16> hex_parser;
uint_parser<uint64_t, 8> oct_parser;
uint_parser<uint64_t, 2> bin_parser;
start = eps(false)
| (hex_parser >> omit[ char_("hHxX")]) /* hexadecimal with suffix */
| (oct_parser >> omit[ char_("qQoO")]) /* octal with suffix */
| (bin_parser >> omit[ char_("bByY")]) /* binary with suffix */
| (dec_parser >> omit[-char_("dDtT")]) /* decimal with optional suffix */
;
}
qi::rule<Iterator, uint64_t()> start;
};
int main(int argv, const char *argc[]) {
typedef std::string::const_iterator iter;
unsigned_parser<iter> up;
for (auto const& test : std::vector<std::string>(argc+1, argc+argv)) {
iter i = test.begin(), end = test.end();
uint64_t val;
bool rv = parse(i, end, up, val);
std::cout << (rv?"Successful":"Failed") << " parse: '" << test << "' -> " << val << "\n";
if (i != end)
std::cout << " ** Remaining unparsed: '" << std::string(i,end) << "'\n";
}
}
如果您使用命令行参数调用它 123_456 123456 1_bh 0_010Q 1010_1010_0111_0111_b
它将打印:
Successful parse: '123_456' -> 123456
Successful parse: '123456' -> 123456
Successful parse: '1_bh' -> 27
Successful parse: '0_010Q' -> 8
Successful parse: '1010_1010_0111_0111_b' -> 43639
上市
用于在 SO:
上保存的完整补丁(在boost-1.57.0
标签上)
commit 24b16304f436bfd0f6e2041b2b7be0c8677c7e75
Author: Seth Heeren <sgheeren@gmail.com>
Date: Thu Mar 19 01:44:55 2015 +0100
rough patch for exposition of my answer only
diff --git a/include/boost/spirit/home/qi/numeric/detail/numeric_utils.hpp b/include/boost/spirit/home/qi/numeric/detail/numeric_utils.hpp
index 5137f87..1ced164 100644
--- a/include/boost/spirit/home/qi/numeric/detail/numeric_utils.hpp
+++ b/include/boost/spirit/home/qi/numeric/detail/numeric_utils.hpp
@@ -262,10 +262,21 @@ namespace boost { namespace spirit { namespace qi { namespace detail
///////////////////////////////////////////////////////////////////////////
// extract_int: main code for extracting integers
///////////////////////////////////////////////////////////////////////////
+#if defined(ALLOW_SO_UNDERSCORE_HACK)
+# define SPIRIT_SO_SKIP_UNDERSCORE_HACK() \
+ if ('_' == *it) { \
+ ++it; \
+ continue; \
+ }
+#else
+# define SPIRIT_SO_SKIP_UNDERSCORE_HACK()
+#endif
+
#define SPIRIT_NUMERIC_INNER_LOOP(z, x, data) \
if (!check_max_digits<MaxDigits>::call(count + leading_zeros) \
|| it == last) \
break; \
+ SPIRIT_SO_SKIP_UNDERSCORE_HACK() \
ch = *it; \
if (!radix_check::is_valid(ch) || !extractor::call(ch, count, val)) \
break; \
@@ -301,12 +312,25 @@ namespace boost { namespace spirit { namespace qi { namespace detail
std::size_t leading_zeros = 0;
if (!Accumulate)
{
+#if defined(ALLOW_SO_UNDERSCORE_HACK)
+ // skip leading zeros
+ for(;it != last;++it) {
+ if ('0' == *it && leading_zeros < MaxDigits) {
+ ++leading_zeros;
+ continue;
+ } else if ('_' == *it) {
+ continue;
+ }
+ break;
+ }
+#else
// skip leading zeros
while (it != last && *it == '0' && leading_zeros < MaxDigits)
{
++it;
++leading_zeros;
}
+#endif
}
typedef typename
@@ -366,6 +390,7 @@ namespace boost { namespace spirit { namespace qi { namespace detail
#define SPIRIT_NUMERIC_INNER_LOOP(z, x, data) \
if (it == last) \
break; \
+ SPIRIT_SO_SKIP_UNDERSCORE_HACK() \
ch = *it; \
if (!radix_check::is_valid(ch)) \
break; \
@@ -399,12 +424,25 @@ namespace boost { namespace spirit { namespace qi { namespace detail
std::size_t count = 0;
if (!Accumulate)
{
+#if defined(ALLOW_SO_UNDERSCORE_HACK)
+ // skip leading zeros
+ for(;it != last;++it) {
+ if ('0' == *it) {
+ ++count;
+ continue;
+ } else if ('_' == *it) {
+ continue;
+ }
+ break;
+ }
+#else
// skip leading zeros
while (it != last && *it == '0')
{
++it;
++count;
}
+#endif
if (it == last)
{
@@ -472,6 +510,7 @@ namespace boost { namespace spirit { namespace qi { namespace detail
};
#undef SPIRIT_NUMERIC_INNER_LOOP
+#undef SPIRIT_SO_SKIP_UNDERSCORE_HACK
///////////////////////////////////////////////////////////////////////////
// Cast an signed integer to an unsigned integer
噢。没有人应该为 Spirit 解析器上下文等实现细节操心 ,除非您正在扩展库并实现自己的解析器指令。
在那之前,phoenix::function<>
、phoenix::bind
甚至 BOOST_PHOENIX_ADAPT_FUNCTION
对任何人来说都足够了。
这里有两种方法可以解决您的问题,无需对库进行任何修补。
直接解析 Live On Coliru
这可以看作 "naive" 仅使用 Qi 和简单的语义操作来解析不同样式的整数的方法:
start = eps [_val=0] >> +(char_("0-9a-fA-F") [ _val = _val*16 + _decode(_1) ] | '_')>> char_("hHxX") /* hexadecimal with suffix */ | eps [_val=0] >> +(char_("0-7") [ _val = _val* 8 + _decode(_1) ] | '_')>> char_("qQoO") /* octal with suffix */ | eps [_val=0] >> +(char_("01") [ _val = _val* 2 + _decode(_1) ] | '_')>> char_("bByY") /* binary with suffix */ | eps [_val=0] >> +(char_("0-9") [ _val = _val*10 + _decode(_1) ] | '_')>> -char_("dDtT") /* decimal with optional suffix */ ;
当然,你会想知道
_decode
是什么样子的。那你自己定义吧:struct decode { template <typename> struct result { typedef int type; }; template <typename Ch> int operator()(Ch ch) const { if (ch>='0' && ch<='9') return ch - '0'; if (ch>='a' && ch<='z') return ch - 'a' + 10; if (ch>='A' && ch<='Z') return ch - 'A' + 10; assert(false); } }; boost::phoenix::function<decode> _decode;
使用
BOOST_PHOENIX_ADAPT_FUNCTION
宏 Live On Coliru您可以使用宏来代替定义函数对象
int decode(char ch) { if (ch>='0' && ch<='9') return ch - '0'; if (ch>='a' && ch<='z') return ch - 'a' + 10; if (ch>='A' && ch<='Z') return ch - 'A' + 10; assert(false); } BOOST_PHOENIX_ADAPT_FUNCTION(int, _decode, decode, 1)
使用
std::strtoul
Live On Coliru当然,上面的内容可能有点"complex",因为它要求您处理整数运算和数字解码的具体细节。
此外,"naive" 方法会做一些重复的工作,以防文字是像“101_101”这样的十进制值。它将计算 子结果 的十六进制、八进制 和 二进制分支,然后才意识到它是十进制。
所以我们可以改变顺序:
start = (raw[+char_("_0-9a-fA-F")] >> char_("hHxX")) [ _val = _strtoul(_1,16) ] /* hexadecimal with suffix */ | (raw[+char_("_0-7")] >> char_("qQoO")) [ _val = _strtoul(_1, 8) ] /* octal with suffix */ | (raw[+char_("_01")] >> char_("bByY")) [ _val = _strtoul(_1, 2) ] /* binary with suffix */ | (raw[+char_("_0-9")] >> -char_("dDtT")) [ _val = _strtoul(_1,10) ] /* decimal with optional suffix */ ;
你又会好奇我们是如何实现的
_evaluate
?它是一个函数,它从raw
(这是一个迭代器范围)和基础中获取合成属性,这在那时肯定是已知的:struct strtoul_f { template <typename, typename> struct result { typedef uint64_t type; }; template <typename Raw, typename Int> uint64_t operator()(Raw raw, Int base) const { std::string s(raw.begin(), raw.end()); s.erase(std::remove(s.begin(), s.end(), '_'), s.end()); char *f(&s[0]), *l(f+s.size()); return std::strtoul(f, &l, base); } }; boost::phoenix::function<strtoul_f> _strtoul;
如您所见,唯一复杂的是首先从范围中删除
_
。