strenum2.cpp

(plain text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// How to hax0r a switch statement to work with strings.
// Public domain. No warranty. Go nuts.

//////////////////////////////////////////////////////////////////////

#include <algorithm>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <cstddef>

namespace enum_utils {
  template <typename F>
  const char *find (typename F::enum_type value) {
    typename F::range_type range;
    for (range = F::get_table(); range.first != range.second; ++range.first) {
      if (range.first->second == value) break;
    }
    return (range.first != range.second) ? range.first->first : 0;
  }

  template <typename F>
  typename F::enum_type
  find (const std::string &name) {
    typename F::range_type range;
    for (range = F::get_table(); range.first != range.second; ++range.first) {
      if (range.first->first == name) break;
    }
    return (range.first != range.second
	    ? range.first->second
	    : typename F::enum_type());
  }

  template <typename T, std::size_t N>
  std::size_t n_elements (const T (&) [N]) { return N; }
}

#define ELOI_COMMA ,
#define ELOI_SEMI ;
#define ELOI_MKENUM_ELMT(name, value) name value
#define ELOI_MKTABLE_ELMT(name, value) \
  std::make_pair(static_cast<const char *>(#name), name)

#define ELOI_MKENUM(enum_name, enum_gen) \
  enum enum_name { enum_gen(ELOI_MKENUM_ELMT, ELOI_COMMA) }

#define ELOI_MKTABLE(enum_name, enum_gen)                         \
  struct enum_name##_table {                                      \
    typedef enum_name enum_type;                                  \
    typedef const std::pair<const char *, enum_type> item_type;   \
    typedef std::pair<item_type *, item_type *> range_type;       \
    static range_type get_table () {                              \
      static const std::pair<const char *, enum_name> table[] = { \
        enum_gen(ELOI_MKTABLE_ELMT, ELOI_COMMA)                   \
      };                                                          \
      return range_type(table,                                    \
			table + enum_utils::n_elements(table));   \
    }	                                                          \
  };

#define ELOI_MKFIND(enum_name, enum_gen)                 \
  enum_name find_##enum_name (const std::string &name) { \
    return enum_utils::find<enum_name##_table>(name);    \
  }                                                      \
  const char *find_##enum_name (enum_name value) {       \
    return enum_utils::find<enum_name##_table>(value);   \
  }

#define ELOI_MKSTRMAP(enum_name, enum_gen) \
  ELOI_MKENUM(enum_name, enum_gen);        \
  ELOI_MKTABLE(enum_name, enum_gen);       \
  ELOI_MKFIND(enum_name, enum_gen);        \
  enum { }

//////////////////////////////////////////////////////////////////////

#define STRMAP_GENERATOR(F, sep) \
  F(none, = 0) sep \
  F(foo,  = 1) sep \
  F(bar,  = 2) sep \
  F(baz,  = 4)

namespace strmap {
  ELOI_MKSTRMAP(values, STRMAP_GENERATOR);
}

//////////////////////////////////////////////////////////////////////

#include <iostream>

int main (int argc, char *argv[]) {
  std::string s;
  std::cin >> s;

  switch (strmap::find_values(s)) {
  case strmap::foo:
    std::cout << "This is the foo code!" << std::endl;
    break;

  case strmap::bar:
    std::cout << "This is the bar code!" << std::endl;
    break;

  case strmap::baz:
    std::cout << "This is the baz code!" << std::endl;
    break;

  default:
    {
      std::istringstream str(s);
      int n;
      if (str >> n) {
	if (const char *name = strmap::find_values(strmap::values(n))) {
	  std::cout << "This is the " << name << " code!\n";
	  break;
	}
      }

      std::cerr << "What?" << std::endl;
      break;
    }
  }
}