map containing a string key and a templated value...

D

devmentee

Hello,

I am trying to create a map/dictionary where the type of key is known
ie std::string, but the value could be of any built in type. ie. int,
double etc. (something along the lines of map<string, T> where T is
template argument)

To hold the value type I have something like:

template<class T>
struct Data
{
T value;
}

Now I am trying to declare a map as below:

std::map<std::string, Data>;

this doesn't/wouldn't work and I don't know how to declare it??

if I have a method which has to pass in this map as a parameter, how
would I do it??
e.g Foo(std::map<string, Data> param ) doesn't work and I don't expect
it to either

Can someone please help!
 
T

Thomas J. Gritzan

Hello,

I am trying to create a map/dictionary where the type of key is known
ie std::string, but the value could be of any built in type. ie. int,
double etc. (something along the lines of map<string, T> where T is
template argument)

To hold the value type I have something like:

template<class T>
struct Data
{
T value;
}

Now I am trying to declare a map as below:

std::map<std::string, Data>;

this doesn't/wouldn't work and I don't know how to declare it??

If you want a heterogenous container, you need run-time polymorphism;
you can't do it with templates alone.

That is, use a virtual base class and templated sub-classes which hold
the actual type.

The easiest way would be to use either of boost::any or
boost::variant<>, they both do something similar:

std::map<std::string, boost::any> mapOfAnyType;
 
D

devmentee

Many Thanks for your prompt reply! Can I please ask following

1) will boost::any let me store any type including built-in types and
are there good examples on their site with regards to this particular
feature?
2) when you said "That is, use a virtual base class and templated
sub-classes which hold the actual type." - did you mean declaring a
something like map<string, base*>? would the base have to be declared
as a template too?
 
Z

zhao.kaiyong

Hello,

I am trying to create a map/dictionary where the type of key is known
ie std::string, but the value could be of any built in type. ie. int,
double etc. (something along the lines of map<string, T> where T is
template argument)

To hold the value type I have something like:

template<class T>
struct Data
{
T value;
}

Now I am trying to declare a map as below:

std::map<std::string, Data>;

this doesn't/wouldn't work and I don't know how to declare it??

if I have a method which has to pass in this map as a parameter, how
would I do it??
e.g Foo(std::map<string, Data> param ) doesn't work and I don't expect
it to either

Can someone please help!

I think you can use

std::map<std::string, Data*>;
 
T

Thomas J. Gritzan

Many Thanks for your prompt reply! Can I please ask following

1) will boost::any let me store any type including built-in types and
are there good examples on their site with regards to this particular
feature?

There are some restrictions for the types documented, but you can store
all built-in types. For the examples, look for yourself:

http://www.boost.org/doc/html/any.html
2) when you said "That is, use a virtual base class and templated
sub-classes which hold the actual type." - did you mean declaring a
something like map<string, base*>? would the base have to be declared
as a template too?

Yes, I meant something like that, and no, the base class should better
not be a template. You can't put a templated class or pointer to
templated class in a container, you can do this only with a concrete
instantiation of the template. That is why you need a base class.

The boost::any class does this for you, and it works as a simple smart
pointer, so you don't even have to store raw pointers in the map.
 
M

Michiel.Salters

Hello,

I am trying to create a map/dictionary where the type of key is known
ie std::string, but the value could be of any built in type. ie. int,
double etc. (something along the lines of map<string, T> where T is
template argument)
[...]

I think you can use

std::map<std::string, Data*>;

No. Data is not a type. It's a template. Any instantiation of the Data
template,
e.g. Data<int>, is a type. The arguments to the std::map template are
two
types. [1] Hence, you can write std::map<std::string, Data<int> >
MyMap;
With the proposed Data template that of course adds little value.

The better solution is already given. (Use boost)

HTH,
Michiel Salters

[1] Not counting the third argument, because it has a default
(std::allocator)
 
D

devmentee

Looks like there is really no way in C++ to have a generic type like
that in a map. Templates solve half the problem but don't go all the
way.. :-(

it is not possible to use std::map<std::string, Data*> for the reasons
mentioned by Michiel.
What I can do is make Data a base class, then derive templated class
from data and instantiate these, and store pointers to these in the
above map. All sounds like too much effort!
I like the idea of using boost but I am not allowed to use it in my
project :-(

Thanks to all of you again...
 
M

mlimber

Hello,

I am trying to create a map/dictionary where the type of key is known
ie std::string, but the value could be of any built in type. ie. int,
double etc. (something along the lines of map<string, T> where T is
template argument)

To hold the value type I have something like:

template<class T>
struct Data
{
T value;
}

Now I am trying to declare a map as below:

std::map<std::string, Data>;

this doesn't/wouldn't work and I don't know how to declare it??

if I have a method which has to pass in this map as a parameter, how
would I do it??
e.g Foo(std::map<string, Data> param ) doesn't work and I don't expect
it to either

Can someone please help!

Just to be clear: do you want to store actual values of different types
in the same map (if so, use boost::any as suggested by others), or are
you just wanting to have a "template typedef" that defines only one of
the existing tempalte parameters like this:

template<typename T>
struct MyMap
{
typedef std::map<std::string,T> type;
};

MyMap<int>::type intMap;
MyMap<float>::type floatMap;

Cheers! --M
 
V

Victor Bazarov

[...] Templates solve half the problem but don't go all the
way.. :-(

[...] All sounds like too much effort!

Life... First you suffer through it, then you die...
 
D

devmentee

Rather die than use VB or C# :p

To the previous poster, no I did think of template typedef but that
only works if i want one specific type. I want a map which stores any
built in data type ie. int, double etc...
so something like map<string, object) in java or other similar
languages...

Victor said:
[...] Templates solve half the problem but don't go all the
way.. :-(

[...] All sounds like too much effort!

Life... First you suffer through it, then you die...
 
M

mlimber

I like the idea of using boost but I am not allowed to use it in my
project :-(

So go ahead and reinvent the wheel: write a discriminated union of your
own, and you'll acheive the same effect.

Cheers! --M
 
D

Diego Martins

mlimber said:
So go ahead and reinvent the wheel: write a discriminated union of your
own, and you'll acheive the same effect.

Cheers! --M

If you like the Java style, create an Object class and make subclasses
that will behave like wrappers of built-in data types (Integer, Bool,
etc.)

map<string,Object *> will be your map type and you can use RTTI to
determine how to access the data

struct Object {
virtual ~Object() {} // allows delete by Object pointer
};

struct Integer: public Object { int data; Integer(int d): data(d) {}
};
struct Bool: public Object { bool data; Bool(bool d): data(d) {} };
....

of course a template can save you from the dirty work
template<typename T>
struct DataWrapper: public Object { T data; DataWrapper(T d): data(d)
};

....

map<string,Object *> suckMap;
suckMap["integer"] = new Integer(10);
suckMap["a little bool"] = new Bool(false);
suckMap["other bool"] = new DataWrapper<bool>(false);
....
if( DataWrapper said:
(suckMap["lonely long") ) {
cout << t->data << "is a long value" << endl;
}

Hope this helps

Diego Martins
 
M

mlimber

Diego said:
If you like the Java style, create an Object class and make subclasses
that will behave like wrappers of built-in data types (Integer, Bool,
etc.)

This is generally frowned upon. See the reasons why here:

http://www.research.att.com/~bs/bs_faq2.html#object

and

http://www.artima.com/intv/goldilocks2.html
map<string,Object *> will be your map type and you can use RTTI to
determine how to access the data

struct Object {
virtual ~Object() {} // allows delete by Object pointer
};

struct Integer: public Object { int data; Integer(int d): data(d) {}
};
struct Bool: public Object { bool data; Bool(bool d): data(d) {} };
...

of course a template can save you from the dirty work
template<typename T>
struct DataWrapper: public Object { T data; DataWrapper(T d): data(d)
};

...

map<string,Object *> suckMap;
suckMap["integer"] = new Integer(10);
suckMap["a little bool"] = new Bool(false);
suckMap["other bool"] = new DataWrapper<bool>(false);
...
if( DataWrapper said:
(suckMap["lonely long") ) {
cout << t->data << "is a long value" << endl;
}

Assuming you fix the syntax errors in this code, it will still not
likely do what you were attempting since std::map's bracket operator
will actually *insert* a default-initialized value (read: null pointer)
with the key "lonely long" if the key cannot be found in the map (cf.
footnote 3 here: http://www.sgi.com/tech/stl/Map.html#3).

This won't cause an immediate problem since dynamic_cast will either
succeed (if the map element with that key has a DataWrapper<long>* as
its value) or will return null and not enter the body of the
if-statement (because the type is different or because the pointer is
null). But the use of std::map<>::eek:perator[]() could potentially insert
a null pointer, which the user of the map may not expect and which you
probably did not intend. This could be fixed by using
std::map<>::find() (or similar) to do the look-up instead.

Cheers! --M
 
D

Diego Martins

mlimber said:
Diego said:
If you like the Java style, create an Object class and make subclasses
that will behave like wrappers of built-in data types (Integer, Bool,
etc.)

This is generally frowned upon. See the reasons why here:

http://www.research.att.com/~bs/bs_faq2.html#object

and

http://www.artima.com/intv/goldilocks2.html
map<string,Object *> will be your map type and you can use RTTI to
determine how to access the data

struct Object {
virtual ~Object() {} // allows delete by Object pointer
};

struct Integer: public Object { int data; Integer(int d): data(d) {}
};
struct Bool: public Object { bool data; Bool(bool d): data(d) {} };
...

of course a template can save you from the dirty work
template<typename T>
struct DataWrapper: public Object { T data; DataWrapper(T d): data(d)
};

...

map<string,Object *> suckMap;
suckMap["integer"] = new Integer(10);
suckMap["a little bool"] = new Bool(false);
suckMap["other bool"] = new DataWrapper<bool>(false);
...
if( DataWrapper said:
(suckMap["lonely long") ) {
cout << t->data << "is a long value" << endl;
}

Assuming you fix the syntax errors in this code, it will still not
likely do what you were attempting since std::map's bracket operator
will actually *insert* a default-initialized value (read: null pointer)
with the key "lonely long" if the key cannot be found in the map (cf.
footnote 3 here: http://www.sgi.com/tech/stl/Map.html#3).

This won't cause an immediate problem since dynamic_cast will either
succeed (if the map element with that key has a DataWrapper<long>* as
its value) or will return null and not enter the body of the
if-statement (because the type is different or because the pointer is
null). But the use of std::map<>::eek:perator[]() could potentially insert
a null pointer, which the user of the map may not expect and which you
probably did not intend. This could be fixed by using
std::map<>::find() (or similar) to do the look-up instead.

Cheers! --M

thanks for the find() advice :)

personally, I dislike this java-style. I only presented a possible
working solution for a "generic" container

# A "universal" class encourages sloppy thinking about types and
interfaces and leads to excess run-time checking.
# Using a universal base class implies cost: Objects must be
heap-allocated to be polymorphic; that implies memory and access cost.
Heap objects don't naturally support copy semantics. Heap objects don't
support simple scoped behavior (which complicates resource management).
A universal base class encourages use of dynamic_cast and other
run-time checking.

these issues are right, but what is the alternative if we really need a
generic container?

cya
Diego Martins
 
M

mlimber

Diego said:
personally, I dislike this java-style. I only presented a possible
working solution for a "generic" container

# A "universal" class encourages sloppy thinking about types and
interfaces and leads to excess run-time checking.
# Using a universal base class implies cost: Objects must be
heap-allocated to be polymorphic; that implies memory and access cost.
Heap objects don't naturally support copy semantics. Heap objects don't
support simple scoped behavior (which complicates resource management).
A universal base class encourages use of dynamic_cast and other
run-time checking.

these issues are right, but what is the alternative if we really need a
generic container?

boost::any, of course. :) See the previous posts in this thread.

Cheers! --M
 
D

Diego Martins

mlimber said:
boost::any, of course. :) See the previous posts in this thread.

Cheers! --M

sounds great! Can you explain to us how boost::any is implemented?

Diego Martins
 
M

mlimber

Diego said:
sounds great! Can you explain to us how boost::any is implemented?

Not here. Ask on the Boost Developer (or User) list, or look through
the code.

Cheers! --M
 
D

Diego Martins

mlimber said:
Not here. Ask on the Boost Developer (or User) list, or look through
the code.

Cheers! --M

I can't figure out why some members of this great group take this
defensive posture.

Me and many users want to code in C++ and learn more C++ to write
better code in C++. This is the reason we spend time in usenet forums.

It is not bad to get a tool as a solution, but we must consider the
case when we aren't able to use external tools. Instead, many users do
prefer saying "BOOST AND GOOD BYE". This is not the first time I see
this things here :-(

This is not a helpdesk. This is a place where the users share their C++
experiencies. Why do ruin that?

Go rest and think about that

Diego Martins
HP
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,776
Messages
2,569,602
Members
45,184
Latest member
ZNOChrista

Latest Threads

Top