Populate an STL map with two arrays (C++)

Bench 0 Tallied Votes 2K Views Share

For anyone who gets fed up with the somewhat verbose syntax involved in populating an STL map container with static arrays of std::pair (Or any of the other somewhat tedious ways of writing 'pair' combinations), here is a function which allows two different (statically allocated) arrays, of equal length, to be used to populate a map by copy-initialisation.

One popular use of STL maps is to create a custom indexed lookup table, where keys and values can be anything (which must obey all the usual STL map key/value rules). This is intended to make the process a little cleaner.

an example is shown in main()

#include <map>

template<typename KeyType, typename ValueType, int N>
class mapmaker
{
    std::pair<KeyType, ValueType> (&table)[N];
    const KeyType (&keys)[N];
    const ValueType (&vals)[N];

    template<int pos> void fill_pair()
    {
        table[pos].first = keys[pos];
        table[pos].second = vals[pos];
        fill_pair<pos-1>();
    }

    template<> void fill_pair<0>()
    {
        table[0].first = keys[0];
        table[0].second = vals[0];
    }
public:
    mapmaker( std::pair<KeyType, ValueType> (&t)[N], const KeyType (&k)[N], const ValueType (&v)[N] )
        : table(t), keys(k), vals(v)
    {
        fill_pair<N-1>();
    }
};

template<typename KeyType, typename ValueType, int N>
std::map<KeyType,ValueType> make_map(const KeyType (&keys)[N], const ValueType (&vals)[N])
{
    std::pair<KeyType, ValueType> table[N];
    mapmaker<KeyType, ValueType, N>( table, keys, vals );
    return std::map<KeyType, ValueType>(table, table+N);
}


#include <iostream>
#include <string>

int main()
{
    using namespace std;

    static const int keys[] = { 9, 2, 3, 0, 5 };
    static const string values[] = { "sheep" , "cow", "horse", "chicken", "dog" };
    static map<int, string> table ( make_map(keys, values) );

    map<int, string>::const_iterator iter;
    for( iter = table.begin(); iter != table.end(); ++iter )
        cout << iter->first << ' ' << iter->second << '\n';
}
summukhe 0 Newbie Poster

It does not work for me, I am using GCC 4.2.4 it is failing in compilation with error

error: template instantiation depth exceeds maximum of 200 (use -ftemplate-depth-NN to increase the maximum) instantiating ‘void MapMaker<KeyType, ValueType, N>::fill_pair() [with unsigned int pos = 4294967117u, KeyType = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, ValueType = unsigned char, unsigned int N = 20u]

I tried to use this piece of code where i want to instantiate a static map of size 20.

Bench 212 Posting Pro

Have you made any changes to the fill_pair() member function?

The mapmaker class relies on compile-time recursion to populate an array of "pairs". I wonder if any modifications you may have made to this have caused it to overflow (Judging by the compiler error, it looks like you may have removed the 'terminating' member function, which is fill_pair<0>() - this is the member function which halts the compile time recursion)

template<int pos> void fill_pair()
    {
        table[pos].first = keys[pos];
        table[pos].second = vals[pos];
        fill_pair<pos-1>();
    }

    template<> void fill_pair<0>()
    {
        table[0].first = keys[0];
        table[0].second = vals[0];
    }
chris.aaker 0 Newbie Poster

My first question is why is static used? My understanding is that static objects are instantiated once and persist untill the end of the program and that by default namespace variables inlcuding the global namespace are implicity static. This is some neat code and I'm trying to understand it.

vijayan121 1,152 Posting Virtuoso

> My first question is why is static used?
> My understanding is that static objects are instantiated once and persist untill the end of the program

In this particular case specifying a static storage duration does not make any perceptible difference.

int main()
{
    int a[] = { 7, 8, 9 } ; // automatic storage duration; a exists till main returns

    // static storage duration; b exists till end of program - ie. till main returns
    static int b[] = { 7, 8, 9 } ;

    // because b is a POD type placed in the SDA, and initialized with compile-time 
    // constants, it does not require dynamic initialization. a does.
}

In C++0x, we could just use its uniform initialization syntax via std::initializer_list<>.
With C++98, using Boost::Assign is equally nifty. http://www.boost.org/doc/libs/1_43_0/libs/assign/doc/index.html

#include <map>
#include <string>

#if __cplusplus > 199711L // C++0x
    #include <initializer_list>
#else // C++98
    #include <boost/assign/list_of.hpp>
#endif // C++0x

int main()
{
    #if __cplusplus > 199711L // C++0x

    std::map< int, std::string > map =
         { {9,"sheep"},{2,"cow"},{3,"horse"},{0,"chicken"},{5,"dog"} } ;

    #else // C++98

    std::map< int, std::string > map = boost::assign::map_list_of
                (9,"sheep")(2,"cow")(3,"horse")(0,"chicken")(5,"dog") ;

    #endif // C++0x
}
chris.aaker 0 Newbie Poster

Can I assume the same with const? Variables defined as const can not be changed later in the program. But because this is a snippet and a quick visual inspect shows that the variables are not changed, const does not have an effect on the program.

vijayan121 1,152 Posting Virtuoso

In this simple case, const doesn't have an effect on the program in terms of the code generated. The compiler can easily figure out that the variables are not changed.

However, it is still a good idea to always use 'const' where ever appropriate; it is just programming hygiene, and it's worth making it a habit.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.