Mapping-Semantics Genericity

# Mapping-Semantics

This section describes genericity over different mapping-semantics. It is organized as follows.

1. Introduction
2. Data Types as a Policy
3. The Basic Problem
4. Mapping Levels
5. Tags and Traits
6. Drawbacks

## Introduction

Motivation::Mapping Semantics discussed scalability issues with the STL's non-unique-mapping associative containers; non-unique association inherently embeds linked-lists in associative containers resulting in scalability problems and other problems.

In `pb_assoc`, all containers have unique-key semantics. Each key is uniquely mapped to "something".

## Data Types as a Policy

All associative-containers in `pb_assoc` are parameterized by a data type. E.g., cc_hash_assoc_cntnr is parameterized as

    template<
    	typename Key,
    	typename Data,
    	...>
    class cc_hash_assoc_cntnr;

There are no separate classes for maps, sets, multimaps, and multisets (as the STL has). Rather, the mapping-semantic is set by specifying the `Key` parameter.

1. If `Data` is any type (e.g., `int` or `std::string`), then the container is a "map" - it maps each `Key` object to a `Data` object.
2. If `Data` is `null_data_type`, then the container is a "set" - it stores each `Key` object. In this case, each `Key` object is not really mapped to anything (except, implicitly, to the fact that it is stored in the container object).
3. If `Data` is compound_data_type`<Cntnr>`, then the container is a "multimap" - it maps each `Key` object into a `Cntnr` object. This structure is recursive - `Cntnr` itself can be a "map", "set", "multimap", and so forth.

Each container derives from one of the three containers in the oval of Figure Data-types as a policy .

1. `basic_assoc_cntnr` is the base for most instantiations of a container's `Data` paramter. This base includes the definition of `data_type`, and supports `operator[]`.
2. `basic_assoc_cntnr` is the base for a `null_data_type` instantiation of a container's `Data` paramter. This base lacks the definition of `data_type`, and does not support `operator[]`.
3. `basic_assoc_cntnr` is the base for a `compound_data_type``<Cntnr>` instantiation of a container's `Data` paramter. This base includes the definition of `data_type`, and supports `operator[]`. It further supports some advanced functionality described in the remainder of this section.

######

###### Data-types as a policy.

## The Basic Problem

Consider a `pb_assoc` "multimap" mapping integers to characters. Since a `pb_assoc` "multimap" is a "map" of "sets", if `m` is an object of this type, it is not possible to directly use `m.insert(std::make_pair(2, 'b')` (however, it is possible to directly use `m[2].insert('b')`). In would be nice if this method whould be supported.

Put differently, while the `pb_assoc` "multimap" can be viewed logically as the collection

{ `int` → {`char`} },

It would be nice if it could simultaneously be viewed as the collection

{ (`int`, `char`) },

i.e., a "set" of pairs.

In more general terms, it would be nice to be able to simultaneously view a collection

{ key_type_0 → { key_type_1 → { key_type_2 → { key_type_3 → { ... }}}}}

as each of the following:

{ (key_type_0, key_type_1) → { key_type_2 &rarr { key_type_e → { ... }}}},

{ (key_type_0, key_type_1, key_type_2) &rarr { key_type_3 → { ... }}}

{ (key_type_0, key_type_1, key_type_2, key_type_3 ) &rarr { }}

...

Mapping_Levels discusses the mechanism for these multiple views in `pb_assoc`

## Mapping Levels

Each associative container in `pb_assoc` has a mapping level. The mapping level is defined by the instantiation of a container's `Data` parameter:

1. If the `Data` parameter is instantiated by `null_data_type` (i.e., the container is a "set"), then the mapping level is 1.
2. If the `Data` parameter is instantiated by compound_data_type`<Cntnr>` (i.e., the container is a "multimap"), then the mapping level is 1 + the mapping level of `Cntnr`.
3. If the `Data` parameter is instantiated by any other type, e.g., `char` (i.e., the container is a "map"), then the mapping level is 1.

Containers can be rebound, at compile time, to different mapping levels. The compound data-type specialization `basic_assoc_cntnr` defines internally

    template<
    	int Mapping_Level>
    struct rebind
    {
    	typedef
    		...
    		other;
    };

(which is similar to the STL's allocator rebind mechanism). the type `other` is the view of the container with mapping level `Mapping_Level`. The container can be safely cast to `other`.

As an example, consider the type

    typedef
    	cc_hash_assoc_cntnr<
    		int,
    		compound_data_type<
    			tree_assoc_cntnr<
    				char,
    				null_data_type> > >
    	cntnr_t;

which is a "map" mapping each `int` to a "set" of `char`s. In this case, `cntnr_t` has mapping level 2.

An object of type `cntnr_t` cannot support `insert(std::make_pair(2, 'b'));`. On the other hand, the following code snippet shows how to do so:

    cntnr_t c;

    typedef
    	t::rebind<1>::other
    	t_;

    ((t_ &)c).insert(std::make_pair(2, 'b'));

`mapping_level_example.cpp` shows a more detailed example.

## Tags and Traits

It is, of course, beneficial to query types for their mapping semantics.

Each container defines internally the type `ms_category` as its mapping-semantics tag (hopefully this name is not copyrighted by some major corporation). The possible tags, shown in Figure are the following:

1. `basic_ms_tag` is a basic mapping-semantics tag. It is the type defined by "set"s.
2. `data_enabled_ms_tag` is a mapping-semantics tag of types that have data. It is the type defined by "map"s.
3. `compound_data_enabled_ms_tag` is a mapping-semantics tag of types that have compound data. It is the type defined by "multimap"s.

Additionally, a container's mapping semantics can be queried by traits. For any container `Cntnr`,

    ms_traits<Cntnr>::mapping_level

indicates the mapping level of the container, for example.

## Drawbacks

`pb_assoc`'s mapping-semantics design has some drawbacks compared to that of the STL.

### Equivalent, Non-Identical Keys

The STL's multimaps and multisets allow storing equivalent, non-identical keys [kleft00sets]. For example, assume a bank maintains a data structure monitoring the accounts opened by each person. This could be modeled as the following:

    // Name type.
    typedef
    	std::string
    	name;

    // Account-id type.
    typedef
    	unsigned long
    	account_id;

    // Association between a name and an account id.
    class opened_info
    {
    public:
    	...

    	// Comparison operator.
    	bool
    	 	operator<
    	 	(const opened_info &r_other)
    	{
    		Comparison is defined as the comparison of the names.
    	 	return m_name < r_other.m_name;
    	}

    private:
    	name m_name;

    	account_id m_acc_id;
    };

    // A multiset of opened accounts.
    typedef
    	std::multiset<
    		opened_info>
    	all_opened_info;

`std::multiset` can accomodate multiple equivalent, non-identical `opened_info` - those with the same name but different account id.

In `pb_assoc`, however, non-unique mapping is unsupported. The equivalent to the above could be

    typedef
    	tree_assoc_cntnr<
    		name,
    		compound_data_type<
    			cc_hash_assoc_cntnr<
    				account_id> > >
    	all_opened_info;

The drawback lies in the fact that the data stored in `all_opened_info` is less encapsulated - an `opened_info` object needs to be constructed when a specific name and account are found, and an `opened_info` object needs to be decomposed into `name` and `account_id` objects when it is inserted into a `all_opened_info` object.

It should be noticed however, that the above drawbacks - construction and decomposition are constant-time additive drawbacks. The drawbacks of the STL's associative containers are in terms of orders of growth.

### Definition of `value_type`

The STL's associative containers contain a pleasingly uniform definition of the `value_type` of a container. If a container is parameterized by `key` as its `Key`, and `data` as its `Data`, then its `value_type` is `std::pair<const key, data>`; for example, the `value_type` of `std::map<int, char>` is `std::pair<const int, char>`. Futhermore, the `value_type` of a container and the `value_type` of the container's iterators are identical.

In `pb_assoc`, conversely, the rules are more complex.

For one, a container's `value_type` is, in general `std::pair<const Key, Data>`, but if `Data` is `null_data_type`, then the `value_type` is `Key`, and if `Data` is `compound_data_type<Cntnr>`, then the `value_type` is `std::pair<const Key, Cntnr>`.

Futhermore, assume that `Cntnr` is an associative container with more than a single mapping level, and let `Cntnr_` be defined as

    typedef
    	typename Cntnr::template rebind<i>::other
    	Cntnr_;

i.e., the container rebound to a different mapping level. In this case, the `value_type` of the rebound container is not the `value_type` of the rebound container's iterators. I.e., it is not true that `typename Cntnr_::value_type` is the same as `typename Cntnr_::iterator::value_type`. This complication never exists for the STL's container.

######

###### Iterator of a rebound type.

### Multisets

`pb_assoc` does not contain a "multiset" type. The closest equivalent is mapping keys to non-negative integral types, e.g., `size_t`.