Sign in to follow this  
MeshGearFox

C++ - Initializing maps?

Recommended Posts

If I have a map<int,string> lookup; object in my code, is there any way to initialize it to some set of entries? Similarly, if I have a map within an object, is there anything I could pass into the object's constructor that would allow me to initialize the map in that object fairly easily?

Share this post


Link to post
Share on other sites
I generally do something like:

#include <map>

const struct Initializer {
int first;
const char * second;

operator std::pair<const int, std::string>() const {
return std::make_pair(first, second);
}
} initializers[] = {
{ 1, "one" },
{ 2, "two" },
{ 3, "three" }
};
const int num_initializers = sizeof(initializers) / sizeof(Initializer);

// somewhere else
std::map<int, std::string> my_map(initializers, initializers + num_initializers);

though there's also boost::assign.

Share this post


Link to post
Share on other sites
Thanks. Um, would you mind if I borrowed that Initializer code?

Anyway, what are pair and make_pair? I've seen them used before in various examples but I'm having a hard time finding info on cppreference about maps. Is there another site that documents them better?

Also, boost looks nice, at least from a code readability standpoint. What sort of performance hit would it have, though?

Share this post


Link to post
Share on other sites
boost::assign is really nice for map pairs - I suspect it is reasonably efficient despite doing incremental insertions since the container is generally going to have to locate the position in the rb tree for each element. I dont use assign support for sequence types but use more traditional stl iterators.


#include <string>
#include <iostream>
#include <vector>
#include <boost/assign.hpp>

using namespace std;
using namespace boost::assign;


template< class T>
int pod_array_size( const T &a)
{
return sizeof( a) / sizeof( *a);
}

int main(int argc, char **argv)
{
// initialise a std::vector of strings with static array
static const char *names_[] = { "bannanas", "pineapples", "pasionnames", "plums" } ;
vector< string> names( names_, names_ + pod_array_size( names_));

// initialse std::map with std::pair< string, int> - using boost::asign
map< string, int> groceries;
insert( groceries)
( "apples", 3)
( "oranges", 2)
( "lemons" , 7);


cout << "sizeof names " << names.size() << " size of groceries " << groceries.size() << endl;
}
;

Share this post


Link to post
Share on other sites
Quote:
Original post by MeshGearFox
Thanks. Um, would you mind if I borrowed that Initializer code?

I wouldn't have posted it otherwise. :)

Quote:
Anyway, what are pair and make_pair?

std::pair<> is a class that just holds two values. It basically works out to:

template <typename T, typename U>
struct pair {
T first;
U second;

pair() : first(T()), second(U()) {}
pair(const T & a, const U & b) : first(a), second(b) {}
template <typename V, typename W>
pair(const std::pair<V, W> & other) : first(other.first), second(other.second) {}
};

std::map<Key, Value> internally stores its data as std::pair<const Key, Value>s.

std::make_pair<>() is a template function that just takes its two arguments and returns a pair based off of the values. It's usually less typing to use std::make_pair(a, b) rather than std::pair<FirstType, SecondType>(a, b).

Quote:
Also, boost looks nice, at least from a code readability standpoint. What sort of performance hit would it have, though?

In general, it's "fast enough". If initializations are a bottleneck for your program then you've probably got a algorithmic problem (performing too many initializations) rather than a code performance problem (initializations being too slow).

Share this post


Link to post
Share on other sites
Okay. Boost is pretty much what I was looking for, however I've got another interesting, related problem. Maybe not that interesting.

I've got a map inside of a class and I'd like to set its values via a constructor.

One way to do this is to just create all of the maps I need and then pass them in when I make the maps, as I'm not going to be creating any new ones during the main program loop. However, if I do this, from my understanding, I'll have both the map inside of the object and outside.

One solution, I imagine, would be to pass a reference to the map in question to the constructor, but that doesn't seem to be possible for maps. Or rather, it seems that I'm able to create a pointer to a map and then set that pointer equal to the memory location of another map, but after that, I have no idea how I should access the data within.

So, if I have something that doesn't use pointers, cout << myContainer.myMap[key] << endl; seems to work as it should, but if myMap is a pointer to a map, it doesn't look like I can use similar syntax to that.

Share this post


Link to post
Share on other sites
Quote:
One solution, I imagine, would be to pass a reference to the map in question to the constructor, but that doesn't seem to be possible for maps.


Initialize a reference type in the classes constructor - this is general for c++ and can initialize any reference type.

struct A
{
A( map< string, int> &m1) : m1( m1) { }
map< string, int> &m1;
};




Quote:
So, if I have something that doesn't use pointers, cout << myContainer.myMap[key] << endl; seems to work as it should, but if myMap is a pointer to a map, it doesn't look like I can use similar syntax to that.


Change the syntax to use the pointer member accessor operator like this

 
cout << pmyContainer->myMap[key];
<edit - botched this>



I wouldnt worry about stuff like boost::assign at the moment which really only aids syntactical clarity - and even here it is arguable/debatable since many programmers dont know/use the boost api's - particularly the less common parts such as boost::assign. Instead focus on how std::map works with std::pair and make_pair().

But do, when you get a chance look at boost::shared_ptr< T> as an alternative to using raw pointers - you will see their use everywhere in the forums here for example. Many would argue they are a glaring omission from the standard c++ library although they will definately be in the future cxx0x revision of c++ as standard. They work like raw pointers but provide lifetime management - in large part alleviating you from having to code this behaviour.

Using your problem case as an example - then instead of using a pointer to the map you could define your map object and use like this

typedef shared_ptr< map< string, int> > mymap; 
mymap m;
m->insert( make_pair( "property", 123));
// pass to my object as a shared resource
o.init( m)




In this way you provide your map to your objects and avoid duplicating the information (if this is the objective) in the same way as using a raw pointer but get the added benefit that when all references to the map are gone the map will also be destroyed.

[Edited by - chairthrower on April 23, 2008 4:33:00 AM]

Share this post


Link to post
Share on other sites
Quote:
I wouldnt worry about stuff like boost::assign at the moment which really only aids syntactical clarity - and even here it is arguable/debatable since many programmers dont know/use the boost api's - particularly the less common parts such as boost::assign.


True, though I'd say that for me it's more readable which is why I *think* it might be more appropriate for what I'm trying to do now. Also, I'm still testing out some of the stuff that uses the maps so an easier method of initializing them, for me at least, seems to be a good idea in this stage.

cout << pmyContainer->myMap[key];


That is what I tried doing. It doesn't work. It gives me a: "base operand of `->' has non-pointer type myMap" error. I think before that my class code was a bit different.



class mapContainer {
public:
map <string, int> *myMap;
mapContainer(map<etc> *_myMap) { myMap = _myMap; }
};


which seemed to work about the same as far as initializing mapContainer went, and I got the same error when trying to access the map.

Share this post


Link to post
Share on other sites
Quote:
Original post by MeshGearFox
cout << pmyContainer->myMap[key];


That is what I tried doing. It doesn't work. It gives me a: "base operand of `->' has non-pointer type myMap" error. I think before that my class code was a bit different.


(*obj.map)[value] or obj.map->operator[](value)

Quote:


class mapContainer {
public:
map <string, int> *myMap;
mapContainer(map<etc> *_myMap) { myMap = _myMap; }
};


which seemed to work about the same as far as initializing mapContainer went, and I got the same error when trying to access the map.


You should use initializer lists to initialize things where possible, if you don't include an initializer then it will be default initialized and then you are assigning a new value to it in the constructor body (note the semantic difference).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this