tags:

views:

264

answers:

4

Consider the following snippet:

class Foo {
public:
    Foo( int Value );

    // other stuff
};

std::list< boost::shared_ptr< Foo > > ListOfFoo = list_of( 1 )( 2 )( 3 )( 4 )( 5 );

This does not work out of the box. What is the simplest way to make this work, or is there any method to assign values to ListOfFoo as simple as that?

+1  A: 
std::list<boost::shared_ptr<Foo> > ListOfFoo = boost::assign::list_of(boost::make_shared<Foo>(1))(boost::make_shared<Foo>(2));

There is no implicit conversion from Foo* to shared_ptr<Foo>.

shared_ptr<Foo> ptr = new Foo(1); // you can't do this
shared_ptr<Foo> ptr(new Foo(1)); // this is ok
shared_ptr<Foo> ptr = make_shared<Foo>(1); // this is also ok

What you want is impossible, you have to explicitly create shared pointers and pass them to list_of.

Nikola Smiljanić
Thanks for the suggestion, but calling make_shared each time looks too ugly. I want it to be as easily readable as the given example.
Space_C0wb0y
+1  A: 

Your boost::list_of requires objects of type boost::shared_ptr<Foo>. So you could do it as follows:

typedef boost::shared_ptr<Foo> FooPtr;

std::list<boost::shared_ptr<Foo> > fooList = list_of
    (FooPtr(new Foo(1))
    (FooPtr(new Foo(2))
    (FooPtr(new Foo(3));
Yukiko
Same as Nikola. I was hoping there was a way to tell list_of to construct shared pointers from the values it is given.
Space_C0wb0y
I don't think that is possible as Nikola mentions in his updated answer. I think his solution with boost::make_shared is quite elegant.
Yukiko
+2  A: 

Another way would be to use std::transform on an array of arguments:

const unsigned DataSize = 5;
int data[DataSize] = {1, 2, 3, 4, 5};
std::list<boost::shared_ptr<Foo> > ListOfFoo;
std::transform(data, data + DataSize, std::back_inserter(ListOfFoo), &boost::make_shared<Foo, int>);

Perhaps looks nicer if the list is to be even larger.

visitor
+2  A: 

boost::assign::ptr_list_of lets you construct a Boost pointer container with a very simple syntax. You can extend it through private inheritance so that it lets you create containers of shared_ptr:

template< class T > 
struct shared_ptr_list : boost::assign_detail::generic_ptr_list<T>
{
    typedef boost::assign_detail::generic_ptr_list<T> Base;

    template< class Seq >
    operator Seq() const 
    {
        Seq result;
        for(typename Base::impl_type::iterator it = Base::values_.begin(), e = Base::values_.end(); it != e; ++it)
            result.push_back(typename Seq::value_type(&*it));
        Base::values_.release().release();
        return result;
    }     

    template< class U >
    shared_ptr_list& operator()( const U& u )
    {
        return (shared_ptr_list&)boost::assign_detail
               ::generic_ptr_list<T>::operator()(u);
    }    
};

template< class T, class U >
shared_ptr_list<T> shared_ptr_list_of( const U& t )
{
    return shared_ptr_list<T>()(t);
}

It looks a bit ugly but then it's really convenient to use:

int main()
{
    using boost::shared_ptr;
    std::deque<shared_ptr<Foo> > deq = shared_ptr_list_of<Foo>(1)(2)(3);
}
Manuel
Excellent, just what I was looking for. I wonder though, if there is any easier way using the list_inserter class. The documentation says it is the key to extending the library, but I couldn't figure out how to use it.
Space_C0wb0y
@Space cowboy - `list_inserter` is what you would use if you are designing a new container class and you want it to interact nicely with Boost.Assign (e.g., to make `t += 2, 3` where `t` is an instance of your class). But I don't think it will be of any use here.
Manuel
Thanks for clearing that up. This is the final answer then.
Space_C0wb0y