Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have a problem where I need to map an integer at compile time to another integer. Basically, I need the compile-time equivalent of std::map<int,int>. If a key is not found in the map, I'd like to return a default value.

The interface I'd like to use:

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 returns the value associated with key in the StaticMap. If key is not found, then default_value is returned.

In general, the number of key/value pairs will be bounded by some limit > 2. What is the best way to build static_map and lookup?

I should also mention that I'm limited to using C++03 language constructs, so no C++11, and no external library dependencies.


Here's the solution I arrived at, inspired by n.m. and DyP's answers below:

#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;
}
like image 238
Jared Hoberock Avatar asked May 10 '13 21:05

Jared Hoberock


People also ask

What is time based key value store?

Time Based Key-Value Store Design a time-based key-value data structure that can store multiple values for the same key at different time stamps and retrieve the key's value at a certain timestamp. Implement the TimeMap class: TimeMap () Initializes the object of the data structure.

How to implement time-based key-value data structure?

Design a time-based key-value data structure that can store multiple values for the same key at different time stamps and retrieve the key's value at a certain timestamp. Implement the TimeMap class: TimeMap () Initializes the object of the data structure.

What is a key-value store in DBMS?

A key-value store is a very power technique that is used in almost every system in the world. It can be as simple as a hash table and at the same time, it can also be a distributed storage system. For instance, the underline system of Cassandra is a key-value storage system and Cassandra is widely used in many companies like Apple, Facebook etc..

What is the best way to store a key-value pair?

The most straightforward thing is to use a hash table to store key-value pairs, which is how most this kind of systems work nowadays. A hash table allows you to read/write a key-value pair in constant time and it’s extremely easy to use.


4 Answers

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;
}
like image 105
n. 1.8e9-where's-my-share m. Avatar answered Oct 21 '22 03:10

n. 1.8e9-where's-my-share m.


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.

like image 8
Josh Kelley Avatar answered Oct 21 '22 04:10

Josh Kelley


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.

like image 7
jdavidls Avatar answered Oct 21 '22 05:10

jdavidls


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;
}
like image 3
dyp Avatar answered Oct 21 '22 03:10

dyp