I want to implement a map, which maps a string to a generic vector.
I want to do this:
std::map<std::string, std::vector<class T> > myMap;
Assuming the proposed myMap had the following inserted into it, it could be used as such:
vector<int> intVec = myMap["ListOfInts"]; // Works because "ListOfInts" maps to a vector<int>
vector<string> stringVec = myMap["ListOfStrings"]; // Works because "ListOfInts" maps to a vector<string>
When I declare the map with the above syntax the compiler has a heart attack.
Can anybody make any suggestions? Or a better associate array option in C++ (suggest non-boost before boost).
Since you know the type you want when you are writing your code, I propose this approach (untested):
// base class for any kind of map
class BaseMap {
public:
virtual ~BaseMap() {}
};
// actual map of vector<T>
template<typename T>
class MapT : public BaseMap, public std::map<std::string, std::vector<T>> {};
class MultiMap {
public:
template<typename T>
std::vector<T>& get(const std::string& key) {
std::unique_ptr<BaseMap>& ptr = maps_[std::type_index(typeid(T))];
if (!ptr) ptr.reset(new MapT<T>());
return ptr->second[key];
}
private:
std::map<std::type_index, std::unique_ptr<BaseMap>> maps_;
}
int main() {
MultiMap map;
std::vector<int>& intVec = map.get<int>("ListOfInts");
std::vector<std::string>& stringVec = map.get<std::string>("ListOfStrings");
}
Maybe this could work for you:
template <typename T>
class MyMap {
std::map<std::string, std::vector<typename T> > map;
public:
/*...*/
};
As mattnewport said boost::variant is one option.
Or to support any types, use boost::any by explicit using any_cast.
Considering boost might be heavy weight, maybe you can reinvent the wheel and simplify it, so that is non boost any more? lol
With C++11, you can use using, it does exactly what you want:
#include <vector>
#include <map>
#include <string>
template<typename T> using mymap = std::map<std::string, std::vector<T>>;
int main()
{
mymap<int> intmap;
mymap<std::string> stringmap;
std::vector<int> intvec = intmap["test"];
std::vector<std::string> stringvec = stringmap["test"];
return 0;
}
Live Demo
Template arguments must be decided compile time. If you want each key of your map to correspond to a vector of a different type, it won't really work. You can get around it with a polymorphic container that returns a void* that you cast to the correct type, but I would suggest trying to find another way to do whatever you want to do with your map first.
Related
Lets say we have some pair like class:
class PairLike{
public:
string key;
int val;
PairLike(string Key, int Val) : key(Key), val(Val){};
//...other members
};
A few objects to go along with it:
PairLike p1("a",1);
PairLike p2("b",2);
PairLike p3("c",3);
PairLike p4("d",4);
Is there a way of automatically working with this object?
For example, something similar to:
std::map<PairLike> container = {p1,p2,p3};
container.insert(p4);
Instead of writing something like:
std::map<string, int> container = {{p1.key, p1.val}, {p2.key, p2.val}, ... }
container.insert({p4.key, p4.val})
I'm aware that using an std::set<PairLike> with a comparator using is_transparentcan achieve the result I'm looking for. However, I am curious if there is any way to approach this problem with a map.
You could provide a conversion operator for converting to std::pair:
class PairLike{
public:
// ...
operator std::pair<const std::string, int>() {
return {key, val};
}
};
And use it like:
std::map<string, int> container{p1,p2,p3};
container.insert(p4);
Edit: please see the #krisz's answer for viable alternative that I overlooked here.
That is not possible. std::pair is defined as part of the class definition, and cannot be altered to a custom class.
P.S. As noted by #UnholySheep, it's defined in the CPP standard:
namespace std {
template<class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T>>>
class map {
public:
// types
using key_type = Key;
using mapped_type = T;
using value_type = pair<const Key, T>;
...
I was wondering if this was a valid way to assign a multidimensional map inside of a class method and if not how would I go about doing this.
template<typename T>
std::map<std::string, std::map<std::string, T>> MT;
template<typename T>
void MonsterTemplate(std::string name, std::string node, template T v) {
MT[name][node] = v;
}
Edit1: I compiled and it gave me many errors but I will just give a portion of the 1st.
error C3376: 'MonsterType::MT': only static data member templates are allowed
Edit2:
I tried creating a struct
template<typename T>
struct Wrapper
{
typedef std::map<std::string, std::map<std::string, T>> MT;
};
I then added this inside the class
template<typename T>
Wrapper<T>::MT mt;
template<typename T>
void MonsterTemplate(std::string name, std::string node, template T v) {
mt[name][node] = v;
}
Then got this error amongst many others.
warning C4346: 'MT': dependent name is not a type
The struct works outside the class without being multidimensional map, but I am unsure how to access it as multidimensional map.. just trying different things.
The idea is I want to store data of several objects data and index them by name, node and value.
Edit3:
So this is what I went with, and haven't gotten an error (just yet :p)
std::map<std::string, std::map<std::string, int>> MT;
void MonsterTemplate(std::string name, std::string node, int v) {
MT[name][node] = v;
}
Just updating this for anyone looking for something similar
So I figured out how to construct this.
template<typename T>
struct Test
{
std::map<std::string, std::map<std::string, std::map<size_t, std::map<std::string, T>>>> testmap;
void MonsterTemplate(std::string creatureName, std::string name, std::string node, T v) {
size_t i = testmap[creatureName][name].size();
testmap[creatureName][name][i][node] = v;
}
};
Test<std::string> str;
Since I needed various data types for T the structure worked much better than trying to find a way to assign a template to a static class, I am only using std::string as an example.
This can then be used inside of the class's method since str has a global scope.
str.MonsterTemplate(creatureName, name, node, value);
I am interested in building an std::unordered_map< K, V> Map; where V is something like Map::iterator. Specifically I want to make V this vector< Pair< double, Map::iterator> >;
. As just stated this is a recursive type in C++ which is not allowed by the standard.
However, the operations of begin(), end() and operator++() on the unordered_map do not need any knowledge of Map::mapped_type to be well defined, so, in principle it seems that Map::iterator should be able to be defined independently of unordered_map.
So I wonder is there a way to define:
typedef std::unordered_map_iterator< K> iterator;
typedef std::unordered_map_const_iterator< K> const_iterator;
then create:
typedef std::unordored_map< K, iterator> Map;
such that, map.insert( std::make_pair( K(), map.begin()); makes sense?
For example what about something like the below?
typedef std::unordered_map< K, V> Premap;
typedef std::unordered_map< K, Premap::iterator> Map;
In partial answer, the above suggestion produces a template deduction error, as expected. But nevertheless something like this should be do-able.
#include <unordered_map>
#include <iostream>
#include <string>
#include <utility>
typedef std::string K; //these two types are arbitrary
typedef double V; //chosen just for completeness sake.
typedef std::unordered_map< K, V> Premap;
typedef std::unordered_map< K, Premap::iterator> Map;
int main( int argc, char ** argv){
Map test;
test.insert( std::make_pair( std::string( "foo"), test.end()));
return 0;
}
Creating a standard container which contains iterators to itself is impossible. However, there are alternative approaches which behave the same.
Solution 1
unordered_map requires unique keys. Therefore, simply use std::string as the value type, and look it up in the map when necessary. The average algorithmic complexity of the lookup is the same in both cases - O(1).
Example: let's say you want to represent this data:
foo -> bar
bar -> baz
baz -> barney
fred -> (nothing)
barney -> (nothing)
Then you would write this to construct the data structure:
std::unordered_map<std::string, std::string> Map;
Map m;
m.insert(std::make_pair(std::string("foo"), std::string("bar")));
m.insert(std::make_pair(std::string("bar"), std::string("baz")));
m.insert(std::make_pair(std::string("baz"), std::string("barney")));
m.insert(std::make_pair(std::string("fred"), std::string("")));
m.insert(std::make_pair(std::string("barney"), std::string("")));
To walk along the chains in this structure, you could use code like this:
Map::iterator i = m.find("foo");
while (i != m.end() && !i->second.empty()) {
i = m.find(i->second);
}
Note that any string which does not appear as a key could be used to mark the end of the chain.
Solution 2
Use boost::ptr_unordered_set and store the pointer to the next value in the value itself. Add appropriate equality and comparison operators, so that the pointer is ignored in the set. Note that this adds an extra level of indirection, but this is unavoidable.
struct ChainElement
: boost::equality_comparable<ChainElement>
{
std::string key;
ChainElement *next;
ChainElement(std::string const &k, ChainElement *n = NULL)
: key(k), next(n)
{}
bool operator==(ChainElement const &other) const {
return key == other.key;
}
};
namespace std {
template <>
hash<ChainElement>::operator()(ChainElement const &ce) const {
return std::hash(ce.key);
};
}
Set up the structure e.g. like this:
#include <boost/ptr_container/ptr_unordered_set.hpp>
// ...
boost::ptr_unordered_set<ChainElement> m;
m.insert(new ChainElement("foo"));
m.insert(new ChainElement("bar"));
m.insert(new ChainElement("baz"));
m.insert(new ChainElement("fred"));
m.insert(new ChainElement("barney"));
m.find("foo")->next = &*m.find("bar");
m.find("bar")->next = &*m.find("baz");
m.find("baz")->next = &*m.find("barney");
Then to walk the structure:
ChainElement *elem = &*m.find("foo");
while (elem != NULL) {
// do something
elem = elem->next;
}
Using boost::ptr_unordered_set rather than std::unordered_set guarantees that the objects will be deleted when the container holding them is deleted.
I want to make some storage for my game. Now the code looks like:
class WorldSettings
{
private:
std::map<std::string, int> mIntegerStorage;
std::map<std::string, float> mFloatStorage;
std::map<std::string, std::string> mStringStorage;
public:
template <typename T>
T Get(const std::string &key) const
{
// [?]
}
};
So, I have a few associative containers which stores the exact type of data. Now I want to add into settings some value: settings.Push<int>("WorldSize", 1000); and get it: settings.Get<int>("WorldSize");. But how to switch need map due to passed type into template?
Or, maybe, you know a better way, thanks.
If your compiler supports this1, you can use template function specialisations:
class WorldSettings
{
private:
std::map<std::string, int> mIntegerStorage;
std::map<std::string, float> mFloatStorage;
std::map<std::string, std::string> mStringStorage;
public:
template <typename T>
T Get(const std::string &key); // purposely left undefined
};
...
template<>
int WorldSettings::Get<int>(const std::string& key) {
return mIntegerStorage[key];
}
template<>
float WorldSettings::Get<float>(const std::string& key) {
return mFloatStorage[key];
}
// etc
Notice that the methods are not const because map<>::operator[] is not const.
Also, if someone tries to use the template with a type other than one you have provided a specialisation for, they will get linker errors, so your code won't misbehave or anything. Which is optimal.
1 If not, see #gwiazdorrr's answer
First of all, since prior to C++11 you can't specialise functions, your member functions must differ in signature - return type does not count. From my experience on some compilers you can do without it, but as usual - you should keep your code as close to standard as possible.
That said you can add a dummy paramater that won't affect performance and the way you call function:
public:
template <typename T>
T Get(const std::string &key) const
{
return GetInner(key, (T*)0);
}
private:
int GetInner(const std::string& key, int*) const
{
// return something from mIntegerStorage
}
float GetInner(const std::string& key, float*) const
{
// return something from mFloatStorage
}
And so on. You get the idea.
Seth's answer is ideal, but if you don't have access to a C++11 compiler, then you can use template class specialization and do this instead. It's much more verbose, but keeps the same functionality.
class WorldSettings
{
template<class T>
struct Selector;
template<class T>
friend struct Selector;
private:
std::map<std::string, int> mIntegerStorage;
std::map<std::string, float> mFloatStorage;
std::map<std::string, std::string> mStringStorage;
public:
template <typename T>
T Get(const std::string &key)
{
return Selector<T>::Get(*this)[key];
}
};
template<>
struct WorldSettings::Selector<int>
{
static std::map<std::string, int> & Get(WorldSettings &settings)
{
return settings.mIntegerStorage;
}
};
template<>
struct WorldSettings::Selector<float>
{
static std::map<std::string, float> & Get(WorldSettings &settings)
{
return settings.mFloatStorage;
}
};
// etc.
In C++03 I would recommend the use of ‘boost::any‘ in the type of the container, and the. You need a single accessor:
std::map<std::string,boost::any> storage;
template <typename T> getValue( std::string const & key ) {
return boost::any_cast<T>( storage[key] );
}
This is a rough sketch, as a member function It would be const, and it should use ‘map::find‘ not to modify the container when searching, it should deal with invalid joeys, and probably remap the boost exceptions into your own application exceptions.
How can I use BOOST_FOREACH efficiently (number-of-character/readability-wise) with a boost::ptr_map?
Kristo demonstrated in his answer that it is possible to use BOOST_FOREACH with a ptr_map, but it does not really save me any typing (or makes my code really more readable) than iterating over the ptr_map with an iterator:
typedef boost::ptr_container_detail::ref_pair<int, int* const> IntPair;
BOOST_FOREACH(IntPair p, mymap) {
int i = p.first;
}
// vs.
boost::ptr_map<int, T>::iterator it;
for (it = mymap.begin(); it != mymap.end(); ++it) {
// doSomething()
}
The following code is somewhere along the lines what I wish for. It follows the standard way on how to use BOOST_FOREACH with a std::map. Unfortunately this does not compile:
boost::ptr_map<int, T> mymap;
// insert something into mymap
// ...
typedef pair<int, T> IntTpair;
BOOST_FOREACH (IntTpair &p, mymap) {
int i = p.first;
}
As STL style containers, the pointer containers have a value_type typedef that you can use:
#include <boost/ptr_container/ptr_map.hpp>
#include <boost/foreach.hpp>
int main()
{
typedef boost::ptr_map<int, int> int_map;
int_map mymap;
BOOST_FOREACH(int_map::value_type p, mymap)
{
}
}
I find that using a typedef for the container makes the code a lot easier to write.
Also, you should try to avoid using the contents of detail namespaces in boost, it's a boost convention that they contain implementation details.
I just ran into the same problem today. Unfortunately, Daniel's suggestion will not work with a constant reference to a map. In my case, the ptr_map was a member of a class, and I wanted to loop through it in a const member function. Borrowing Daniel's example, this is what I had to do in my case:
#include "boost/ptr_container/ptr_map.hpp"
#include "boost/foreach.hpp"
int main()
{
typedef boost::ptr_map<int, int> int_map;
int_map mymap;
const int_map& mymap_const_ref(mymap);
BOOST_FOREACH(int_map::const_iterator::value_type p, mymap_const_ref)
{
}
}
It seems that int_map::const_iterator::value_type is equivalent to boost::ptr_container_detail::ref_pair<int, const int* const>.
Save yourself the typing and improve readability by using tuples:
boost::ptr_map<int, T> mymap;
int key;
T * value;
BOOST_FOREACH(boost::tie(key, value), mymap)
{
...
}
This example code compiled for me with g++ 4.1.2:
#include "boost/ptr_container/ptr_map.hpp"
#include "boost/foreach.hpp"
int main()
{
boost::ptr_map<int, int> mymap;
typedef boost::ptr_container_detail::ref_pair<int, int* const> IntPair;
BOOST_FOREACH(IntPair p, mymap)
{
int i = p.first;
}
return 0;
}
I use this homebrew template which adds an iteration type which can be handled by BOOST_FOREACH
namspace homebrew
{
template
<
class Key,
class T,
class Compare = std::less<Key>,
class CloneAllocator = boost::heap_clone_allocator,
class Allocator = std::allocator< std::pair<const Key,void*> >
>
class ptr_map :
public boost::ptr_map<Key,T,Compare,CloneAllocator,Allocator>
{
public:
typedef boost::ptr_container_detail::ref_pair<Key,const T* const> const_ref;
typedef boost::ptr_container_detail::ref_pair<Key,T* const> ref;
};
}
Let's assume that foo and bar are two of your favorite types ;)
typedef homebrew::ptr_map<foo,bar> Map;
int f( const Map& m )
{
BOOST_FOREACH(Map::const_ref v, m)
{
v.first; // foo
v.second; // const bar* const
}
}
or
int f( Map& m )
{
BOOST_FOREACH(Map::ref v, m)
{
v.first; // foo
v.second; // bar* const
}
}
Which one you have to use doesn't seem to depend on the way you use it in the loop (const or non-const) but on the map's constness!! So the following will end up in an error...
int f( Map& m )
{
BOOST_FOREACH(Map::const_ref v, m) // can't use const_ref because m isn't const
{
...
}
}
Weird! isn't it?
The greatest thing to me is that of all this solutions which were suggested here, this is the first one which is handled correctly by the Eclipse CDT syntax coloring (when using the 'Code/Problem' syntax coloring attribute).
It should compile without the reference:
BOOST_FOREACH (IntTpair p, mymap)
I think the problem is that maps do not actually store objects as pairs, but as a tree structure with the first element as the key, so BOOST_FOREACH can't get a reference to a pair but it can create a temporary copy of one.
using ::value_type won't let you const-iterate through the container. I use iterator reference types
typedef boost::ptr_map< myKey, myPtrType > MyMap;
MyMap m;
BOOST_FOREACH( MyMap::iterator::reference it, m )
do_something( it.second );
BOOST_FOREACH( MyMap::const_iterator::reference it, m )
do_something_const( it.second );
In the end, I went for declaring the iteration variable before the loop.
std::pair<std::string, TrailStep*> step;
BOOST_FOREACH(step, m_steps)
{
SAFE_DELETE(step.second);
}
But indeed, there ought to be a simpler way. (Use D instead?)
You might try this uber-cool way to iterate over maps, ptr or otherwise:
https://svn.boost.org/trac/boost/attachment/ticket/3469/foreach_field.hpp
// no typedef needed
BOOST_FOREACH_FIELD((int i)(const T& t), mymap)
// do something with i or t instead of first/second
I'm not sure it will work with a template parameter, but maybe you used that just for abstraction.