2013-05-10 7 views
12

Mam problem, w którym muszę mapować liczbę całkowitą w czasie kompilacji na inną liczbę całkowitą. Zasadniczo potrzebuję ekwiwalentu czasu kompilacji std::map<int,int>. Jeśli klucz nie znajduje się na mapie, chciałbym zwrócić wartość domyślną.Jak zbudować składnicę klucz/wartość w czasie kompilacji?

Interfejs Chciałbym użyć:

template<unsigned int default_value, 
     unsigned int key0, unsigned int value0, 
     unsigned int key1, unsigned int value1, 
     ...> 
struct static_map 
{ 
    ... 
}; 

template<unsigned int key, typename StaticMap> 
struct lookup 
{ 
    static unsigned int value = ... 
}; 

lookup zwraca wartość związaną z key w StaticMap. Jeśli nie zostanie znalezione key, zwracana jest nazwa default_value.

Ogólnie liczba par klucz/wartość będzie ograniczona przez pewien limit> 2. Jaki jest najlepszy sposób na zbudowanie static_map i lookup?

Należy również wspomnieć, że jestem ograniczony do używania konstrukcji językowych C++ 03, więc nie ma C++ 11 i nie ma zależności zewnętrznych bibliotek.


Oto rozwiązanie, do którego doszedłem inspirowane n.m. i odpowiedzi DYP jest poniżej:

#include <iostream> 

template<unsigned int k, unsigned int v> 
struct key_value 
{ 
    static const unsigned int key = k; 
    static const unsigned int value = v; 
}; 


template<typename Head, typename Tail = void> 
struct cons 
{ 
    template<unsigned int key, unsigned int default_value> 
    struct get 
    { 
    static const unsigned int value = (key == Head::key) ? (Head::value) : Tail::template get<key,default_value>::value; 
    }; 
}; 


template<typename Head> 
struct cons<Head,void> 
{ 
    template<unsigned int key, unsigned int default_value> 
    struct get 
    { 
    static const unsigned int value = (key == Head::key) ? (Head::value) : default_value; 
    }; 
}; 


template<unsigned int default_value, 
     unsigned int key0, unsigned int value0, 
     unsigned int key1, unsigned int value1, 
     unsigned int key2, unsigned int value2, 
     unsigned int key3, unsigned int value3, 
     unsigned int key4, unsigned int value4, 
     unsigned int key5, unsigned int value5, 
     unsigned int key6, unsigned int value6, 
     unsigned int key7, unsigned int value7> 
struct static_map 
{ 
    template<unsigned int key> 
    struct get 
    { 
    typedef cons< 
     key_value<key0,value0>, 
     cons< 
     key_value<key1,value1>, 
     cons< 
      key_value<key2,value2>, 
      cons< 
      key_value<key3,value3>, 
      cons< 
       key_value<key4,value4>, 
       cons< 
       key_value<key5,value5>, 
       cons< 
        key_value<key6,value6>, 
        cons< 
        key_value<key7,value7> 
        > 
       > 
       > 
      > 
      > 
     > 
     > 
    > impl; 

    static const unsigned int value = impl::template get<key,default_value>::value; 
    }; 
}; 


template<unsigned int key, typename StaticMap> 
struct lookup 
{ 
    static const unsigned int value = StaticMap::template get<key>::value; 
}; 


int main() 
{ 
    typedef static_map<13, 
        0, 0, 
        1, 10, 
        2, 20, 
        3, 30, 
        4, 40, 
        5, 50, 
        6, 60, 
        7, 70 
    > my_static_map; 

    std::cout << "0 maps to " << lookup<0, my_static_map>::value << std::endl; 
    std::cout << "1 maps to " << lookup<1, my_static_map>::value << std::endl; 
    std::cout << "2 maps to " << lookup<2, my_static_map>::value << std::endl; 
    std::cout << "3 maps to " << lookup<3, my_static_map>::value << std::endl; 
    std::cout << "4 maps to " << lookup<4, my_static_map>::value << std::endl; 
    std::cout << "5 maps to " << lookup<5, my_static_map>::value << std::endl; 
    std::cout << "6 maps to " << lookup<6, my_static_map>::value << std::endl; 
    std::cout << "7 maps to " << lookup<7, my_static_map>::value << std::endl; 
    std::cout << "100 maps to " << lookup<100, my_static_map>::value << std::endl; 

    return 0; 
} 
+0

możliwe duplikat [Inicjowanie statyczne std :: map w C++] (http://stackoverflow.com/questions/138600/initializing-a-static-stdmapint-int-in-c) – user93353

+5

@ user93353: to pytanie nie jest związane z pytaniem, które połączyłeś. –

+0

@DyP: Tak, potrzebuję czegoś w rodzaju mapy MPL doładowania. Niestety, nie mogę polegać na MPL, więc chciałbym zrozumieć implementację. –

Odpowiedz

11

w C++ 11:

template <int kk, int vv> 
struct kv 
{ 
    static const int k = kk, v = vv; 
}; 

template <int dflt, typename...> 
struct ct_map; 

template <int dflt> 
struct ct_map<dflt> 
{ 
    template<int> 
    struct get 
    { 
     static const int val = dflt; 
    }; 
}; 

template<int dflt, int k, int v, typename... rest> 
struct ct_map<dflt, kv<k, v>, rest...> 
{ 
    template<int kk> 
    struct get 
    { 
     static const int val = 
      (kk == k) ? 
      v : 
      ct_map<dflt, rest...>::template get<kk>::val; 
    }; 
}; 

typedef ct_map<42, kv<10, 20>, kv<11, 21>, kv<23, 7>> mymap; 

#include <iostream> 
int main() 
{ 
    std::cout << mymap::get<10>::val << std::endl; 
    std::cout << mymap::get<11>::val << std::endl; 
    std::cout << mymap::get<23>::val << std::endl; 
    std::cout << mymap::get<33>::val << std::endl; 
} 
+0

Czy nie jest wymagana definicja 'kv :: k' i' kv :: v' (poza klasą)? – dyp

+0

@DyP: nie, ponieważ nie są używane odr. –

+0

Jak można rzucić błąd podczas kompilacji, jeśli klucz nie zostanie znaleziony? – karliwson

3

Coś jak to będzie działać:

template<int Key> 
struct StaticMap { 
    static const int Value = 0; 
}; 

template<> 
struct StaticMap<1> { 
    static const int Value = 3; 
}; 

int main() 
{ 
    cout << StaticMap<0>::Value << ", " 
     << StaticMap<1>::Value << ", " 
     << StaticMap<2>::Value << endl; 
} 

0 jest wartością domyślną, a klucz od 1 daje wartość 3. Dodać dodatkowe specjalizacje w zależności od potrzeb.

Czy to ogólna koncepcja tego, czego szukasz? Nie jest to interfejs, o który prosiłeś, chociaż makra preprocesorów (takie jak Boost.Preprocessor) mogą usprawnić i uprościć konfigurację.

1

Można użyć szablonu specjalizacji

template <char key> 
struct Map; 

template <char key> 
struct Map { static const int value = -1; }; // not exists node 

template <> struct Map< 'A' > { static const int value = 1; }; // 'A' -> 1 
template <> struct Map< 'B' > { static const int value = 2; }; // 'B' -> 2 
// .... 

int lookup = Map<'B'>::value; // = 2 

Można skorzystania z niektórych makr w celu uproszczenia definicji treści.

2

Zasadniczo na podstawie dziedziczenia: Każda instancja odwzorowania dziedziczy typy odnośników swojej klasy bazowej (-> zmniejszanie problemu) i definiuje wyszukiwanie klucza.

Edycja: ulepszona wersja bazowa pomysłów na n.m.

#include <iostream> 
#include <cstddef> 

template < int t_key, int t_value > 
struct ct_int_pair 
{ 
    enum { key = t_key, value = t_value }; 
}; 

struct dummy; 

template < int default_value, 
      typename key_value_pair0, 
      typename key_value_pair1 = dummy, 
      typename key_value_pair2 = dummy > 
struct ct_map 
    : ct_map < default_value, key_value_pair1, key_value_pair2, dummy > 
{ 
    typedef ct_map < default_value, key_value_pair1, key_value_pair2, dummy > base; 

    // DUMMY required for partial specialization 
    template < int key, class DUMMY = dummy > 
    struct lookup 
    { 
     enum { value = base::template lookup <key> :: value }; 
    }; 
     template < class DUMMY > 
     struct lookup < key_value_pair0::key, DUMMY > 
     { 
      enum { value = key_value_pair0::value }; 
     }; 
}; 

    template < int default_value > 
    struct ct_map < default_value, dummy, dummy, dummy > 
    { 
     template < int key > 
     struct lookup 
     { 
      enum { value = default_value }; 
     }; 
    }; 


template < int key, typename StaticMap > 
struct lookup 
{ 
    enum { value = StaticMap :: template lookup <key> :: value }; 
}; 


// example 

typedef ct_map < -1, 
       ct_int_pair<21, 42>, 
       ct_int_pair<10, 15> > my_map; 

enum 
{ 
    value0 = lookup<21, my_map>::value 
    , value1 = lookup<10, my_map>::value 
    , value2 = lookup<100, my_map>::value 
}; 

int main() 
{ 
    std::cout << value0 << " : " << value1 << " : " << value2 << std::endl; 
}