iam M
iam M

Reputation: 21

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

I am facing these below issues when I upgrade from boost_1_73 and c++14 to boost_1_77 and c++17. What will be the problem?

**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>'

Here is a snip of code causing the issue but can't share more code sorry.

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("}");

Upvotes: 1

Views: 246

Answers (1)

sehe
sehe

Reputation: 392911

Like I said, there's not enough information. Let me just use the crystal ball and assume types:

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 {
};

Now like I said, GBPtr cannot be std::shared_ptr, boost::shared_ptr or even boost::scoped_ptr because they lack implicit conversion (for good reason). So, we have to assume some kind of bespoke variation, let's call it 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>;

Okay, now we can look at the grammar. Let's assume iPtr is really a "lazy rule" of some kind, so *iPtr is not a parser expression using Kleene-star, but really just derefences the pointer:

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

Next, let's assume withAndHeight is a parameterized rule (why? oh well apparently there was some reason):

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

So we can make the grammar complete and self-contained:

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("}");

Note a few very minor simplifications.

Sidenote: &!qi::lit is funny, perhaps it should just have been !qi::lit (same effect). Also, assuming that withAndHeight cannot start with } anyways, it's completely redundant anyways.

Similarly, -(*iPtr) is just the same as *iPtr which is why I had to assume that iPtr was of pointer type.

Now, the compiler error reads:

/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

It is clear that Phoenix's result_of doesn't accept a raw pointer-to-member-function there. No doubt, the noexcept throws it off (noexcept didn't exist back in the day). So, let's help the compiler using 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("}");

Now it compiles. Good, let's imagine some useful definition of 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)];

Now we can test:

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 failed\n");
            }
        } 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));
        }
    }
}

Which prints Live On Compiler Explorer:

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

Buttt... This Is C++17, So...

Perhaps you could simplify. An idea is to extract the phoenix actors:

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)                               //
    > "}";

Still the same output: https://compiler-explorer.com/z/ec331KW4W

However, that is a bit like turd polish. Let's embrace c++17 with polymorphic lambdas and CTAD:

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

Or even better, without polymorphic lambdas:

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

Now we can simply write:

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)                     //
    > "}";

See it Live Again

Other Notes

  1. Why have the inherited attribute? It complicates things:

    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)]         //
        > "}";
    

    That's strictly more consistent. See it Live Again

  2. In fact, why not just do it without any semantic action:

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

    Now you can simply:

    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                                          //
        > '}';
    

    And still have the exact same output: https://compiler-explorer.com/z/3PqYMEqqP. Full listing for reference:

    //#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 failed\n");
                }
            } 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));
            }
        }
    }
    

Upvotes: 0

Related Questions