Reputation: 6308
I have a class that stores a number of entities. I would like to be able to add and remove arbitrary (named) properties that are stored for every entity.
I use this map to store the properties:
std::map<std::string, boost::any> m_properties;
In order to add a property, I add an instance of MyProperty<T>
to the property map. MyProperty<T>
stores a property of type T
for each of my m_size
entities.
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 to me it seems cumbersome that I have to create an instance of MyProperty<T>
, forget about its type, and cast it back on the next line.
Is there a better, more direct way to insert the property into the map and return a reference to it?
I would like to avoid copying around the MyProperty
instance as much as possible, as it contains a significant amount of data.
EDIT: It looks like I was not clear enough on the focus of my problem. I am not worried about the performance of the cast, as in my use case, I will only add a handful of properties. However, I was looking for a "nicer" solution in terms of coding style, as I am always trying to find the simplest and most elegant solution for the task at hand.
Upvotes: 3
Views: 469
Reputation: 14603
I'd 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);
}
The "cast" is pretty cheap, you can check in the source code of any
. It can also be made cheaper, if you write your own any
. Otherwise, you could try moving, if copying is too slow, or constructing in-place.
I've had a similar problem a long time ago and I've solved it by storing ::std::unique_ptr
s (you can also use ::std::shared_ptr
s, but these 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;
}
Upvotes: 3
Reputation: 393719
This works, but to me it seems cumbersome that I have to create an instance of MyProperty, forget about its type, and cast it back on the next line.
If you don't want to forget about the type, you should not be using boost::any
, because boost::any
does precisely and only that: erase the type form the interface.
You could consider boost::variant
instead (though it comes with another set of design trade-offs to make)
Upvotes: 4