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.
source to share
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)
source to share
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;
}
source to share