The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
make_enum.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2016 by Chris Beck <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Defines the MAKE_ENUM macro.
18  *
19  * Currently this comes in 1-argument and 2-argument versions.
20  *
21  * <b>Example usage:</b>
22  *
23  * @code
24  * #include "utils/make_enum.hpp"
25  *
26  * MAKE_ENUM(enumname,
27  * (val1, "name1")
28  * (val2, "name2")
29  * (val3, "name3")
30  * )
31  * @endcode
32  *
33  * <b>What it does:</b>
34  *
35  * Generates a struct @a enumname which holds an enum and provides functions to
36  * convert to and from string, as well as a constant @a enumname::count, which
37  * is set to the number of possible enum values defined.
38  *
39  * @code
40  * // Throws bad_enum_cast if the string value is not recognized.
41  * enumname enumname::string_to_enum(const std::string&);
42  *
43  * // Returns the specified default instead of throwing if the string value is
44  * // not recognized. Never throws.
45  * enumname enumname::string_to_enum(const std::string&, enumname);
46  *
47  * // Never throws.
48  * std::string enumname::enum_to_string(enumname);
49  *
50  * // Count of defined enum values.
51  * const size_t enumname::count;
52  * @endcode
53  *
54  * It also defines the following stream operations:
55  *
56  * @code
57  * // Never throws. Fails an assertion check if the value passed is not defined
58  * // by the enumeration.
59  * std::ostream& operator<<(std::ostream&, enumname);
60  *
61  * // Never throws. Sets the stream's fail bit if the input is not recognized.
62  * std::istream& operator>>(std::istream&, enumname&);
63  * @endcode
64  *
65  * In case of a bad string -> enum conversion from istream input, the istream
66  * will have its fail bit set. This means that lexical_casts will throw a
67  * bad_lexical_cast in the similar scenario; however, note that
68  * bad_lexical_cast does not provide any details about the error.
69  *
70  * It is recommended to use MAKE_ENUM types with the built-in versions of
71  * lexical_cast or lexical_cast_default provided by Wesnoth (see util.hpp).
72  * However, if you do <b>not</b> want twml_exception to be thrown under any
73  * circumstances, use the string_to_enumname functions instead.
74  *
75  * See src/tests/test_make_enum.cpp for example code.
76  */
77 
78 #ifndef MAKE_ENUM_HPP
79 #define MAKE_ENUM_HPP
80 
81 #include <cassert>
82 #include <exception>
83 #include <string>
84 
85 #include <boost/preprocessor/cat.hpp>
86 #include <boost/preprocessor/seq/for_each.hpp>
87 #include <boost/preprocessor.hpp>
88 
89 #include <istream>
90 #include <ostream>
91 
92 class bad_enum_cast : public std::exception
93 {
94 public:
95  bad_enum_cast(const std::string& enumname, const std::string& str)
96  : message("Failed to convert string \"" + str + "\" to type " + enumname)
97  , name(enumname)
98  , bad_val(str)
99  {}
100 
101  virtual ~bad_enum_cast() throw() {}
102 
103  const char * what() const throw()
104  {
105  return message.c_str();
106  }
107 
108  const char * type() const throw()
109  {
110  return name.c_str();
111  }
112 
113  const char * value() const throw()
114  {
115  return bad_val.c_str();
116  }
117 
118 private:
120 };
121 
122 namespace make_enum_detail
123 {
124  void debug_conversion_error(const std::string& tmp, const bad_enum_cast & e);
125 }
126 
127 
128 #define ADD_PAREN_1( A, B ) ((A, B)) ADD_PAREN_2
129 #define ADD_PAREN_2( A, B ) ((A, B)) ADD_PAREN_1
130 #define ADD_PAREN_1_END
131 #define ADD_PAREN_2_END
132 #define MAKEPAIRS( INPUT ) BOOST_PP_CAT(ADD_PAREN_1 INPUT,_END)
133 #define PP_SEQ_FOR_EACH_I_PAIR(macro, data, pairs) BOOST_PP_SEQ_FOR_EACH_I(macro, data, MAKEPAIRS(pairs))
134 
135 
136 #define CAT2( A, B ) A ## B
137 #define CAT3( A, B, C ) A ## B ## C
138 
139 
140 #define EXPAND_ENUMVALUE_NORMAL(r, data, i, record) \
141  BOOST_PP_TUPLE_ELEM(2, 0, record) = i,
142 
143 
144 #define EXPAND_ENUMFUNC_NORMAL(r, data, i, record) \
145  if(data == BOOST_PP_TUPLE_ELEM(2, 1, record)) return BOOST_PP_TUPLE_ELEM(2, 0, record);
146 #define EXPAND_ENUMPARSE_NORMAL(r, data, i, record) \
147  if(data == BOOST_PP_TUPLE_ELEM(2, 1, record)) { *this = BOOST_PP_TUPLE_ELEM(2, 0, record); return true; }
148 #define EXPAND_ENUMFUNCREV_NORMAL(r, data, i, record) \
149  if(data == BOOST_PP_TUPLE_ELEM(2, 0, record)) return BOOST_PP_TUPLE_ELEM(2, 1, record);
150 
151 #define EXPAND_ENUMFUNCTIONCOUNT(r, data, i, record) \
152  + 1
153 
154 class enum_tag
155 {
156 };
157 #define MAKE_ENUM(NAME, CONTENT) \
158 struct NAME : public enum_tag \
159 { \
160  enum type \
161  { \
162  PP_SEQ_FOR_EACH_I_PAIR(EXPAND_ENUMVALUE_NORMAL, ,CONTENT) \
163  }; \
164  type v; \
165  NAME(type v) : v(v) {} \
166  /*We don't want a default contructor but we need one in order to make lexical_cast working*/ \
167  NAME() : v() {} \
168  /*operator type() const { return v; } */\
169  static NAME string_to_enum (const std::string& str, NAME def) \
170  { \
171  PP_SEQ_FOR_EACH_I_PAIR(EXPAND_ENUMFUNC_NORMAL, str , CONTENT) \
172  return def; \
173  } \
174  static NAME string_to_enum (const std::string& str) \
175  { \
176  PP_SEQ_FOR_EACH_I_PAIR(EXPAND_ENUMFUNC_NORMAL, str , CONTENT) \
177  throw bad_enum_cast( #NAME , str); \
178  } \
179  /** Sets *this to the value given in str, does nothing if str was not valid enum value. */ \
180  /** @returns true iff @a str was a valid enum value. */ \
181  /** @param TStr a std::string or a string_span (from the wesnothd code). */ \
182  template<typename TStr> \
183  bool parse (const TStr& str) \
184  { \
185  PP_SEQ_FOR_EACH_I_PAIR(EXPAND_ENUMPARSE_NORMAL, str , CONTENT) \
186  return false; \
187  } \
188  /* for const char* parameters we cannot use the template above because it would only compare the pointer. */ \
189  bool parse (const char* str) \
190  { \
191  return parse(std::string(str)); \
192  } \
193  static std::string name() \
194  { \
195  return #NAME; \
196  } \
197  static std::string enum_to_string (NAME val) \
198  { \
199  PP_SEQ_FOR_EACH_I_PAIR(EXPAND_ENUMFUNCREV_NORMAL, val.v , CONTENT) \
200  assert(false && "Corrupted enum found with identifier NAME"); \
201  throw "assertion ignored"; \
202  } \
203  static const char* enum_to_cstring (NAME val) \
204  { \
205  PP_SEQ_FOR_EACH_I_PAIR(EXPAND_ENUMFUNCREV_NORMAL, val.v , CONTENT) \
206  assert(false && "Corrupted enum found with identifier NAME"); \
207  throw "assertion ignored"; \
208  } \
209  std::string to_string () const \
210  { \
211  return enum_to_string(*this); \
212  } \
213  const char* to_cstring () const \
214  { \
215  return enum_to_cstring(*this); \
216  } \
217  friend std::ostream& operator<< (std::ostream & os, NAME val) \
218  { \
219  os << enum_to_string(val); \
220  return os; \
221  } \
222  friend std::ostream& operator<< (std::ostream & os, NAME::type val) \
223  { \
224  return (os << NAME(val)); \
225  } \
226  friend std::istream& operator>> (std::istream & is, NAME& val) \
227  { \
228  std::istream::sentry s(is, true); \
229  if(!s) return is; \
230  std::string temp; \
231  is >> temp; \
232  try { \
233  val = string_to_enum(temp); \
234  } catch (const bad_enum_cast & /*e*/) {\
235  is.setstate(std::ios::failbit); \
236  /*make_enum_detail::debug_conversion_error(temp, e); */\
237  } \
238  return is; \
239  } \
240  friend std::istream& operator>> (std::istream & os, NAME::type& val) \
241  { \
242  return (os >> reinterpret_cast< NAME &>(val)); \
243  } \
244  friend bool operator==(NAME v1, NAME v2) \
245  { \
246  return v1.v == v2.v; \
247  } \
248  friend bool operator==(NAME::type v1, NAME v2) \
249  { \
250  return v1 == v2.v; \
251  } \
252  friend bool operator==(NAME v1, NAME::type v2) \
253  { \
254  return v1.v == v2; \
255  } \
256  friend bool operator!=(NAME v1, NAME v2) \
257  { \
258  return v1.v != v2.v; \
259  } \
260  friend bool operator!=(NAME::type v1, NAME v2) \
261  { \
262  return v1 != v2.v; \
263  } \
264  friend bool operator!=(NAME v1, NAME::type v2) \
265  { \
266  return v1.v != v2; \
267  } \
268  /* operator< is used for by std::map and similar */ \
269  friend bool operator<(NAME v1, NAME v2) \
270  { \
271  return v1.v < v2.v; \
272  } \
273  template<typename T> \
274  T cast() \
275  { \
276  return static_cast<T>(v); \
277  } \
278  static NAME from_int(int i) \
279  { \
280  return static_cast<type>(i); \
281  } \
282  static const size_t count = 0 PP_SEQ_FOR_EACH_I_PAIR(EXPAND_ENUMFUNCTIONCOUNT, , CONTENT);\
283  bool valid() \
284  { \
285  return cast<size_t>() < count; \
286  } \
287 private: \
288  /*prevent automatic conversion for any other built-in types such as bool, int, etc*/ \
289  /*template<typename T> \
290  operator T () const; */\
291  /* For some unknown reason the following version doesnt compile: */ \
292  /* template<typename T, typename = typename boost::enable_if<Cond>::type> \
293  operator T(); */\
294 };
295 
296 
297 #endif
const char * type() const
Definition: make_enum.hpp:108
std::string bad_val
Definition: make_enum.hpp:119
const char * what() const
Definition: make_enum.hpp:103
const char * value() const
Definition: make_enum.hpp:113
std::string message
Definition: make_enum.hpp:119
virtual ~bad_enum_cast()
Definition: make_enum.hpp:101
GLuint const GLchar * name
Definition: glew.h:1782
GLsizei GLenum GLuint GLuint GLsizei char * message
Definition: glew.h:2499
void debug_conversion_error(const std::string &temp, const bad_enum_cast &e)
Definition: make_enum.cpp:7
#define e
GLsizei const GLcharARB ** string
Definition: glew.h:4503
std::string name
Definition: make_enum.hpp:119
bad_enum_cast(const std::string &enumname, const std::string &str)
Definition: make_enum.hpp:95