1 // -*- C++ -*-
2 /* GG is a GUI for OpenGL.
3    Copyright (C) 2003-2008 T. Zachary Laine
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public License
7    as published by the Free Software Foundation; either version 2.1
8    of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA
19 
20    If you do not wish to comply with the terms of the LGPL please
21    contact the author as other terms are available for a fee.
22 
23    Zach Laine
24    whatwasthataddress@gmail.com */
25 
26 /** \file Enum.h \brief Contains the utility classes and macros that allow for
27     easy conversion to and from an enum value and its textual
28     representation. */
29 
30 #ifndef _GG_Enum_h_
31 #define _GG_Enum_h_
32 
33 #include <cstdlib>
34 #include <iostream>
35 #include <map>
36 #include <sstream>
37 #include <string>
38 #include <climits>
39 
40 #include <boost/algorithm/string/trim.hpp>
41 
42 namespace GG {
43 
44     #define GG_ENUM_NAME_BUFFER_SIZE 80
45 
46     /** This class is not meant for public consumption.
47       * Access this class through the functions generated
48       * in the GG_ENUM or GG_CLASS_ENUM macro invocation. */
49     template <typename EnumType>
50     class EnumMap {
51     public:
52         const std::string& operator[](EnumType value) const;
53         EnumType operator[](const std::string& name) const;
54 
55         void Insert(int& default_value, const std::string& entry);
56         size_t size() const;
57 
58         static const EnumType BAD_VALUE = (EnumType)INT_MIN;
59     private:
60         std::map<std::string, EnumType> m_name_to_value_map;
61         std::map<EnumType, std::string> m_value_to_name_map;
62     };
63 
64     /** Do not call this function directly.
65       * Instead, rely on the functions generated
66       * by the GG_ENUM or GG_CLASS_ENUM macro invocations. */
67     template <typename EnumType>
GetEnumMap()68     EnumMap<EnumType>& GetEnumMap() {
69         static EnumMap<EnumType> map;
70         return map;
71     }
72 
73     /** Do not call this function directly.
74       * Instead, rely on the functions generated
75       * by the GG_ENUM or GG_CLASS_ENUM macro invocations. */
76     template <typename EnumType>
BuildEnumMap(EnumMap<EnumType> & map,const std::string & enum_name,const char * comma_separated_names)77     void BuildEnumMap(EnumMap<EnumType>& map, const std::string& enum_name, const char* comma_separated_names) {
78         std::stringstream name_stream(comma_separated_names);
79 
80         int default_value = 0;
81         std::string name;
82         while (std::getline(name_stream, name, ',')) {
83             map.Insert(default_value, name);
84         }
85     }
86 
87 /** An enum macro for use inside classes.
88   * Enables << and >> for your enum,
89   * all of which will exist in whatever namespace this
90   * macro is used. */
91 #define GG_CLASS_ENUM(EnumName, ...)                                                    \
92     enum EnumName : int {                                                               \
93         __VA_ARGS__                                                                     \
94      };                                                                                 \
95                                                                                         \
96     friend inline std::istream& operator>>(std::istream& is, EnumName& value) {  \
97         ::GG::EnumMap<EnumName>& map = ::GG::GetEnumMap<EnumName>();                    \
98         if (map.size() == 0)                                                            \
99             ::GG::BuildEnumMap(map, #EnumName, #__VA_ARGS__);                           \
100                                                                                         \
101         std::string name;                                                               \
102         is >> name;                                                                     \
103         value = map[name];                                                              \
104         return is;                                                                      \
105     }                                                                                   \
106                                                                                         \
107     friend inline std::ostream& operator<<(std::ostream& os, EnumName value) {   \
108         ::GG::EnumMap<EnumName>& map = ::GG::GetEnumMap<EnumName>();                    \
109         if (map.size() == 0)                                                            \
110             ::GG::BuildEnumMap(map, #EnumName, #__VA_ARGS__);                           \
111                                                                                         \
112         const std::string& name = map[value];                                           \
113         return os << name;                                                              \
114     }                                                                                   \
115 
116 /** An enum macro for use outside of classes.
117   * Enables << and >> for your enum,
118   * all of which will exist in whatever namespace this
119   * macro is used. */
120 #define GG_ENUM(EnumName, ...)                                                          \
121     enum EnumName : int {                                                               \
122         __VA_ARGS__                                                                     \
123     };                                                                                  \
124                                                                                         \
125     inline std::istream& operator>>(std::istream& is, EnumName& value) {         \
126         ::GG::EnumMap<EnumName>& map = ::GG::GetEnumMap<EnumName>();                    \
127         if (map.size() == 0)                                                            \
128             ::GG::BuildEnumMap(map, #EnumName, #__VA_ARGS__);                           \
129                                                                                         \
130         std::string name;                                                               \
131         is >> name;                                                                     \
132         value = map[name];                                                              \
133         return is;                                                                      \
134     }                                                                                   \
135                                                                                         \
136     inline std::ostream& operator<<(std::ostream& os, EnumName value) {          \
137         ::GG::EnumMap<EnumName>& map = ::GG::GetEnumMap<EnumName>();                    \
138         if (map.size() == 0)                                                            \
139             ::GG::BuildEnumMap(map, #EnumName, #__VA_ARGS__);                           \
140                                                                                         \
141         const std::string& name = map[value];                                           \
142         return os << name;                                                              \
143     }                                                                                   \
144 
145       /////////////
146      // EnumMap //
147     /////////////
148     template <typename EnumType>
149     const std::string& EnumMap<EnumType>::operator[](EnumType value) const {
150         auto it = m_value_to_name_map.find(value);
151         if (it != m_value_to_name_map.end()) {
152             return it->second;
153         } else {
154             static std::string none("None");
155             return none;
156         }
157     }
158 
159     template <typename EnumType>
160     EnumType EnumMap<EnumType>::operator[](const std::string& name) const {
161         auto it = m_name_to_value_map.find(name);
162         if (it != m_name_to_value_map.end()) {
163             return it->second;
164         } else {
165             return BAD_VALUE;
166         }
167     }
168 
169     template <typename EnumType>
size()170     size_t EnumMap<EnumType>::size() const {
171         return m_name_to_value_map.size();
172     }
173 
174     template <typename EnumType>
Insert(int & default_value,const std::string & entry)175     void EnumMap<EnumType>::Insert(int& default_value, const std::string& entry) {
176         std::stringstream name_and_value(entry);
177 
178         std::string name;
179         std::getline(name_and_value, name, '=');
180 
181         std::string value_str;
182         EnumType value;
183         if (std::getline(name_and_value, value_str)) {
184             value = (EnumType)strtol(value_str.c_str(), nullptr, 0);
185         }
186         else {
187             value = (EnumType)default_value;
188         }
189 
190         boost::trim(name);
191 
192         m_name_to_value_map[name] = value;
193         m_value_to_name_map[value] = name;
194         default_value = value + 1;
195     }
196 
197 } // namespace GG
198 
199 #endif
200