Template to insert into a map? Is this really necessary?

J

Jim Langston

I'm working on a program, and at one time I find it necessary to load
classes into a map. Now, these classes don't have default constructors, so
I can't retrieve them using
MyMap[MyKey].

So I wound up with a real ugly line:

DropdownBox& ThisBox = (Dropdowns.insert( Dropdowns.begin(),
std::make_pair<std::string, DropdownBox>( "RoteDots", DropdownBox( Parent,
IDC_RoteDots ) ) ))->second;

My map is fairly simple.

std::map<std::string, DropdownBox> Dropdowns;

Well, I tried to improve it a little, and came up with

std::map<std::string, DropdownBox>::iterator it = Dropdowns.insert(
Dropdowns.begin(), std::make_pair<std::string, DropdownBox>( "RoteDots",
DropdownBox( Parent, IDC_RoteDots ) ) );
DropdownBox& ThisBox = it->second;

but that's still ugly. So I decided to make a template function to help.
The template itself is kinda messy, but makes the code easier.

The template:

template<class K, class D>
D& InsertToMap( K& Key, D& Data, std::map<K, D>& Map )
{
std::map<K, D>::iterator it = Map.insert( Map.begin(), std::make_pair<K,
D>( Key, Data ) );
return it->second;
}

The usage:

DropdownBox& ThisBox = InsertToMap( std::string("RoteDots"), DropdownBox(
Parent, IDC_RoteDots ), Dropdowns );

which even though still a bit ugly, is better than the first one IMO.

Of course I could always insert into the map then use .find() but I've
alwasy found that bothersome in itself.

Any opionions?
 
O

Old Wolf

template<class K, class D>
D& InsertToMap( K& Key, D& Data, std::map<K, D>& Map )

The usage:

DropdownBox& ThisBox = InsertToMap( std::string("RoteDots"),
DropdownBox(Parent, IDC_RoteDots ), Dropdowns );

This is an error; you can't bind a temporary to
a non-const reference. Either switch your compiler
to conforming mode, or post your exact code.

Also, why would you want to return a reference
to the input data. It doesn't make any logical
sense to do anything with it once a copy of it
has been put into the map.
which even though still a bit ugly, is better than the first one

You seem to have omitted trying:
Dropdowns.insert( std::make_pair(
std::string("RoteDots"), DropDownBox( Parent, IDC_RoteDots ) ) );

(the whole point of std::make_pair is that you do
not explicitly specify the template parameters).

If you dont mind using a function then the obvious is
(replace Ddmap with the typedef you've defined for
your map):
void insert_dropdown( Ddmap &map,
Ddmap::key_type const &key, Ddmap::value_type const &value )
{
map.insert( std::make_pair(key, value) );
}

and then call:
insert_map( Dropdowns, "RoteDots, DropDownBox(foo,bar) );

You could even go as far as:
void insert_dropdown( Ddmap &map,
Ddmap::key_type const &key, Foo *foo, Bar *bar )
{
map.insert( std::make_pair(key, DropdownBox(foo,bar)) );
}
 
J

Jim Langston

Old Wolf said:
This is an error; you can't bind a temporary to
a non-const reference. Either switch your compiler
to conforming mode, or post your exact code.

Dang, I forgot to change from Warning level 3 to Warning level 4 when I
created this project. With W4 it does indeed say it's non conforming. Hmm.
Also, why would you want to return a reference
to the input data. It doesn't make any logical
sense to do anything with it once a copy of it
has been put into the map.

Because I am creating the instance in the map, then I need to do things with
it (add entries, etc..).
I.E.
DropdownBox& RoteDots = InsertToMap( Dropdowns, std::string("RoteDots"),
DropdownBox( Parent, IDC_RoteDots ) );
RoteDots.AddEntry( "0" );
RoteDots.AddEntry( "1" );
RoteDots.AddEntry( "2" );
RoteDots.AddEntry( "3" );
RoteDots.AddEntry( "4" );
RoteDots.AddEntry( "5" );
You seem to have omitted trying:
Dropdowns.insert( std::make_pair(
std::string("RoteDots"), DropDownBox( Parent, IDC_RoteDots ) ) );

(the whole point of std::make_pair is that you do
not explicitly specify the template parameters).

I can fix that in my template.
If you dont mind using a function then the obvious is
(replace Ddmap with the typedef you've defined for
your map):
void insert_dropdown( Ddmap &map,
Ddmap::key_type const &key, Ddmap::value_type const &value )
{
map.insert( std::make_pair(key, value) );
}

and then call:
insert_map( Dropdowns, "RoteDots, DropDownBox(foo,bar) );

You could even go as far as:
void insert_dropdown( Ddmap &map,
Ddmap::key_type const &key, Foo *foo, Bar *bar )
{
map.insert( std::make_pair(key, DropdownBox(foo,bar)) );
}

Well, without the need to retrieve a reference to the added entry, it is
eaiser by a lot of ways, but I need to retrieve a reference to the entry
just added.
 
O

Old Wolf

Dang, I forgot to change from Warning level 3 to Warning level 4 when I
created this project. With W4 it does indeed say it's non conforming. Hmm.

You should actually fix this (i.e. make the function
take its new key and value by const reference).
Well, without the need to retrieve a reference to the added entry, it is
eaiser by a lot of ways, but I need to retrieve a reference to the entry
just added.

return map.insert(....)->second;
 
J

Jim Langston

Old Wolf said:
You should actually fix this (i.e. make the function
take its new key and value by const reference).

I did.
return map.insert(....)->second;

Well, that's what I'm doing. Although I am saving to an iterator first then
returning ->second
 
J

James Kanze

I'm working on a program, and at one time I find it necessary to load
classes into a map. Now, these classes don't have default constructors, so
I can't retrieve them using
MyMap[MyKey].
So I wound up with a real ugly line:
DropdownBox& ThisBox = (Dropdowns.insert( Dropdowns.begin(),
std::make_pair<std::string, DropdownBox>( "RoteDots", DropdownBox( Parent,
IDC_RoteDots ) ) ))->second;

Just curious, but why the iterator argument? It's only a hint,
and only helps if the insertion occurs immediately in front of
it---in this case, if the new element will be the first element.

Most of the type, I'll have the map typedef'ed, and use its
value type:

typedef std::map< std::string, DropdownBox >
DDBoxMap ;
// ...
DropdownBox& thisBox = dropdowns.insert(
DDBoxMap::value_type(
"RoteDots",
DropdownBox( Parent,
IDC_RoteDots ) )
.first->second ;

Of course, most of the time, I'll also want to know if the
insertion succeeded.
My map is fairly simple.
std::map<std::string, DropdownBox> Dropdowns;
Well, I tried to improve it a little, and came up with
std::map<std::string, DropdownBox>::iterator it = Dropdowns.insert(
Dropdowns.begin(), std::make_pair<std::string, DropdownBox>( "RoteDots",
DropdownBox( Parent, IDC_RoteDots ) ) );
DropdownBox& ThisBox = it->second;
but that's still ugly. So I decided to make a template
function to help. The template itself is kinda messy, but
makes the code easier.
The template:
template<class K, class D>
D& InsertToMap( K& Key, D& Data, std::map<K, D>& Map )
{
std::map<K, D>::iterator it = Map.insert( Map.begin(), std::make_pair<K,
D>( Key, Data ) );
return it->second;
}

The real question is: what should the behavior be if the object
is already there: an error, use the already existing object, or
replace it with the new one. For the first two, you can base
your behavior on the return value of insert:

template< typename Key, typename Value >
Value&
insertIntoMap(
std::map< Key, Value >&
map,
Key const& key,
Value const& value )
{
typedef std::map< Key, Value >
Map ;
std::pair< Map::iterator, bool >
result
= map.insert( Map::value_type( key, value ) ) ;
if ( ! result.second ) {
// error handling, or
// result.first->second = value ;
// to set the new value.
// Drop the if to use the existing value.
}
return result.first->second ;
}
 
J

Jim Langston

I'm working on a program, and at one time I find it necessary to load
classes into a map. Now, these classes don't have default constructors,
so
I can't retrieve them using
MyMap[MyKey].
So I wound up with a real ugly line:
DropdownBox& ThisBox = (Dropdowns.insert( Dropdowns.begin(),
std::make_pair<std::string, DropdownBox>( "RoteDots", DropdownBox(
Parent,
IDC_RoteDots ) ) ))->second;

Just curious, but why the iterator argument? It's only a hint,
and only helps if the insertion occurs immediately in front of
it---in this case, if the new element will be the first element.

If you don't give an iterator as the first parameter, a different .insert is
called which returns a value instead of an iterator. I needed the iterator,
so gave it an iterator.
Most of the type, I'll have the map typedef'ed, and use its
value type:

typedef std::map< std::string, DropdownBox >
DDBoxMap ;
// ...
DropdownBox& thisBox = dropdowns.insert(
DDBoxMap::value_type(
"RoteDots",
DropdownBox( Parent,
IDC_RoteDots ) )
.first->second ;

Of course, most of the time, I'll also want to know if the
insertion succeeded.

Most of the time I would too. In this particular case, I didn't care. I
didn't even really care what iterator it gave me with as a result. I knew
they were unique values.

[Snip the rest]

The funny thing is, after I implented this, and finished my program, I
realized I never once searched for the key. And then I realized a structure
would be better to hold the instances than a map :/ So I deleted the
template and everything associated with it.
 
J

James Kanze

"James Kanze" <[email protected]> wrote in message
I'm working on a program, and at one time I find it
necessary to load classes into a map. Now, these classes
don't have default constructors, so I can't retrieve them
using MyMap[MyKey].
So I wound up with a real ugly line:
DropdownBox& ThisBox = (Dropdowns.insert( Dropdowns.begin(),
std::make_pair<std::string, DropdownBox>( "RoteDots", DropdownBox(
Parent,
IDC_RoteDots ) ) ))->second;
Just curious, but why the iterator argument? It's only a hint,
and only helps if the insertion occurs immediately in front of
it---in this case, if the new element will be the first element.
If you don't give an iterator as the first parameter, a
different .insert is called which returns a value instead of
an iterator. I needed the iterator, so gave it an iterator.

The "value type" returned is an std::pair, with the iterator as
first element. You still get the iterator.
Most of the time I would too. In this particular case, I
didn't care. I didn't even really care what iterator it gave
me with as a result. I knew they were unique values.

In which case, you can always assert that the second element of
the pair returned is true. (Or just ignore it.)
 

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,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top