簡體   English   中英

在Boost.Spirit中,為什么向量(包含在結構中)需要融合包裝器,而不是變體?

[英]In Boost.Spirit, why is a fusion wrapper required for a vector (wrapped in a struct), but not a variant?

我想了解使用Boost.Spirit封裝struct時需要BOOST_FUSION_ADAPT_STRUCT的確切場景。

以下是兩個例子。 一個示例是具有(僅) variant數據成員的單成員struct 此版本不需要將結構包裝在Fusion容器中的BOOST_FUSION_ADAPT_STRUCT宏。 構造函數足以讓Spirit根據傳入的rhs實例化/填充屬性。

(請參閱代碼中的注釋,以了解由於屬性折疊規則,我認為Boost.Spirit正在為規則定義的rhs生成的屬性類型。)

第二個示例是單個memeber struct其中(僅)具有vector數據成員。 即使定義了構造函數以允許Spirit基於rhs填充屬性,它也無法在沒有BOOST_FUSION_ADAPT_STRUCT情況下進行編譯。

為什么不同? 我想了解為什么在第一種情況下,可以使用構造函數來填充屬性( struct ),而在第二種情況下,構造函數是不夠的,必須使用BOOST_FUSION_ADAPT_STRUCT


上面舉例說明如下。

例1:變體

#include <string>
#include <vector>
#include <boost/variant.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi  = boost::spirit::qi;
typedef std::string::const_iterator It;

using intermediate = boost::variant<std::string, int>;

// Simple parser demonstrating successful build with 'works_great'
struct works_great // No need for BOOST_FUSION_ADAPT_STRUCT - whoopee!
                   // But why - even given the constructor??
{
    intermediate i;
    works_great() = default;
    works_great(intermediate i) : i{i} {}
};

// Not required for 'works_great' - constructors work just fine
//BOOST_FUSION_ADAPT_STRUCT(works_great, v)

struct parser : qi::grammar<It, works_great()>
{
    parser() : parser::base_type(works_great)
    {
        using namespace qi;
        intermediate = qi::string("test") | qi::int_;

        // rhs should have attribute of type 'variant',
        // matching the constructor
        works_great = '{' >> intermediate >> '}';
    }

  private:
    qi::rule<It, intermediate()>  intermediate;
    qi::rule<It, works_great()>   works_great;
};

int main()
{
    // The following all compiles/builds just fine
    // (I don't care about the actual runtime results).
    static const parser p;
    works_great wg;
    std::string const data {"{test}"};
    auto f(begin(data)), l(end(data));
    qi::parse(f,l,p,wg);
}

例2:矢量

#include <string>
#include <vector>
#include <boost/variant.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi  = boost::spirit::qi;
typedef std::string::const_iterator It;

// We need BOOST_FUSION_ADAPT_STRUCT for this one, but not for the above.
// Constructors don't help. Only difference seems to be
// the vector (rather than variant).
struct not_so_much // not so much - unless BOOST_FUSION_ADAPT_STRUCT is used
{
    std::vector<int> s;

    // Constructors do not help here
    //not_so_much() = default;
    //not_so_much(std::vector<int> s) : s{std::move(s)} {}
};

// Required for 'not_so_much' - constructors don't work
BOOST_FUSION_ADAPT_STRUCT(not_so_much, s)

// Simple parser demonstrating successful build with 'not_so_much' -
// but only when BOOST_FUSION_ADAPT_STRUCT is used.
struct parser : qi::grammar<It, not_so_much()>
{
    parser() : parser::base_type(not_so_much)
    {
        using namespace qi;

        // Note: I know that 'eps' is required, below, to compile the 
        // single-member struct successfully

        // rhs should have attribute of type 'vector<int>',
        // matching the constructor as well...
        // but it doesn't work.
        not_so_much = eps >> (qi::int_ % "|");
    }

  private:
    qi::rule<It, not_so_much()> not_so_much;
};

int main()
{
    // The following all compiles/builds just fine
    static const parser p;
    not_so_much nm;
    std::string const data {"5|9|16"};
    auto f(begin(data)), l(end(data));
    qi::parse(f,l,p,nm);
}

差異是雙重的:

  • 該屬性不是容器
  • 默認構造函數允許將合成屬性隱式轉換為公開屬性

后者的區別,你注意到了。 第一個:不是那么多。


真正有原則的答案是:

Qi Attribute Propagation是一種啟發式機器。

可悲的是,很少有東西可以優化性能(X3做得更好)。 其中一個例外的關鍵領域是增量解析到容器(甚至跨多個規則)¹。

這很有意義(因為即使例如逐字符構建字符串也會非常慢......)。 但它確實會帶來驚喜(例如, boost :: spirit :: qi在輸出上重復解析理解Boost.spirit的字符串解析器

¹(實際上也是非容器,但我離題了。我認為沒有語義動作就會發揮作用)

一些不必要的體操

你實際上可以改變屬性傳播的時間點,並且沒有適應性,但我建議反對它:只是適應更加一致和自我描述:

住在Coliru

#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;

namespace Ast {
    using vec = std::vector<int>;
    struct not_so_much {
        vec s;

        not_so_much() = default;
        not_so_much(vec s) : s(std::move(s)) {}
    };
}

typedef std::string::const_iterator It;
typedef qi::rule<It, Ast::not_so_much()> Parser;

template <typename Expr> void do_test(Expr const& expression) {
    Parser const p = expression;
    Ast::not_so_much nm;

    std::string const data {"5|9|16"};
    It f = begin(data), l = end(data);

    if (qi::parse(f,l,p,nm)) {
        std::cout << "Parsed " << nm.s.size() << " elements: ";
        copy(nm.s.begin(), nm.s.end(), std::ostream_iterator<int>(std::cout, " "));
        std::cout << "\n";
    } else {
        std::cout << "Parse failed\n";
    }

    if (f != l)
        std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}

int main() {
    using namespace qi;
    do_test(attr_cast<Ast::not_so_much, Ast::vec>(int_ % '|'));
    do_test(attr_cast<Ast::not_so_much>(int_ % '|'));

    do_test(as<Ast::vec>()[int_ % '|']);
}

打印

Parsed 3 elements: 5 9 16 
Parsed 3 elements: 5 9 16 
Parsed 3 elements: 5 9 16 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM