4

I'm in the process of learning boost::lambda and I've managed to create a situation that I can't resolve with what I know so far.

Apparently in the bowels of boost::lambda, the following example causes the attempted instantiation of abstract class AbstractFoo, and prevents the lambda expression from compiling. The problem is that I don't know why it is trying to instantiate it so I cant try to work around it.

Any boost::lambda experts that can:

  • give me a clue as to why this is happening?
  • suggest a work around?

Example:

#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>


struct AbstractFoo
{
    typedef boost::shared_ptr<AbstractFoo> Ptr;
    virtual int it() const = 0;
};

struct Bar : public AbstractFoo
{
    typedef boost::shared_ptr<Bar> Ptr;
    virtual int it() const { return 3; }
};

typedef AbstractFoo Foo;  // Comment this out
//typedef Bar Foo;        // and this in to make this example compilable

int main()
{
  namespace bll = boost::lambda;

  boost::function< bool (const Foo::Ptr &)> func;
  func = (bll::protect(bll::bind( &Foo::it, *bll::_1))(bll::_1) == 3);

  return 0;
}

This fails to compile (on gcc 4.4.3, boost 1_40) with a monster template error the important part of which seems to be:

error: cannot declare field 
           ‘boost::tuples::cons<AbstractFoo,boost::tuples::null_type>::head’ 
       to be of abstract type ‘AbstractFoo’
       because the following virtual functions are pure within ‘AbstractFoo’:
            virtual int AbstractFoo::it() const
Catskul
  • 17,916
  • 15
  • 84
  • 113
  • +1 for relatively new syntax `namespace bll = boost::lambda;` – iammilind Jul 27 '11 at 09:06
  • 2
    iammilind , if I am not mistaken, c++ namespace alias should be over 10 years old... – RoundPi Jul 27 '11 at 09:19
  • 1
    Catskul,which version of boost are you using? It seems to compile fine with VS2008 and boost 1_47_0 on my machine. – RoundPi Jul 27 '11 at 09:23
  • 1
    On VS2010 and boost 1.47 it also compiles fine. – mkaes Jul 27 '11 at 09:26
  • @mkaes, very interesting, I'm using GCC 4.4.3. Clang 2.8 rejects it as well. – Catskul Jul 27 '11 at 09:32
  • Boost 1_40. I wonder if it's been addressed since then. I'll have to try with a newer boost. – Catskul Jul 27 '11 at 09:45
  • Is the `protect` really necessary here? – Luc Danton Jul 27 '11 at 09:47
  • @Luc, I believe so, yes. – Catskul Jul 27 '11 at 09:49
  • @Luc, the end result is that the functor takes one argument which is a shared pointer to Foo, and calling the functor will test for equality between the result of Foo's it() and 3. – Catskul Jul 27 '11 at 10:01
  • I suggest not using `protect` then. It's not designed for what you are doing here, which is a very straight use of Boost.Lambda. Similarly for your answer. – Luc Danton Jul 27 '11 at 10:04
  • @Luc, I wasn't able to get it to work without protect. My original attempts was `((bll::_1) ->* memFun) == arg )` but I couldn't get it to work. If you have an alternate suggestion that compiles, I'm all ears. – Catskul Jul 27 '11 at 10:06

2 Answers2

1

As you discovered, you can not do that, because the object needs to be copied, but in this case it can not be instantiated because it contains a pure virtual method. The simplest solution is to pass it using a pointer :

#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>

#include <iostream>

struct AbstractFoo
{
    typedef boost::shared_ptr<AbstractFoo> Ptr;
    virtual int it() const = 0;
};

struct Bar : public AbstractFoo
{
    typedef boost::shared_ptr<Bar> Ptr;
    virtual int it() const { return 3; }
};

typedef AbstractFoo Foo;  // Comment this out
//typedef Bar Foo;        // and this in to make this example compilable

int main()
{
  namespace bll = boost::lambda;

  boost::function< bool ( const Foo * )> func;
  func = ( bll::protect( bll::bind( &Foo::it, bll::_1 ) )( bll::_1 ) == 3);
  //func = bll::bind( &Foo::it, bll::_1 );

  Foo::Ptr p( new Bar );
  std::cout << std::boolalpha << func( p.get() ) << std::endl;
}

To be more precise, this :

*bll::_1

needs to instantiate and copy object of type AbstractFoo

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • It seems very odd that it would create an instance of the object there. I would expect it could manage to use a reference if needed (which would not create the abstract instationation problem). – Catskul Jul 27 '11 at 09:37
  • See for example http://stackoverflow.com/questions/4038256/interface-like-semantics-with-boostbind . This might have been addressed in the newer boost version (at least I hope so) – BЈовић Jul 27 '11 at 09:53
  • The comments on the original question re working in VS with Boost 1_47 seem to imply that it may have. – Catskul Jul 27 '11 at 09:57
  • Current docs suggest that when using `_1`, passing is done by reference (as usual) and no copy is stored (unlike what happens when binding an argument). – Luc Danton Jul 27 '11 at 10:03
  • @Luc I do not have the latest boost version, therefore the compilation fails. Apparently the same applies to the question poster. – BЈовић Jul 27 '11 at 10:07
1

Riffing off of JVo's answer, the following works around the issue:

func3 = (bll::protect(bll::bind( &Foo::it, 
                                 bll::bind( &Foo::Ptr::get, 
                                            bll::_1         ))) (bll::_1) == 2);

where

bll::bind( &Foo::Ptr::get, bll::_1)

Pulls out the pointer so that the place holder is not dereffed in line.

From the comments suggesting compiling without error in VS with Boost 1_47 I might guess that the issue has since been fixed in boost, and that it was a sort of bug.

Catskul
  • 17,916
  • 15
  • 84
  • 113