【问题标题】:Boost spirit parser fails with incomplete type errorBoost Spirit 解析器失败,类型错误不完整
【发布时间】:2019-01-10 18:17:00
【问题描述】:

我一直在尝试各种事情,但仍然不太明白为什么以下失败并出现“不完整类型”错误

#define BOOST_PHOENIX_LIMIT 30
#define SPIRIT_ARGUMENTS_LIMIT 30

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <ctime>
#include <chrono>
#include <string>
#include <iomanip>

namespace qi = boost::spirit::qi;
namespace phi = boost::phoenix;
using namespace std::chrono_literals;
using namespace qi::labels;

using It = std::string::const_iterator;

#define PRICE_MULT 10000

class ImbalanceMsg
{

public:
    ImbalanceMsg(){}

    ImbalanceMsg(timespec ts,
                 uint8_t msgtype,
                 uint64_t seq_num,
                 std::string symbol,
                 uint64_t symbol_seqnum,
                 uint64_t ref_price,
                 uint32_t paired_qty,
                 uint32_t total_imb_qty,
                 uint32_t mkt_imb_qty,
                 uint32_t auction_time,
                 char     auction_type,
                 char     imb_side,
                 uint64_t cont_clear_price,
                 uint64_t auction_int_clear_price,
                 uint64_t ssr_filling_price,
                 uint64_t ind_match_price,
                 uint64_t upper_collar,
                 uint64_t lower_collar,
                 uint32_t auction_status,
                 uint32_t freeze_status,
                 uint32_t num_ext
    ) :

            m_ref_price{ref_price},
            m_paired_qty{paired_qty},
            m_total_imb_qty{total_imb_qty},
            m_mkt_imb_qty{mkt_imb_qty},
            m_auction_time{auction_time},
            m_auction_type{auction_type},
            m_imb_side{imb_side},
            m_cont_clear_price{cont_clear_price},
            m_auction_int_clear_price{auction_int_clear_price},
            m_ssr_filling_price{ssr_filling_price},
            m_ind_match_price{ind_match_price},
            m_upper_collar{upper_collar},
            m_lower_collar{lower_collar},
            m_auction_status{auction_status},
            m_freeze_status{freeze_status},
            m_num_ext{num_ext}
    {}

    // auto msg = parse( "105,42982201,15:00:05.553620224,AAPL,1192101,157.56,0,200,0,1600,C,S,0,0,0,157.57,159140000,155980000,0,0,0" );


    std::string m_symbol;
    uint64_t m_symbol_seqnum;
    uint64_t m_ref_price;
    uint32_t m_paired_qty;
    uint32_t m_total_imb_qty;
    uint32_t m_mkt_imb_qty;
    uint32_t m_auction_time;
    char     m_auction_type;
    char     m_imb_side;
    uint64_t m_cont_clear_price;
    uint64_t m_auction_int_clear_price;
    uint64_t m_ssr_filling_price;
    uint64_t m_ind_match_price;
    uint64_t m_upper_collar;
    uint64_t m_lower_collar;
    uint32_t  m_auction_status;
    uint32_t  m_freeze_status;
    uint32_t m_num_ext;

};




int main() {

    std::string s = "AAPL,1192101,157.56,0,200,0,1600,C,S,0,0,0,157.57,159140000,155980000,0,0,0";

    timespec ts;
    uint8_t msgtype = 105;
    uint64_t seq_num = 42982201;

    qi::uint_parser<uint32_t, 10, 1, 6> int_part;
    qi::uint_parser<uint8_t , 10, 1, 1> m_digit;

    qi::rule<std::string::iterator, uint64_t()>
        m_fixed_point = int_part[qi::_val =  qi::_1 * PRICE_MULT] >>
              -("." >> -(m_digit[qi::_val += qi::_1 * 1000])
                    >> -(m_digit[qi::_val += qi::_1 * 100])
                    >> -(m_digit[qi::_val += qi::_1 * 10])
                    >> -(m_digit[qi::_val += qi::_1 ])
              );

    qi::rule<std::string::iterator, ImbalanceMsg()>
        m_wire_msg = ( qi::as_string[*qi::alpha]   >> "," // symbol
                                                   >> qi::ulong_    >> "," // symbol seq num
                                                   >> m_fixed_point >> "," // ref price
                                                   >> qi::uint_     >> "," // paired_qty
                                                   >> qi::uint_     >> "," // total_imb_qty
                                                   >> qi::uint_     >> "," // mkt_imb_qty
                                                   >> qi::uint_     >> "," // auction_time
                                                   >> qi::char_     >> "," // auction type
                                                   >> qi::char_     >> "," // imb side
                                                   >> m_fixed_point >> "," // cont_clear_price
                                                   >> m_fixed_point >> "," // auction_int_clear_price
                                                   >> m_fixed_point >> "," // ssr_filling_price
                                                   >> m_fixed_point >> "," // ind_match_price
                                                   >> m_fixed_point >> "," // upper_collar
                                                   >> m_fixed_point >> "," // lower_collar
                                                   >> qi::ushort_   >> "," // auction status
                                                   >> qi::ushort_   >> "," // freeze status
                                                   >> qi::ushort_
         )[qi::_val = phi::construct<ImbalanceMsg>(ts, msgtype, seq_num,
                                                      qi::_1, //symbol
                                                      qi::_2, //market_id
                                                      qi::_3, //system_id
                                                      qi::_4, //exchange_code
                                                      qi::_5, //security_type
                                                      qi::_6, //lot_size
                                                      qi::_7, // prev_close_price
                                                      qi::_8, // prev_close_volume
                                                      qi::_9, // price_resolution
                                                      qi::_10, // round_lot
                                                      qi::_11, // mpv
                                                      qi::_12,
                                                      qi::_13,
                                                      qi::_14,
                                                      qi::_15,
                                                      qi::_16,
                                                      qi::_17,
                                                      qi::_18
    )];

    ImbalanceMsg m;
    bool ok = parse( s.begin(), s.end(), m_wire_msg, m );
    std::cout << "ok=" << ok << std::endl;


}

与 ImbalanceMsg 相比,属性较少的较小类也不会出现同样的问题。

我有几种其他类型的消息类,它们的代码相似,但都编译得很好。

有人可以指点一下吗?

【问题讨论】:

  • 我意识到如果我注释掉最后 9 个字段。它会正确编译....也许某个地方有错误?
  • 特别是当我们开始引入 qi::_12 时它失败了。在此之前它编译得很好。

标签: c++ boost-spirit semantic-actions


【解决方案1】:

只要有一个占位符,就可以编译语义动作表达式(在[] 内)。

到目前为止一切顺利。

但要真正编译规则(从包含您的语义操作的模板表达式),您实际上还需要 Fusion 支持大序列:

#define BOOST_MAX_FUSION_VECTOR_SIZE 30

请注意,除了您传递的 3 个参数太多(_1 到 _18 为 18,但您已经传递了硬编码的 (ts, msgtype, seq_num),因此 15 个就足够了)。

但就像我之前的回答 (Compile error due boost spirit placeholder limit not more than 10) 一样,我建议不要这样做。这只会使您的代码不可读,编译成本极高,并且通常不会带来有用的好处。

相反,请考虑使用qi::_0,它一次只传递整个合成属性序列。

或者根本不使用语义动作;

看妈妈,没有手!

我经常说我更喜欢避免语义动作。原因:Boost Spirit: "Semantic actions are evil"?

在这种情况下,我建议进行简单的融合序列调整:

struct ImbalanceMsg {
    timespec    ts;
    uint8_t     msgtype;
    uint64_t    seq_num;

    std::string symbol;
    uint64_t    symbol_seqnum;
    uint64_t    ref_price;
    uint32_t    paired_qty;
    uint32_t    total_imb_qty;
    uint32_t    mkt_imb_qty;
    uint32_t    auction_time;
    char        auction_type;
    char        imb_side;
    uint64_t    cont_clear_price;
    uint64_t    auction_int_clear_price;
    uint64_t    ssr_filling_price;
    uint64_t    ind_match_price;
    uint64_t    upper_collar;
    uint64_t    lower_collar;
    uint32_t    auction_status;
    uint32_t    freeze_status;
    uint32_t    num_ext;
};

BOOST_FUSION_ADAPT_STRUCT(ImbalanceMsg, 
    ts, msgtype, seq_num, symbol, symbol_seqnum, ref_price,
    paired_qty, total_imb_qty, mkt_imb_qty, auction_time, auction_type, imb_side,
    cont_clear_price, auction_int_clear_price, ssr_filling_price, ind_match_price, upper_collar,
    lower_collar, auction_status, freeze_status, num_ext)

现在您可以简单地解析所有成员:

qi::rule<std::string::const_iterator, ImbalanceMsg()> m_wire_msg 
    = qi::attr(ts) >> qi::attr(msgtype) >> qi::attr(seq_num)
    >> qi::as_string[*qi::alpha] >> "," // symbol
    >> qi::ulong_                >> "," // symbol seq num
    >> m_fixed_point             >> "," // ref price
    >> qi::uint_                 >> "," // paired_qty
    >> qi::uint_                 >> "," // total_imb_qty
    >> qi::uint_                 >> "," // mkt_imb_qty
    >> qi::uint_                 >> "," // auction_time
    >> qi::char_                 >> "," // auction type
    >> qi::char_                 >> "," // imb side
    >> m_fixed_point             >> "," // cont_clear_price
    >> m_fixed_point             >> "," // auction_int_clear_price
    >> m_fixed_point             >> "," // ssr_filling_price
    >> m_fixed_point             >> "," // ind_match_price
    >> m_fixed_point             >> "," // upper_collar
    >> m_fixed_point             >> "," // lower_collar
    >> qi::ushort_               >> "," // auction status
    >> qi::ushort_               >> "," // freeze status
    >> qi::ushort_;

而且属性传播是完全自动化的:

Live On Coliru

#define BOOST_MAX_FUSION_VECTOR_SIZE 30
#define BOOST_PHOENIX_LIMIT 30
#define SPIRIT_ARGUMENTS_LIMIT 30
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/io.hpp>
#include <ctime>

namespace qi = boost::spirit::qi;

#define PRICE_MULT 10000

struct ImbalanceMsg {
    timespec    ts;
    uint8_t     msgtype;
    uint64_t    seq_num;

    std::string symbol;
    uint64_t    symbol_seqnum;
    uint64_t    ref_price;
    uint32_t    paired_qty;
    uint32_t    total_imb_qty;
    uint32_t    mkt_imb_qty;
    uint32_t    auction_time;
    char        auction_type;
    char        imb_side;
    uint64_t    cont_clear_price;
    uint64_t    auction_int_clear_price;
    uint64_t    ssr_filling_price;
    uint64_t    ind_match_price;
    uint64_t    upper_collar;
    uint64_t    lower_collar;
    uint32_t    auction_status;
    uint32_t    freeze_status;
    uint32_t    num_ext;
};

BOOST_FUSION_ADAPT_STRUCT(ImbalanceMsg, 
    ts, msgtype, seq_num, symbol, symbol_seqnum, ref_price,
    paired_qty, total_imb_qty, mkt_imb_qty, auction_time, auction_type, imb_side,
    cont_clear_price, auction_int_clear_price, ssr_filling_price, ind_match_price, upper_collar,
    lower_collar, auction_status, freeze_status, num_ext)

static inline std::ostream& operator<<(std::ostream& os, timespec) { return os << "(timespec:TODO)"; }

struct SimpleReal : qi::real_policies<double> {
    template <typename It> static bool parse_exp(It, It)         { return false; }
    template <typename It> static bool parse_exp_n(It, It, int)  { return false; }
    template <typename It> static bool parse_nan(It, It, double) { return false; }
    template <typename It> static bool parse_inf(It, It, double) { return false; }
};

int main() {

    qi::rule<std::string::const_iterator, uint64_t()> m_fixed_point 
        = qi::real_parser<double, SimpleReal>{} [ qi::_val = PRICE_MULT*qi::_1 ];

    timespec ts;
    uint8_t msgtype = 105;
    uint64_t seq_num = 42982201;

    qi::rule<std::string::const_iterator, ImbalanceMsg()> m_wire_msg 
        = qi::attr(ts) >> qi::attr(msgtype) >> qi::attr(seq_num)
        >> qi::as_string[*qi::alpha] >> "," // symbol
        >> qi::ulong_                >> "," // symbol seq num
        >> m_fixed_point             >> "," // ref price
        >> qi::uint_                 >> "," // paired_qty
        >> qi::uint_                 >> "," // total_imb_qty
        >> qi::uint_                 >> "," // mkt_imb_qty
        >> qi::uint_                 >> "," // auction_time
        >> qi::char_                 >> "," // auction type
        >> qi::char_                 >> "," // imb side
        >> m_fixed_point             >> "," // cont_clear_price
        >> m_fixed_point             >> "," // auction_int_clear_price
        >> m_fixed_point             >> "," // ssr_filling_price
        >> m_fixed_point             >> "," // ind_match_price
        >> m_fixed_point             >> "," // upper_collar
        >> m_fixed_point             >> "," // lower_collar
        >> qi::ushort_               >> "," // auction status
        >> qi::ushort_               >> "," // freeze status
        >> qi::ushort_;

    ImbalanceMsg m;
    std::string const s = "AAPL,1192101,157.56,0,200,0,1600,C,S,0,0,0,157.57,159140000,155980000,0,0,0";

    bool ok = parse( s.begin(), s.end(), m_wire_msg, m );

    std::cout << "ok=" << ok << "\n";

    using boost::fusion::operator<<;
    std::cout << m << "\n";
}

打印

ok=1
((timespec:TODO) i 42982201 AAPL 1192101 1575600 0 200 0 1600 C S 0 0 0 1575700 1591400000000 1559800000000 0 0 0)

使用qi::_0

您可以在此处阅读有关此方法的更多信息:Passing each element of a parsed sequence to a function that returns a rule's attribute type

template <typename T> struct Factory {
    template <typename Seq>
    T operator()(Seq const& seq) const { return my_apply(Construct{}, seq); }
  private:
    struct Construct {
        template <typename... I> T operator()(I... initializers) const 
            { return T { initializers... }; }
    };
};

令人惊讶的是,boost::fusion::apply 仍然不存在,所以我将从该答案 (c++14) 中添加 my_apply 实现。

现在您可以在语义操作中使用它:

...
>> qi::ushort_) [ qi::_val = phi::bind(Factory<ImbalanceMsg>{}, qi::_0) ];

这样做的好处是不需要 Boost Fusion Adaptation,也不需要大量的占位符。

奖金

我还会更简单地进行定点实数解析:

qi::rule<std::string::const_iterator, uint64_t()> m_fixed_point 
    = qi::real_parser<double, SimpleReal>{} [ qi::_val = PRICE_MULT*qi::_1 ];

SimpleReal 是不允许特殊表示(如科学记数法)的真正政策。

【讨论】:

  • 非常感谢您的详细和启发性的帖子!顺便说一句,对于真正的解析器,是否可以将整数和小数部分单独作为整数,然后从例如 123.45 -> 1234500 执行转换,假设我总是想要小数点后的 4 位数字。
  • 原因是我将这些数字粘贴到查找图结构中,并且我不想通过将任何内容转换为浮点数或双精度数来引入浮点精度相关问题
  • 您能否评论一下使用 BOOST_FUSION_ADAPT_STRUCT 在空间/时间/效率上的权衡?它实际上将什么引入正在调整的结构中?谢了!
  • 它不会在结构中引入任何东西。就像文档说的那样,它确实 adapt 将结构用作融合序列(通过专门化一组特征)。它有编译时间溢价,但没有运行时间。你可以例如Fusion Adapt timespec 不侵入第三方库
  • 回复:数字:我认为 double 的精度应该使得任何二进制浮点表示的不准确性总是远低于三个舍入阈值。这很容易检查你想要的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多