Reputation: 6084
In one of my projects I'm using a tree implementation, where I used the container
C=std::map<K,V>
to maintain a list of children for each tree node. Each tree node has a unique name key K
being normally a std::string
.
template<typename V, template<typename Key=std::string,typename Type=TreeNode<V>,typename ...> typename C>
class TreeNode {
typedef C<std::string, Value> cont_type;
typedef V data_type;
cont_type childs;
data_type value;
cont_type::iterator genericFind(const K& k) {
// Something generic here!!!
}
}
This implementation worked quite well for me besides the fact, that std::map doesn't respects the order of insertion in the tree. For some applications I need to keep the order of insertion, but for other applications the need for fast information retrivial is more important.
Hence C
needs to be either of type
std::vector<std::pair<K, V>> // keeping insertion order
or
std::map<K,V> // fast information retrivial
No I have a problem with the implementation of my TreeNode
class, still using explicitly the interface of std::map
. Especially, it uses the member functions find
, erase
, insert
that needed to be replaced by something generic, where the implementation for the both container types is quite concrete.
For example childs.find(key)
needs to be replaced by find(childs.begin(), childs.end(), key)
, whenever I plugin the std::vector
implementation.
Maybe there is another solution, that I'm not aware of. This might be maybe boost::multi_index
, but I'm quite unsure about this.
What might be the easiest way to solve my issue?
Upvotes: 2
Views: 2320
Reputation: 217275
You may create dedicated overload functions, and use them
template <typename Key, typename V>
auto my_find(std::map<Key, V>& m, const Key& key)
{
return m.find(key);
}
template <typename Key, typename V>
auto my_find(std::vector<std::pair<Key, V>>& v, const Key& key)
{
return std::find_if(v.begin(), v.end(), [&](const auto& p) {
return p.first == key;
});
}
Upvotes: 3
Reputation: 93274
Create generic wrappers for your desired container "choices" with the same interface:
template <typename TKey, typename TValue>
class my_map_wrapper
{
private:
std::map<TKey, TValue> _map;
public:
// Same interface as 'my_vector_wrapper'.
template <typename TEKey, typename TEValue>
void emplace(TEKey&& key, TEValue&& value)
{
_map.emplace(std::make_pair(std::forward<TEKey>(key),
std::forward<TEValue>(value)));
}
// ...
};
template <typename TKey, typename TValue>
class my_vector_wrapper
{
private:
std::vector<std::pair<TKey, TValue>> _vec;
public:
// Same interface as 'my_map_wrapper'.
template <typename TEKey, typename TEValue>
void emplace(TEKey&& key, TEValue&& value)
{
_vec.emplace_back(std::forward<TEKey>(key),
std::forward<TEValue>(value));
}
// ...
};
Templatize your tree node class on the wrapper itself:
template <typename TWrapper>
class TreeNode
{
private:
TWrapper _container;
public:
// Use "uniform" container interface...
};
You can now define convenient type aliases to choose between containers in user code:
template <typename TKey, typename TValue>
using MapTreeNode = TreeNode<my_map_wrapper<TKey, TValue>>;
template <typename TKey, typename TValue>
using VectorTreeNode = TreeNode<my_vector_wrapper<TKey, TValue>>;
Upvotes: 2