GG
Flags.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 /* GG is a GUI for SDL and OpenGL.
3  Copyright (C) 2007 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 
29 #ifndef _GG_Flags_h_
30 #define _GG_Flags_h_
31 
32 #include <GG/Exception.h>
33 
34 #include <boost/utility/enable_if.hpp>
35 #include <boost/lexical_cast.hpp>
36 #include <boost/mpl/assert.hpp>
37 
38 #include <cassert>
39 #include <iosfwd>
40 #include <map>
41 #include <set>
42 
43 
44 namespace GG {
45 
46 namespace detail {
47  inline std::size_t OneBits(unsigned int num)
48  {
49  std::size_t retval = 0;
50  const std::size_t NUM_BITS = sizeof(num) * 8;
51  for (std::size_t i = 0; i < NUM_BITS; ++i) {
52  if (num & 1)
53  ++retval;
54  num >>= 1;
55  }
56  return retval;
57  }
58 }
59 
60 
63 template <class T>
64 struct is_flag_type : boost::mpl::false_ {};
65 
66 
74 #define GG_FLAG_TYPE(name) \
75  class name; \
76  \
77  template <> \
78  struct is_flag_type<name> : boost::mpl::true_ {}; \
79  \
80  class name \
81  { \
82  public: \
83  name() : m_value(0) {} \
84  explicit name(unsigned int value) : \
85  m_value(value) \
86  { \
87  if (1u < detail::OneBits(value)) \
88  throw std::invalid_argument( \
89  "Non-bitflag passed to " #name " constructor"); \
90  } \
91  bool operator==(name rhs) const \
92  { return m_value == rhs.m_value; } \
93  bool operator!=(name rhs) const \
94  { return m_value != rhs.m_value; } \
95  bool operator<(name rhs) const \
96  { return m_value < rhs.m_value; } \
97  private: \
98  unsigned int m_value; \
99  friend class Flags<name>; \
100  }; \
101  \
102  template <> \
103  FlagSpec<name>& FlagSpec<name>::instance(); \
104  \
105  inline std::ostream& operator<<(std::ostream& os, name n) \
106  { \
107  os << FlagSpec<name>::instance().ToString(n); \
108  return os; \
109  } \
110  \
111  inline std::istream& operator>>(std::istream& is, name& n) \
112  { \
113  std::string str; \
114  is >> str; \
115  n = FlagSpec<name>::instance().FromString(str); \
116  return is; \
117  }
118 
119 
122 #define GG_FLAGSPEC_IMPL(name) \
123  template <> \
124  FlagSpec<name>& FlagSpec<name>::instance() \
125  { \
126  static FlagSpec retval; \
127  return retval; \
128  }
129 
130 
148 template <class FlagType>
149 class GG_API FlagSpec
150 {
151 public:
152  // If you have received an error message directing you to the line below,
153  // it means you probably have tried to use this class with a FlagsType
154  // that is not a type generated by GG_FLAG_TYPE. Use that to generate new
155  // flag types.
156  BOOST_MPL_ASSERT((is_flag_type<FlagType>));
157 
159  typedef typename std::set<FlagType>::iterator iterator;
161  typedef typename std::set<FlagType>::const_iterator const_iterator;
162 
164 
165  GG_ABSTRACT_EXCEPTION(Exception);
166 
168  GG_CONCRETE_EXCEPTION(UnknownFlag, GG::FlagSpec, Exception);
169 
171  GG_CONCRETE_EXCEPTION(UnknownString, GG::FlagSpec, Exception);
173 
175  static FlagSpec& instance();
176 
178 
179  bool contains(FlagType flag) const
180  { return find(flag) != end(); }
183  bool permanent(FlagType flag) const
184  { return m_permanent.find(flag) != m_permanent.end(); }
187  const_iterator find(FlagType flag) const
188  { return m_flags.find(flag); }
190  const_iterator begin() const
191  { return m_flags.begin(); }
193  const_iterator end() const
194  { return m_flags.end(); }
198  const std::string& ToString(FlagType flag) const
199  {
200  typename std::map<FlagType, std::string>::const_iterator it = m_strings.find(flag);
201  if (it == m_strings.end())
202  throw UnknownFlag("Could not find string corresponding to unknown flag");
203  return it->second;
204  }
207  FlagType FromString(const std::string& str) const
208  {
209  for (typename std::map<FlagType, std::string>::const_iterator it = m_strings.begin();
210  it != m_strings.end();
211  ++it) {
212  if (it->second == str)
213  return it->first;
214  }
215  throw UnknownString("Could not find flag corresponding to unknown string");
216  return FlagType(0);
217  }
219 
221 
225  void insert(FlagType flag, const std::string& name, bool permanent = false)
226  {
227 #ifndef NDEBUG
228  bool insert_successful =
229 #endif
230  m_flags.insert(flag).second;
231  assert(insert_successful);
232  if (permanent)
233  m_permanent.insert(flag);
234  m_strings[flag] = name;
235  }
241  bool erase(FlagType flag)
242  {
243  bool retval = true;
244  if (permanent(flag)) {
245  retval = false;
246  } else {
247  m_flags.erase(flag);
248  m_permanent.erase(flag);
249  m_strings.erase(flag);
250  }
251  return retval;
252  }
254 
255 private:
256  FlagSpec() {}
257 
258  std::set<FlagType> m_flags;
259  std::set<FlagType> m_permanent;
260  std::map<FlagType, std::string> m_strings;
261 };
262 
263 
264 template <class FlagType>
265 class Flags;
266 
267 template <class FlagType>
268 std::ostream& operator<<(std::ostream& os, Flags<FlagType> flags);
269 
274 template <class FlagType>
275 class Flags
276 {
277 private:
278  struct ConvertibleToBoolDummy {int _;};
279 
280 public:
281  // If you have received an error message directing you to the line below,
282  // it means you probably have tried to use this class with a FlagsType
283  // that is not a type generated by GG_FLAG_TYPE. Use that to generate new
284  // flag types.
285  BOOST_MPL_ASSERT((is_flag_type<FlagType>));
286 
288 
289  GG_ABSTRACT_EXCEPTION(Exception);
290 
292  GG_CONCRETE_EXCEPTION(UnknownFlag, GG::Flags, Exception);
294 
296 
297  Flags() : m_flags(0) {}
301  Flags(FlagType flag) :
302  m_flags(flag.m_value)
303  {
304  if (!FlagSpec<FlagType>::instance().contains(flag))
305  throw UnknownFlag("Invalid flag with value " + boost::lexical_cast<std::string>(flag.m_value));
306  }
308 
310 
313  operator int ConvertibleToBoolDummy::* () const
314  { return m_flags ? &ConvertibleToBoolDummy::_ : 0; }
316  bool operator==(Flags<FlagType> rhs) const
317  { return m_flags == rhs.m_flags; }
319  bool operator!=(Flags<FlagType> rhs) const
320  { return m_flags != rhs.m_flags; }
324  bool operator<(Flags<FlagType> rhs) const
325  { return m_flags < rhs.m_flags; }
327 
329 
332  {
333  m_flags |= rhs.m_flags;
334  return *this;
335  }
339  {
340  m_flags &= rhs.m_flags;
341  return *this;
342  }
346  {
347  m_flags ^= rhs.m_flags;
348  return *this;
349  }
351 
352 private:
353  unsigned int m_flags;
354 
355  friend std::ostream& operator<<<>(std::ostream& os, Flags<FlagType> flags);
356 };
357 
359 template <class FlagType>
360 std::ostream& operator<<(std::ostream& os, Flags<FlagType> flags)
361 {
362  unsigned int flags_data = flags.m_flags;
363  bool flag_printed = false;
364  for (std::size_t i = 0; i < sizeof(flags_data) * 8; ++i) {
365  if (flags_data & 1) {
366  if (flag_printed)
367  os << " | ";
368  os << FlagSpec<FlagType>::instance().ToString(FlagType(1 << i));
369  flag_printed = true;
370  }
371  flags_data >>= 1;
372  }
373  return os;
374 }
375 
378 template <class FlagType>
380 {
381  Flags<FlagType> retval(lhs);
382  retval |= rhs;
383  return retval;
384 }
385 
388 template <class FlagType>
390 { return lhs | Flags<FlagType>(rhs); }
391 
394 template <class FlagType>
396 { return Flags<FlagType>(lhs) | rhs; }
397 
400 template <class FlagType>
401 typename boost::enable_if<
402  is_flag_type<FlagType>,
403  Flags<FlagType>
404 >::type
405 operator|(FlagType lhs, FlagType rhs)
406 { return Flags<FlagType>(lhs) | Flags<FlagType>(rhs); }
407 
410 template <class FlagType>
412 {
413  Flags<FlagType> retval(lhs);
414  retval &= rhs;
415  return retval;
416 }
417 
420 template <class FlagType>
422 { return lhs & Flags<FlagType>(rhs); }
423 
426 template <class FlagType>
428 { return Flags<FlagType>(lhs) & rhs; }
429 
432 template <class FlagType>
433 typename boost::enable_if<
434  is_flag_type<FlagType>,
435  Flags<FlagType>
436 >::type
437 operator&(FlagType lhs, FlagType rhs)
438 { return Flags<FlagType>(lhs) & Flags<FlagType>(rhs); }
439 
442 template <class FlagType>
444 {
445  Flags<FlagType> retval(lhs);
446  retval ^= rhs;
447  return retval;
448 }
449 
452 template <class FlagType>
454 { return lhs ^ Flags<FlagType>(rhs); }
455 
458 template <class FlagType>
460 { return Flags<FlagType>(lhs) ^ rhs; }
461 
464 template <class FlagType>
465 typename boost::enable_if<
466  is_flag_type<FlagType>,
467  Flags<FlagType>
468 >::type
469 operator^(FlagType lhs, FlagType rhs)
470 { return Flags<FlagType>(lhs) ^ Flags<FlagType>(rhs); }
471 
474 template <class FlagType>
476 {
477  Flags<FlagType> retval;
479  for (typename FlagSpec<FlagType>::const_iterator it = spec.begin(); it != spec.end(); ++it) {
480  if (!(*it & flags))
481  retval |= *it;
482  }
483  return retval;
484 }
485 
488 template <class FlagType>
489 typename boost::enable_if<
490  is_flag_type<FlagType>,
491  Flags<FlagType>
492 >::type
493 operator~(FlagType flag)
494 { return ~Flags<FlagType>(flag); }
495 
496 } // namespace GG
497 
498 #endif