将精灵注入到字符串规则中
Boost Spirit int into string rule
假设我有以下规则期望字符串值,但应该将其转换为整数:
integer %=
attr_cast<int,std::string>(
lexeme[
-(char_('+') | char_('-')) >>
char_("1-9") >> *char_("0-9")
]
)
;
所以我定义了一个转换结构:
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 &) {}
};
不幸的是,期望将左规则的int引用转换为字符串引用(我删除了引用操作符,但它不起作用)。在互联网上,我看到了用另一种方式将int转换为string的例子,其中他们不使用int的引用。我如何定义一个适当的转换?
首先,您不必这样写:
integer %= qi::int_; // Job Done
接下来,您可以通过执行as_string[ ... ]
来更轻松、更高效地编写qi::attr_cast<std::string>(...)
。
接下来,如果您确实想以费力的方式解析int,请尝试以下操作:
bool ok = parse(f, l,
(matches['-'] | -lit('+') >> attr(false)) [ phx::ref(negative) = _1 ] >> // sign
eps [ _val = 0 ] >>
+digit [ _val *= 10, _val += (_1 - '0') ],
parsed);
查看Coliru在整个范围内测试(u)int(8,16,32,64,max)_t
的现场演示:
上面使用的qi::int_parser<>
模板(通过qi::int_
)本质上是这种方法的推广,但更有效。
现在,如果你坚持的话,你当然可以使用特征技巧:
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);
}
};
} } }
这是用大炮射击苍蝇。不要介意boost::lexical_cast
不能正确地处理uint8_t
和int8_t
(将它们特别视为char
和unsigned char
?),所以我必须也硬代码异常:
// boost lexical_cast does not usefully support `char` types as 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);
}
};
现在所有的测试用例都通过了
Int parsed = 0;
bool ok = parse(f, l, as_string [ -char_("-+") >> +digit ], parsed);
参见Live On Coliru
现在让我用唯一"理智"的方法来总结:不要重新发明轮子
Int parsed = 0;
bool ok = qi::parse(f, l, qi::auto_, parsed);
完整程序Live On Coliru
#include <boost/spirit/include/qi.hpp>
template <typename Int>
void do_test() {
for (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>();
}