如何构建编译时键/值存储?

时间:2013-05-10 21:06:20

标签: c++ template-meta-programming

我有一个问题,我需要在编译时将一个整数映射到另一个整数。基本上,我需要编译时等效于std::map<int,int>。如果在地图中找不到某个键,我想返回一个默认值。

我想使用的界面:

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会在key中返回与StaticMap相关联的值。如果找不到key,则会返回default_value

通常,键/值对的数量将受某些限制的限制&gt; 2.构建static_maplookup的最佳方式是什么?

我还应该提到我仅限于使用C ++ 03语言结构,因此没有C ++ 11,也没有外部库依赖。


这是我到达的解决方案,灵感来自于上午。和DyP的答案如下:

#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;
}

5 个答案:

答案 0 :(得分:13)

在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;
}

答案 1 :(得分:5)

这样的事情会起作用:

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是默认值,键1表示值3.根据需要添加其他特化。

这是您正在寻找的一般想法吗?虽然预处理器宏(例如Boost.Preprocessor)可以简化并简化设置,但它不是您请求的接口。

答案 2 :(得分:2)

基本上基于继承:每个映射实例都继承其基类的查找类型( - &gt; reduce problem)并定义键的查找。

编辑:改进版本基于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;
}

答案 3 :(得分:1)

您可以使用模板专业化

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

您可以利用一些宏来简化内容的定义。

答案 4 :(得分:0)

Similar question(虽然在2年后问过),我的回答与此类似,但是由于这不是17标准的版本,因此是我的:

#include <type_traits>
#include <tuple>

//tag for typenames
template <class T>
struct tag_type
{
    using type = T;
};

//tag for autos
template <auto val>
struct tag_auto
{
    constexpr static decltype(val) value = val;
};

//generic pair
template <typename key_tag, typename val_tag>
struct cexpr_pair
{
    using key = key_tag;
    using value = val_tag;
};

template <class ... cexpr_pairs>
class cexpr_generic_map
{
    template <typename cexpr_tag_key, size_t iter = 0>
    constexpr static auto Find()
    {
        //failed to find by key
        if constexpr (iter == sizeof...(cexpr_pairs))
            //you can substitute void with "tag_auto<default_value>"
            return cexpr_pair<cexpr_tag_key, void>();
        else
        {
            typedef std::tuple_element_t<iter, std::tuple<cexpr_pairs...>> cur_pair;
            if constexpr (std::is_same_v<cexpr_tag_key, cur_pair::key>)
                return cur_pair();
            else 
                return Find<cexpr_tag_key, iter + 1>();
        }
    }

public:

    template <typename tag_key>
    using found_pair = decltype(Find<tag_key>());
};

用法示例:

typedef cexpr_generic_map<
cexpr_pair<tag_auto<0>, tag_auto<4>>,
cexpr_pair<tag_auto<1>, tag_auto<8>>,
cexpr_pair<tag_auto<2>, tag_auto<15>>,
cexpr_pair<tag_auto<4>, tag_auto<0>>,
cexpr_pair<tag_auto<8>, tag_auto<1>>,
cexpr_pair<tag_auto<15>, tag_auto<2>>
> map_inverse;



static_assert(map_inverse::found_pair<tag_auto<0>>::value::value == 4);
static_assert(map_inverse::found_pair<tag_auto<1>>::value::value == 8);
static_assert(map_inverse::found_pair<tag_auto<2>>::value::value == 15);

static_assert(map_inverse::found_pair<tag_auto<4>>::value::value == 0);
static_assert(map_inverse::found_pair<tag_auto<8>>::value::value == 1);
static_assert(map_inverse::found_pair<tag_auto<15>>::value::value == 2);

奖金: 由于此映射是“通用”映射,因此键和值都可以是任何类型或值的标签。而且,它们可以是任何类型。并且各种cexpr_pairs可以一起存储。您只需要以某种方式强制它具有唯一的密钥即可。

用法示例:

struct abc
{
    void func1(int i, double d)
    {
        std::cout << i << '\n' << d << '\n';
    }

    int func2()
    {
        return 9;
    }
};

typedef cexpr_pair<tag_auto<1>, tag_auto<&abc::func1>> pair_func1;
typedef cexpr_pair<tag_auto<2>, tag_auto<&abc::func2>> pair_func2;
typedef cexpr_pair<tag_type<int>, tag_auto<&abc::func2>> int_func2;
typedef cexpr_pair<abc, tag_auto<18>> custom1;
typedef cexpr_pair<int, abc> custom2;

typedef cexpr_generic_map<pair_func1, pair_func2, int_func2, custom1, custom2> map_funcs;

    int main()
{
    abc a;
    (a.*map_funcs::found_pair<tag_auto<1>>::value::value)(6, 3.28);                         // 6 3.28

    std::cout << (a.*map_funcs::found_pair<tag_auto<2>>::value::value)() << std::endl;      // 9
    std::cout << (a.*map_funcs::found_pair<tag_type<int>>::value::value)() << std::endl;    // 9 
    std::cout << map_funcs::found_pair<abc>::value::value << std::endl;                     // 18
    map_funcs::found_pair<int>::value().func1(4, 8.15162342);                               // 4 8.15162342

    std::cin.get();
    return 0;