当我从 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_ptr
,boost::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 8
error: ‘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) //
> "}";
再次观看直播
其他注意事项
为什么有继承的属性?它使事情复杂化:
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)] // > "}";
这在严格上是一致的。再次观看直播
事实上,为什么不直接在没有任何语义操作的情况下这样做:
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)); } } }