开发者

Boost lambda with shared_ptr

If I have a polymorphic base class called Base as well as classes Derived1 and Derived2 which inherit from Base. I can then use boost::lambda to create a factory of sorts. Something like:

typedef boost::function<Base *()> Creator;
std::map<std::string,Creator> map1;
map1["Derived1"] = boost::lambda::new_ptr<Derived1>();
map1["Derived2"] = boost::lambda::new_ptr<Derived2>();

(This isn't real code, I'm just trying to illustrate the problem.)

This works, so I can then do a lookup in the map using a string and then invoke the lambda function to instantiate that class. All good.

The problem with this is that it's dealing in raw pointers, I'd prefer to be using smart pointers (std::shared_ptr).

So if I change from:

typedef boost::function<Base *>() Creator;

to:

typedef boost::function<std::shared_ptr<Base> >() Creator;

Then I'm getting stuck from here. I've tried using boost::lambda::bind in conjunction with boost::lambda::new_ptr but I'm not having much luck, can't get past compilation errors. (Huge reams of template-related error output.)

I've checked other similar messages within StackOverflow, Using boost::bind and boost::lambda::new_ptr to return a shared_ptr constructor is close but if I try to apply its solution I get the template errors mentioned above.

I'm happy to provide sample code and the actual errors if it helps, but hopefully the above info is sufficient. I'm using boost 1.47开发者_开发知识库.0 on GCC 4.6 as well as 4.7 snapshot on Fedora 15.


class Base { 
public:
    virtual ~Base() = 0;
};
Base::~Base() {}

class Derived1 : public Base {};
class Derived2 : public Base {};

typedef boost::shared_ptr<Base> BasePtr;

typedef boost::function<BasePtr()> Creator;
template <typename T>
Creator MakeFactory()
{
    namespace la = boost::lambda;
    return la::bind( 
        la::constructor<BasePtr>(), 
        la::bind(la::new_ptr<T>()));
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::map<std::string,Creator> map1;    
    map1["Derived1"] = MakeFactory<Derived1>();
    map1["Derived2"] = MakeFactory<Derived2>();
    BasePtr p1 = map1["Derived1"]();
    BasePtr p2 = map1["Derived2"]();

    return 0;
}

however, why go to the trouble when you could write:

template <typename T>
BasePtr MakeFactoryImpl()
{
    return BasePtr(new T());
}
template <typename T>
Creator MakeFactory()
{
    return Creator(&MakeFactoryImpl<T>);
}


This is a common problem. The fact that two types are related (in your case by inheritance) does not imply that the instantiations of a template with those two types maintains the same relationship.

The solution is to return always shared_ptr<Base>, since it can hold both pointers to Base or any derived type, which will be semantically compatible with your current version (i.e. in both versions the caller gets a (smart)-pointer-to Base.

As an aside, I would avoid returning shared_ptr from a factory, as you are forcing your choice of smart pointer into all of your users. I would prefer to either return a raw pointer (the user can choose, but it is dangerous in some situations) or a unique_ptr or even auto_ptr, which are safe and still allow the user to choose a different mechanism (i.e. if your function returns an auto_ptr, the user can still use a shared_ptr by doing shared_ptr<Base> p( f().release() );, while the opposite is not possible (memory managed by a shared_ptr cannot be released to use in a different smart pointer.


This quick-and-dirty return type adapter is good not only for converting return types from Derived* to Base*, but between any convertible types. For simplicity, the function-object takes no arguments. With C++11 variadic templates it should be easy to add arbitrary argument handling. Feel free to improve on this in any way you wish.

template <typename ToType>
class return_type_adapter
{    
    template <typename toType>
    class return_type_adapter_impl_base
    {
      public:
        virtual toType call() = 0;
    };

    template <typename toType, typename Func>
    class return_type_adapter_impl : public return_type_adapter_impl_base<toType>
    {
      public:
        return_type_adapter_impl (Func func) : func(func) {}
        toType call() { return toType(func()); }
      private:
        Func func;
    };

    boost::shared_ptr<return_type_adapter_impl_base<ToType> > impl_base;

  public:
    ToType operator() () { return impl_base->call(); }

    template <typename Func>
    return_type_adapter (Func func) :
        impl_base(new return_type_adapter_impl<ToType, Func>(func)) {}
};


map1["Derived1"] = boost::lambda::bind(
    boost::lambda::constructor<boost::shared_ptr<Base>>(),
    boost::lambda::bind(
        boost::lambda::new_ptr<Derived1>()));
map1["Derived2"] = boost::lambda::bind(
    boost::lambda::constructor<boost::shared_ptr<Base>>(),
    boost::lambda::bind(
        boost::lambda::new_ptr<Derived2>()));

But honestly, this is the level of complexity where it doesn't really make sense to use boost lambda any more. A simpler solution:

template<typename DerivedType>
boost::shared_ptr<Base> makeDerived() {
    return boost::shared_ptr<Base>(new DerivedType);
}
[...]

    map1["Derived1"] = makeDerived<Derived1>;
    map1["Derived2"] = makeDerived<Derived2>;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜