boost::phoenix::bind issue in c++17



当我从 boost_1_73 和 c++14 升级到 boost_1_77 和 c++17 时,我遇到了以下问题。 会有什么问题?

**Error 1:**
include/boost/utility/result_of.hpp:218:8: error: 'GB* (boost::intrusive_ptr::*)() const noexcept' is not a class, struct, or union type
**Error 2:**
include/boost/phoenix/core/detail/function_eval.hpp:119:21: error: no type named 'type' in 'struct boost::result_of<GB* (boost::intrusive_ptr::* const(boost::intrusive_ptr&))() const noexcept>'

这是导致问题的代码片段,但抱歉无法共享更多代码。

run = qi::lit("g_d")[qi::_val = phoenix::new_<GB>()] > qi::lit("{") >
-(*iPtr)[phoenix::bind(&ECProperty::addToList,
phoenix::bind(&GBPtr::get, qi::_val), qi::_1)] >
+(&!qi::lit("}") > widthAndHeight(qi::_val)) > qi::lit("}");

就像我说的,没有足够的信息。让我只使用水晶球并假设类型:

struct ECProperty {
virtual ~ECProperty() = default;
void addToList(int i) { _list.push_back(i); }
void addWH(int width, int height) { _dimensions.emplace_back(width, height); }
std::vector<int>                   _list;
std::vector<std::tuple<int, int> > _dimensions;
};
struct GB : ECProperty {
};

现在就像我说的,GBPtr不能std::shared_ptrboost::shared_ptr甚至boost::scoped_ptr,因为它们缺乏隐式转换(有充分的理由)。因此,我们必须假设某种定制的变体,我们称之为DumbPointer

template <typename T> struct DumbPointer : public std::shared_ptr<T> {
using SP = std::shared_ptr<T>;
using SP::SP;
// A smart pointer with implicit conversion constructor...  not so smart
// Don't try this at home
/*implicit*/ DumbPointer(T* take_ownership = nullptr) : SP(take_ownership)
{ }
};
using GBPtr = DumbPointer<GB>;

好了,现在我们可以看看语法了。让我们假设iPtr真的是某种"懒惰规则",所以*iPtr不是使用 Kleene-star 的解析器表达式,而实际上只是取消了指针的围栏:

auto const* iPtr = std::addressof(qi::int_);

接下来,让我们假设withAndHeight是一个参数化规则(为什么?哦,显然有一些原因):

qi::rule<It, void(GBPtr)> widthAndHeight;

因此,我们可以使语法完整且自包含:

qi::rule<It, GBPtr(), qi::space_type> run =         //
qi::lit("g_d")[qi::_val = px::new_<GB>()] > "{" //
> -(*iPtr)[                                     //
px::bind(&ECProperty::addToList,          //
px::bind(&GBPtr::get, qi::_val),
qi::_1)]                          //
> +((&!qi::lit("}")) > widthAndHeight(qi::_val)) //
> qi::lit("}");

请注意一些非常小的简化。

旁注:&!qi::lit很有趣,也许它应该只是!qi::lit(相同的效果)。此外,假设withAndHeight无论如何都不能从}开始,那么无论如何它都是完全多余的。

同样,-(*iPtr)*iPtr相同,这就是为什么我不得不假设iPtr是指针类型。

现在,编译器错误为:


/home/sehe/custom/boost_1_77_0/boost/utility/result_of.hpp|215 col 8error: ‘GB* (std::__shared_ptr<GB, __gnu_cxx::_S_atomic>::*)() const noexcept’ is not a class, struct, or union type

很明显,Phoenix 的result_of不接受那里的原始指向成员函数。毫无疑问,noexcept抛弃了它(noexcept当时不存在)。因此,让我们使用std::mem_fn帮助编译器:

qi::rule<It, GBPtr(), qi::space_type> run =         //
qi::lit("g_d")[qi::_val = px::new_<GB>()] > "{" //
> -(*iPtr)[                                     //
px::bind(&ECProperty::addToList,
px::bind(std::mem_fn(&GBPtr::get), qi::_val), //
qi::_1)]                                      //
> +((&!qi::lit("}")) > widthAndHeight(qi::_val))             //
> qi::lit("}");

现在它编译了。很好,让我们想象一些有用的withAndHeight定义:

qi::rule<It, void(GBPtr)> widthAndHeight = //
(qi::int_ >> "x" >> qi::int_)[         //
px::bind(&ECProperty::addWH,
px::bind(std::mem_fn(&GBPtr::get), qi::_r1), qi::_1,
qi::_2)];

现在我们可以测试:

int main()
{
using It = std::string::const_iterator;
auto const* iPtr = std::addressof(qi::int_);
qi::rule<It, void(GBPtr)> widthAndHeight = //
(qi::int_ >> "x" >> qi::int_)[         //
px::bind(&ECProperty::addWH,
px::bind(std::mem_fn(&GBPtr::get), qi::_r1), qi::_1,
qi::_2)];
qi::rule<It, GBPtr(), qi::space_type> run =         //
qi::lit("g_d")[qi::_val = px::new_<GB>()] > "{" //
> -(*iPtr)[                                     //
px::bind(&ECProperty::addToList,
px::bind(std::mem_fn(&GBPtr::get), qi::_val), //
qi::_1)]                                      //
> +((&!qi::lit("}")) > widthAndHeight(qi::_val))             //
> qi::lit("}");
BOOST_SPIRIT_DEBUG_NODES((run)(widthAndHeight))
for (std::string const s :
{
"",
"g_d { 42 1200x800 400x768 }",
}) //
{
fmt::print("===== '{}' =====n", s);
It    f = begin(s), l = end(s);
GBPtr val;
try {
if (phrase_parse(f, l, run, qi::space, val)) {
fmt::print("Parsed _list: {} _dimensions: {}n", val->_list,
val->_dimensions);
} else {
fmt::print("Parse failedn");
}
} catch(qi::expectation_failure<It> const& ef) {
fmt::print(stderr, "Expected {} at '{}'n", ef.what_,
std::string(ef.first, ef.last));
}
if (f != l) {
fmt::print("Remaining input '{}'n", std::string(f, l));
}
}
}

哪个打印实时编译器资源管理器:

===== '' =====
Parse failed
===== 'g_d { 42 1200x800 400x768 }' =====
Parsed _list: {42} _dimensions: {(1200, 800), (400, 768)}

屁股...这是C++17,所以...

也许你可以简化。一个想法是提取凤凰演员:

auto const gb_ = px::bind(std::mem_fn(&GBPtr::get), _val);
qi::rule<It, GBPtr(), qi::space_type> run =               //
qi::lit("g_d")[_val = px::new_<GB>()] > "{"           //
> -(*iPtr)[px::bind(&ECProperty::addToList, gb_, _1)] //
> +widthAndHeight(_val)                               //
> "}";

仍然相同的输出:https://compiler-explorer.com/z/ec331KW4W

然而,这有点像草皮抛光剂。让我们拥抱带有多态 lambda 和 CTAD 的 c++17:

px::function addWH = [](auto& ecp, int w, int h) { ecp->addWH(w, h); };
px::function addToList = [](auto& ecp, int i) { ecp->addToList(i); };

甚至更好,没有多态 lambda:

px::function addWH     = [](GB& p, int w, int h) { p.addWH(w, h);  };
px::function addToList = [](GB& p, int i)        { p.addToList(i); };

现在我们可以简单地写:

qi::rule<It, void(GBPtr)> widthAndHeight = //
(qi::int_ >> "x" >> qi::int_)[addWH(*_r1, _1, _2)];
qi::rule<It, GBPtr(), qi::space_type> run =     //
qi::lit("g_d")[_val = px::new_<GB>()] > "{" //
> -(*iPtr)[addToList(*_val, _1)]            //
> +widthAndHeight(_val)                     //
> "}";

再次观看直播

其他注意事项

  1. 为什么有继承的属性?它使事情复杂化:

    using WandH = std::tuple<int, int>;
    px::function addWH     = [](GB& p, WandH wxh) { p.addWH(wxh); };
    px::function addToList = [](GB& p, int i) { p.addToList(i); };
    qi::rule<It, WandH()> widthAndHeight = //
    qi::int_ >> "x" >> qi::int_;
    qi::rule<It, GBPtr(), qi::space_type> run =     //
    qi::lit("g_d")[_val = px::new_<GB>()] > "{" //
    > -(*iPtr)[addToList(*_val, _1)]            //
    > +widthAndHeight[addWH(*_val, _1)]         //
    > "}";
    

    这在严格上是一致的。再次观看直播

  2. 事实上,为什么不直接在没有任何语义操作的情况下这样做:

    struct ECProperty {
    std::vector<int>   lst;
    std::vector<WandH> dims;
    };
    BOOST_FUSION_ADAPT_STRUCT(ECProperty, lst, dims)
    

    现在,您可以简单地:

    qi::rule<Iterator, WandH()> widthAndHeight = qi::int_ >> "x" >> qi::int_;
    qi::rule<Iterator, ECProperty(), qi::space_type> run = qi::eps //
    > "g_d" > '{'                                              //
    > qi::repeat(0, 1)[*iPtr]                                  //
    > +widthAndHeight                                          //
    > '}';
    

    并且仍然具有完全相同的输出:https://compiler-explorer.com/z/3PqYMEqqP。完整列表供参考:

    //#define BOOST_SPIRIT_DEBUG
    #include <boost/fusion/adapted.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <fmt/ranges.h>
    #include <fmt/ostream.h>
    namespace qi = boost::spirit::qi;
    namespace px = boost::phoenix;
    using WandH = std::tuple<int, int>;
    struct ECProperty {
    std::vector<int>   lst;
    std::vector<WandH> dims;
    };
    BOOST_FUSION_ADAPT_STRUCT(ECProperty, lst, dims)
    int main()
    {
    using Iterator = std::string::const_iterator;
    auto const* iPtr = std::addressof(qi::int_);
    qi::rule<Iterator, WandH()> widthAndHeight = qi::int_ >> "x" >> qi::int_;
    qi::rule<Iterator, ECProperty(), qi::space_type> run = qi::eps //
    > "g_d" > '{'                                              //
    > qi::repeat(0, 1)[*iPtr]                                  //
    > +widthAndHeight                                          //
    > '}';
    BOOST_SPIRIT_DEBUG_NODES((run)(widthAndHeight))
    for (std::string const s :
    {
    "",
    "g_d { 42 1200x800 400x768 }",
    }) //
    {
    fmt::print("===== '{}' =====n", s);
    Iterator   f = begin(s), l = end(s);
    ECProperty val;
    try {
    if (phrase_parse(f, l, run, qi::space, val)) {
    fmt::print("Parsed lst: {} dims: {}n", val.lst, val.dims);
    } else {
    fmt::print("Parse failedn");
    }
    } catch(qi::expectation_failure<Iterator> const& ef) {
    fmt::print(stderr, "Expected {} at '{}'n", ef.what_,
    std::string(ef.first, ef.last));
    }
    if (f != l) {
    fmt::print("Remaining input '{}'n", std::string(f, l));
    }
    }
    }
    

最新更新