How to build a compile-time key/value store?

痴心易碎 提交于 2019-12-03 02:21:10

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

Something like this would work:

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 is the default value, and a key of 1 gives a value of 3. Add additional specializations as needed.

Is this the general idea of what you're looking for? It's not the interface you requested, although preprocessor macros (such as Boost.Preprocessor) could streamline and simplify setting it up.

Essentially based on inheritance: Every map instantiation inherits the lookup types of its base class(es) (-> reduce problem) and defines a lookup for a key.

Edit: improved version base on n.m.'s ideas.

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

You can use template specialization

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

You can avail yourself of some macros to simplify the definition of content.

Similar question (though asked 2 years later), with my similar answer, but since here's no version for a 17 standard, here's mine:

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

Usage example:

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);

And a Bonus: Since this map is "generic" either key and value can be a tag of any type or value. Moreover, they may be any type at all. And various cexpr_pairs can be stored alltogether. You just have to somehow enforce it to have unique keys.

Usage example:

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;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!