2

I've tried to reduce this problem as far as I can.

If I uncomment void initialize(), then this code compiles. If I leave it commented out, then it doesn't build.

The only way I've found to fix this problem is to build in C++03 mode with boost::shared_ptr instead of std::shared_ptr.

I've attempted with the stock clang compiler on OS X Lion (with libc++) and the following compilers on CentOS 6.4 x64:

/opt/llvm/3.2/bin/clang++ -gcc-toolchain /opt/gcc/4.7.2 -std=gnu++11 foo.cc -I/opt/boost/1.53.0/include -DBOOST_SPIRIT_USE_PHOENIX_V3 -DBOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT -DBOOST_SPIRIT_DEBUG
/opt/llvm/3.1/bin/clang++ -gcc-toolchain /opt/gcc/4.7.2 -std=gnu++11 foo.cc -I/opt/boost/1.53.0/include -DBOOST_SPIRIT_USE_PHOENIX_V3 -DBOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT -DBOOST_SPIRIT_DEBUG
/opt/llvm/3.0/bin/clang++ -gcc-toolchain /opt/gcc/4.7.2 -std=gnu++11 foo.cc -I/opt/boost/1.53.0/include -DBOOST_SPIRIT_USE_PHOENIX_V3 -DBOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT -DBOOST_SPIRIT_DEBUG
/opt/gcc/4.7.2/bin/g++ -std=gnu++11 foo.cc -I/opt/boost/1.53.0/include -DBOOST_SPIRIT_USE_PHOENIX_V3 -DBOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT -DBOOST_SPIRIT_DEBUG
/opt/gcc/4.7.1/bin/g++ -std=gnu++11 foo.cc -I/opt/boost/1.53.0/include -DBOOST_SPIRIT_USE_PHOENIX_V3 -DBOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT -DBOOST_SPIRIT_DEBUG

As usual, the compiler output from spirit is quite verbose, so I've included it as a gist:

The Code Follows...

#include <boost/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <memory>

class Object {
    public:
        void initialize(std::vector<int>) {
        }

        //void initialize() {
        //}
};

int main() {
    boost::spirit::qi::rule<std::string::iterator, int()> integer;
    boost::spirit::qi::rule<std::string::iterator, std::shared_ptr<Object>()> object;

    using boost::phoenix::bind;
    using boost::spirit::_val;
    using boost::spirit::_1;

    object  
        = (*integer) [bind(&Object::initialize, *_val, _1)];
}
ildjarn
  • 62,044
  • 9
  • 127
  • 211
Bill Lynch
  • 80,138
  • 16
  • 128
  • 173

1 Answers1

2
#define BOOST_SPIRIT_USE_PHOENIX_V3

Fixes it for me. And change *val to just val because phoenix will know how to bind the member function to it.


UPDATE As @llonesmiz hinted, this turns out to be related to ADL indeed. Though the relation is highly subtle.

  • Somewhere along the way, the presence of std::vector<> in the type of the member-function-pointer is making ADL search the std namespace and finding std::bind, instead of phoenix::bind.
  • Somehow, when you pass val, instead of *val, the compiler selects the phoenix bind as a better match.
  • You can see that when you have a member function that takes, say, an int (instead of a type from the std namespace), the problem disappears, and phoenix bind is always selected.

You can see the above observations by inspecting the output of this minimal test program that dumps the typeids of various bind expressions (and runs them through c++filt)



#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/phoenix.hpp>
#include <memory>

class Object {
    public:
        void initialize(std::vector<int>) {
        }
};

int main() {
    boost::spirit::qi::rule<std::string::iterator, int()> integer;
    boost::spirit::qi::rule<std::string::iterator, std::shared_ptr<Object>()> object;

    using boost::phoenix::bind;
    using boost::spirit::_val;
    using boost::spirit::_1;

    object  
        = (*integer) [bind(&Object::initialize, _val, _1)];
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • So. A few thoughts, I do have the Use_V3 defined. I just do it on the command line. Secondly, Clang on CentOS was using libstdc++ 4.7.2 as the C++ Library, so the implementation of std::shared_ptr should be identical between those compilers. That may not be the case on liveworkspace though, and I'll do some checking on my end. Thirdly, even if boost::phoenix::bind includes some code to automatically unpack the shared_ptr, shouldn't it work with it? _I'm also doing some more work on looking through you answer. I'll give a followup in a few more minutes_. – Bill Lynch Mar 10 '13 at 19:23
  • Yeah. So it looks like you're correct that removing the dereference operator almost fixes the issue. boost::shared_ptr works for both clang and gcc. libstdc++ 4.7.2's std::shared_ptr works only under gcc. I am still confused why it fails if I tell the code to dereference the shared_ptr. – Bill Lynch Mar 10 '13 at 19:31
  • 1
    @sharth Seems to be something related to ADL. [This](http://liveworkspace.org/code/1o2Gem$2) seems to work. –  Mar 10 '13 at 19:36
  • 1
    Okay. Here's why those overloads aren't being found. GCC declares it to be whatever compiler it actually is using the `__GNUC__` and `__GNUC_MINOR__` macros. Clang lies and says it's something like 4.2.1. Boost, then goes in and says that libstdc++ 4.2.1 didn't have std::shared_ptr, so it doesn't include the overloads. – Bill Lynch Mar 10 '13 at 19:54
  • 1
    @llonesmiz I have indeed confirmed that it is in fact the `std::vector<>` parameter that pulls `std::bind` into associated ADL namespaces. I've updated the answer with some of my Sherlock-work trying to debug ADL lookup. Does anyone know a better way to 'debug' ADL/overload resolution? – sehe Mar 10 '13 at 22:26
  • @sehe Thanks for going the extra mile. No idea about another way to debug this, I just saw that the first line in the g++ error output contained `std::Bind`. –  Mar 11 '13 at 05:41