Insert an element into the boost :: any map and return a link to it

I have a class that stores multiple objects. I would like to be able to add and remove arbitrary (named) properties that are stored for each object.

I am using this map to save properties:

std::map<std::string, boost::any> m_properties;

      

To add a property, I add an instance MyProperty<T>

to the property map. MyProperty<T>

stores a type property T

for each of my objects m_size

.

template <class T>
MyProperty<T>& addProperty(const std::string& name, const T& defaultValue = T())
{
    assert(!m_properties.count(name));
    m_properties[name] = MyProperty<T>(m_size, defaultValue);
    return boost::any_cast<MyProperty<T>&>(m_properties[name]);
}

      

This works, but it seems cumbersome to me that I have to create an instance MyProperty<T>

, forget about its type and return it on the next line.

Is there a better, more direct way to insert a property into a map and return a reference to it?

I would like to avoid copying the instance MyProperty

as much as possible since it contains a significant amount of data.

EDIT: Looks like I was not clear enough at the center of my problem. I am not worried about the performance of the role as in my case I will only add a few properties. However, I was looking for a "nicer" solution in terms of coding style, as I always try to find the simplest and most elegant solution for this problem.

+3


source to share


2 answers


It works, but it seems cumbersome to me that I have to instantiate MyProperty, forget about its type and discard it on the next line.

If you don't want to forget about the type, you shouldn't use boost::any

because it boost::any

has exactly and only this: erase the type of the form with the interface.



You may want to consider instead boost::variant

(although it comes with a different set of design tradeoffs)

+4


source


I would try this:

template <class T>
MyProperty<T>& addProperty(const std::string& name, const T& defaultValue = T())
{
    assert(!m_properties.count(name));
    auto const p(m_properties.insert(name, MyProperty<T>(m_size, defaultValue)));
    assert(p.second);
    return boost::any_cast<MyProperty<T>&>(p.first->second);
}

      

"Cast" is pretty cheap, you can check the source code any

. It can also be made cheaper if you write your own any

. Otherwise, you can try navigating if copying is too slow or creating in place.



I had a similar problem a long time ago and solved it by keeping it ::std::unique_ptr

(you can also use ::std::shared_ptr

s, but they are heavier). But boost::any

so far only works with ::std::shared_ptr

s.

template <class T>
MyProperty<T>& addProperty(const std::string& name, const T& defaultValue = T())
{
    assert(!m_properties.count(name));
    auto const ptr(::std::make_shared<MyProperty<T> >(defaultValue));
    auto const p(m_properties.insert(name, ptr));
    assert(p.second);
    return *ptr;
}

      

+3


source







All Articles