将Spirit int提升为字符串规则

时间:2014-03-24 19:22:37

标签: c++ parsing boost boost-spirit

假设我有以下规则期望字符串值,但应将其转换为整数:

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转换为字符串,它们不使用int的引用。如何定制适当的转换?

1 个答案:

答案 0 :(得分:1)

首先,您不必写这个:

integer %= qi::int_; // Job Done

接下来,您可以通过qi::attr_cast<std::string>(...)更轻松,更高效地撰写as_string[ ... ]

接下来,如果您确实想以费力的方式解析内注,请尝试以下方法:

bool ok = parse(f, l,
        (matches['-']  | -lit('+') >> attr(false)) [ phx::ref(negative) = _1 ] >> // sign
        eps    [ _val = 0 ] >>
        +digit [ _val *= 10, _val += (_1 - '0') ],
        parsed);

在整个范围内查看{strong> live demo on 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_tint8_t(专门将其视为charunsigned 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>();
}