Claw  1.7.3
Multi Type Map

The multi type map is a map in which values of different types can be associated to the same key.

Why this class

In its original form, the goal of the claw::multi_type_map was to provide a simple and elegant solution to associate values of several types to keys in a map. This is a quite recurrent problem in programming. Indeed, a quick search on the Internet will result on questions like in this forum, or this other forum or eventually this last one.

One solution for this problem would be to store the values in several maps, and deal with them according to the context. An other solution would be to use a single map and to impose to the values to inherit from a given class. Both solutions are tricky and painful.

Hopefully, when using claw::multi_type_map you can obtain the same behavior without worrying. Plus, several functionalities are ready, such as exploring the whole set of pairs (key, value).

Examples

This section presents the creation and the exploration of a claw::multi_type_map.

Declaring and using a multi_type_map

The following program defines a multi_type_map where the values of type bool, float or std::string are associated to keys of type int.

typedef claw::multi_type_map<int, my_type_list> map_type;
map_type example_map;

The first statement defines the list of the types of the values stored in the map. This list must end with claw::meta::no_type. Here, the claw::meta::type_list_maker type from the metaprogramming package with manage this for us.

Then you can insert values in the map:

example_map.set<bool>( 0, false );
example_map.set<bool>( 1, true );
example_map.set<float>( 10, 3.14159 );
example_map.set<float>( 30, 2.71828 );
example_map.set<float>( 20, 1.61803 );
example_map.set<std::string>( 100, "a string" );
example_map.set<std::string>( 200, "an other string" );

You can also get the value associated with a given key for a given type:

std::cout << example_map.get<bool>(1) << std::endl;

Or even iterate on the pairs defined for a given type:

for ( map_type::iterator<float>::type it=example_map.begin<float>();
it!=example_map.end<float>(); ++it )
std::cout << it->first << '\t' << it->second << std::endl;

Exploring the whole map

Exploring the map for a given type is a convenient thing. An other convenient thing would be to iterate over the map for all the types at once. This is what the claw::multi_type_map_visitor is made for.

First we have to define a function object that will receive the keys and the values. Our goal is to print the values in a stream, thus we define:

struct map_printer
{
std::ostream& m_stream;
map_printer( std::ostream& os )
: m_stream(os)
{ }
template<typename T>
void operator()( int key, T value ) const
{
m_stream << "(" << key << ", " << value << ")" << std::endl;
}
};

Then, all we have to do is to pass an instance of this function object to a claw::multi_type_map_visitor:

visitor.run( example_map, map_printer(std::cout) );

The following output is obtained from this program:

(0, 0)
(1, 1)
(10, 3.14159)
(20, 1.61803)
(30, 2.71828)
(100, a string)
(200, an other string)

Classes related to the multi type map

This section presents the interfaces of the structures related to the claw::multi_type_map.

The claw::multi_type_map class

The claw::multi_type_map class is defined as follows. For the sake of clarity, the end of the recursion on the inheritance is not described here.

Instantiations of the claw::multi_type_map type must meet the following type requirements:

template<typename Key, typename TypeList>
class multi_type_map:
public multi_type_map<Key, typename TypeList::queue_type>
{
public:
typedef Key key_type;
typedef typename TypeList::head_type value_type;
typedef multi_type_map<Key, TypeList> self_type;
typedef multi_type_map<Key, typename TypeList::queue_type> super;
template<typename ValueType>
struct iterator
{
typedef implementation_defined type;
typedef
typename implementation_defined const_type;
};
public:
template<typename ValueType>
void erase( typename iterator<ValueType>::type it );
template<typename ValueType>
std::size_t erase( const key_type& k );
template<typename ValueType>
const ValueType& get( const key_type& k ) const;
template<typename ValueType>
ValueType& get( const key_type& k );
template<typename ValueType>
void set( const key_type& k, const ValueType& v );
void set( const self_type& m );
template<typename ValueType>
bool exists( const key_type& k ) const;
std::size_t size() const;
template<typename ValueType>
typename iterator<ValueType>::type begin();
template<typename ValueType>
typename iterator<ValueType>::type end();
template<typename ValueType>
typename iterator<ValueType>::const_type begin() const;
template<typename ValueType>
typename iterator<ValueType>::const_type end() const;
};

claw::multi_type_map_visitor to explore a map

The claw::multi_type_map_visitor is defined as follows.

class multi_type_map_visitor
{
public:
template<typename Key, typename TypeList, typename Function>
void run( multi_type_map<Key, TypeList>& m, Function f ) const;
};

The type passed for the Function type argument of the claw::multi_type_map_visitor::run method must be a function object with an operator() similar to the following, where [text] means that text is optional.

template<typename T> [any_return_type] operator() (Key [const [&]], T [[const] &]) [const]

In other words:

Moreover, it is worth noting that any element can be inserted in the map during the visit and that removing the visited element from the map is allowed.