GG
|
00001 // -*- C++ -*- 00002 /* GG is a GUI for SDL and OpenGL. 00003 Copyright (C) 2007 T. Zachary Laine 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Lesser General Public License 00007 as published by the Free Software Foundation; either version 2.1 00008 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Lesser General Public License for more details. 00014 00015 You should have received a copy of the GNU Lesser General Public 00016 License along with this library; if not, write to the Free 00017 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 00018 02111-1307 USA 00019 00020 If you do not wish to comply with the terms of the LGPL please 00021 contact the author as other terms are available for a fee. 00022 00023 Zach Laine 00024 whatwasthataddress@gmail.com */ 00025 00029 #ifndef _GG_Flags_h_ 00030 #define _GG_Flags_h_ 00031 00032 #include <GG/Exception.h> 00033 00034 #include <boost/utility/enable_if.hpp> 00035 #include <boost/lexical_cast.hpp> 00036 #include <boost/mpl/assert.hpp> 00037 #include <boost/serialization/access.hpp> 00038 #include <boost/serialization/nvp.hpp> 00039 00040 #include <cassert> 00041 #include <iosfwd> 00042 #include <map> 00043 #include <set> 00044 00045 00046 namespace GG { 00047 00048 namespace detail { 00049 inline std::size_t OneBits(unsigned int num) 00050 { 00051 std::size_t retval = 0; 00052 const std::size_t NUM_BITS = sizeof(num) * 8; 00053 for (std::size_t i = 0; i < NUM_BITS; ++i) { 00054 if (num & 1) 00055 ++retval; 00056 num >>= 1; 00057 } 00058 return retval; 00059 } 00060 } 00061 00062 00065 template <class T> 00066 struct is_flag_type : boost::mpl::false_ {}; 00067 00068 00076 #define GG_FLAG_TYPE(name) \ 00077 class name; \ 00078 \ 00079 template <> \ 00080 struct is_flag_type<name> : boost::mpl::true_ {}; \ 00081 \ 00082 class name \ 00083 { \ 00084 public: \ 00085 name() : m_value(0) {} \ 00086 explicit name(unsigned int value) : \ 00087 m_value(value) \ 00088 { \ 00089 if (1u < detail::OneBits(value)) \ 00090 throw std::invalid_argument( \ 00091 "Non-bitflag passed to " #name " constructor"); \ 00092 } \ 00093 bool operator==(name rhs) const \ 00094 { return m_value == rhs.m_value; } \ 00095 bool operator!=(name rhs) const \ 00096 { return m_value != rhs.m_value; } \ 00097 bool operator<(name rhs) const \ 00098 { return m_value < rhs.m_value; } \ 00099 private: \ 00100 unsigned int m_value; \ 00101 friend class Flags<name>; \ 00102 \ 00103 friend class boost::serialization::access; \ 00104 template <class Archive> \ 00105 void serialize(Archive& ar, const unsigned int version) \ 00106 { ar & BOOST_SERIALIZATION_NVP(m_value); } \ 00107 }; \ 00108 \ 00109 template <> \ 00110 FlagSpec<name>& FlagSpec<name>::instance(); \ 00111 \ 00112 inline std::ostream& operator<<(std::ostream& os, name n) \ 00113 { \ 00114 os << FlagSpec<name>::instance().ToString(n); \ 00115 return os; \ 00116 } \ 00117 \ 00118 inline std::istream& operator>>(std::istream& is, name& n) \ 00119 { \ 00120 std::string str; \ 00121 is >> str; \ 00122 n = FlagSpec<name>::instance().FromString(str); \ 00123 return is; \ 00124 } 00125 00126 00129 #define GG_FLAGSPEC_IMPL(name) \ 00130 template <> \ 00131 FlagSpec<name>& FlagSpec<name>::instance() \ 00132 { \ 00133 static FlagSpec retval; \ 00134 return retval; \ 00135 } 00136 00137 00155 template <class FlagType> 00156 class GG_API FlagSpec 00157 { 00158 public: 00159 // If you have received an error message directing you to the line below, 00160 // it means you probably have tried to use this class with a FlagsType 00161 // that is not a type generated by GG_FLAG_TYPE. Use that to generate new 00162 // flag types. 00163 BOOST_MPL_ASSERT((is_flag_type<FlagType>)); 00164 00166 typedef typename std::set<FlagType>::iterator iterator; 00168 typedef typename std::set<FlagType>::const_iterator const_iterator; 00169 00171 00172 GG_ABSTRACT_EXCEPTION(Exception); 00173 00175 GG_CONCRETE_EXCEPTION(UnknownFlag, GG::FlagSpec, Exception); 00176 00178 GG_CONCRETE_EXCEPTION(UnknownString, GG::FlagSpec, Exception); 00180 00182 static FlagSpec& instance(); 00183 00185 00186 bool contains(FlagType flag) const 00187 { return find(flag) != end(); } 00190 bool permanent(FlagType flag) const 00191 { return m_permanent.find(flag) != m_permanent.end(); } 00194 const_iterator find(FlagType flag) const 00195 { return m_flags.find(flag); } 00197 const_iterator begin() const 00198 { return m_flags.begin(); } 00200 const_iterator end() const 00201 { return m_flags.end(); } 00205 const std::string& ToString(FlagType flag) const 00206 { 00207 typename std::map<FlagType, std::string>::const_iterator it = m_strings.find(flag); 00208 if (it == m_strings.end()) 00209 throw UnknownFlag("Could not find string corresponding to unknown flag"); 00210 return it->second; 00211 } 00214 FlagType FromString(const std::string& str) const 00215 { 00216 for (typename std::map<FlagType, std::string>::const_iterator it = m_strings.begin(); 00217 it != m_strings.end(); 00218 ++it) { 00219 if (it->second == str) 00220 return it->first; 00221 } 00222 throw UnknownString("Could not find flag corresponding to unknown string"); 00223 return FlagType(0); 00224 } 00226 00228 00232 void insert(FlagType flag, const std::string& name, bool permanent = false) 00233 { 00234 #ifndef NDEBUG 00235 bool insert_successful = 00236 #endif 00237 m_flags.insert(flag).second; 00238 assert(insert_successful); 00239 if (permanent) 00240 m_permanent.insert(flag); 00241 m_strings[flag] = name; 00242 } 00248 bool erase(FlagType flag) 00249 { 00250 bool retval = true; 00251 if (permanent(flag)) { 00252 retval = false; 00253 } else { 00254 m_flags.erase(flag); 00255 m_permanent.erase(flag); 00256 m_strings.erase(flag); 00257 } 00258 return retval; 00259 } 00261 00262 private: 00263 FlagSpec() {} 00264 00265 std::set<FlagType> m_flags; 00266 std::set<FlagType> m_permanent; 00267 std::map<FlagType, std::string> m_strings; 00268 }; 00269 00270 00271 template <class FlagType> 00272 class Flags; 00273 00274 template <class FlagType> 00275 std::ostream& operator<<(std::ostream& os, Flags<FlagType> flags); 00276 00281 template <class FlagType> 00282 class Flags 00283 { 00284 private: 00285 struct ConvertibleToBoolDummy {int _;}; 00286 00287 public: 00288 // If you have received an error message directing you to the line below, 00289 // it means you probably have tried to use this class with a FlagsType 00290 // that is not a type generated by GG_FLAG_TYPE. Use that to generate new 00291 // flag types. 00292 BOOST_MPL_ASSERT((is_flag_type<FlagType>)); 00293 00295 00296 GG_ABSTRACT_EXCEPTION(Exception); 00297 00299 GG_CONCRETE_EXCEPTION(UnknownFlag, GG::Flags, Exception); 00301 00303 00304 Flags() : m_flags(0) {} 00308 Flags(FlagType flag) : 00309 m_flags(flag.m_value) 00310 { 00311 if (!FlagSpec<FlagType>::instance().contains(flag)) 00312 throw UnknownFlag("Invalid flag with value " + boost::lexical_cast<std::string>(flag.m_value)); 00313 } 00315 00317 00320 operator int ConvertibleToBoolDummy::* () const 00321 { return m_flags ? &ConvertibleToBoolDummy::_ : 0; } 00323 bool operator==(Flags<FlagType> rhs) const 00324 { return m_flags == rhs.m_flags; } 00326 bool operator!=(Flags<FlagType> rhs) const 00327 { return m_flags != rhs.m_flags; } 00331 bool operator<(Flags<FlagType> rhs) const 00332 { return m_flags < rhs.m_flags; } 00334 00336 00338 Flags<FlagType>& operator|=(Flags<FlagType> rhs) 00339 { 00340 m_flags |= rhs.m_flags; 00341 return *this; 00342 } 00345 Flags<FlagType>& operator&=(Flags<FlagType> rhs) 00346 { 00347 m_flags &= rhs.m_flags; 00348 return *this; 00349 } 00352 Flags<FlagType>& operator^=(Flags<FlagType> rhs) 00353 { 00354 m_flags ^= rhs.m_flags; 00355 return *this; 00356 } 00358 00359 private: 00360 unsigned int m_flags; 00361 00362 friend std::ostream& operator<<<>(std::ostream& os, Flags<FlagType> flags); 00363 00364 friend class boost::serialization::access; 00365 template <class Archive> 00366 void serialize(Archive& ar, const unsigned int version); 00367 }; 00368 00370 template <class FlagType> 00371 std::ostream& operator<<(std::ostream& os, Flags<FlagType> flags) 00372 { 00373 unsigned int flags_data = flags.m_flags; 00374 bool flag_printed = false; 00375 for (std::size_t i = 0; i < sizeof(flags_data) * 8; ++i) { 00376 if (flags_data & 1) { 00377 if (flag_printed) 00378 os << " | "; 00379 os << FlagSpec<FlagType>::instance().ToString(FlagType(1 << i)); 00380 flag_printed = true; 00381 } 00382 flags_data >>= 1; 00383 } 00384 return os; 00385 } 00386 00389 template <class FlagType> 00390 Flags<FlagType> operator|(Flags<FlagType> lhs, Flags<FlagType> rhs) 00391 { 00392 Flags<FlagType> retval(lhs); 00393 retval |= rhs; 00394 return retval; 00395 } 00396 00399 template <class FlagType> 00400 Flags<FlagType> operator|(Flags<FlagType> lhs, FlagType rhs) 00401 { return lhs | Flags<FlagType>(rhs); } 00402 00405 template <class FlagType> 00406 Flags<FlagType> operator|(FlagType lhs, Flags<FlagType> rhs) 00407 { return Flags<FlagType>(lhs) | rhs; } 00408 00411 template <class FlagType> 00412 typename boost::enable_if< 00413 is_flag_type<FlagType>, 00414 Flags<FlagType> 00415 >::type 00416 operator|(FlagType lhs, FlagType rhs) 00417 { return Flags<FlagType>(lhs) | Flags<FlagType>(rhs); } 00418 00421 template <class FlagType> 00422 Flags<FlagType> operator&(Flags<FlagType> lhs, Flags<FlagType> rhs) 00423 { 00424 Flags<FlagType> retval(lhs); 00425 retval &= rhs; 00426 return retval; 00427 } 00428 00431 template <class FlagType> 00432 Flags<FlagType> operator&(Flags<FlagType> lhs, FlagType rhs) 00433 { return lhs & Flags<FlagType>(rhs); } 00434 00437 template <class FlagType> 00438 Flags<FlagType> operator&(FlagType lhs, Flags<FlagType> rhs) 00439 { return Flags<FlagType>(lhs) & rhs; } 00440 00443 template <class FlagType> 00444 typename boost::enable_if< 00445 is_flag_type<FlagType>, 00446 Flags<FlagType> 00447 >::type 00448 operator&(FlagType lhs, FlagType rhs) 00449 { return Flags<FlagType>(lhs) & Flags<FlagType>(rhs); } 00450 00453 template <class FlagType> 00454 Flags<FlagType> operator^(Flags<FlagType> lhs, Flags<FlagType> rhs) 00455 { 00456 Flags<FlagType> retval(lhs); 00457 retval ^= rhs; 00458 return retval; 00459 } 00460 00463 template <class FlagType> 00464 Flags<FlagType> operator^(Flags<FlagType> lhs, FlagType rhs) 00465 { return lhs ^ Flags<FlagType>(rhs); } 00466 00469 template <class FlagType> 00470 Flags<FlagType> operator^(FlagType lhs, Flags<FlagType> rhs) 00471 { return Flags<FlagType>(lhs) ^ rhs; } 00472 00475 template <class FlagType> 00476 typename boost::enable_if< 00477 is_flag_type<FlagType>, 00478 Flags<FlagType> 00479 >::type 00480 operator^(FlagType lhs, FlagType rhs) 00481 { return Flags<FlagType>(lhs) ^ Flags<FlagType>(rhs); } 00482 00485 template <class FlagType> 00486 Flags<FlagType> operator~(Flags<FlagType> flags) 00487 { 00488 Flags<FlagType> retval; 00489 const FlagSpec<FlagType>& spec = FlagSpec<FlagType>::instance(); 00490 for (typename FlagSpec<FlagType>::const_iterator it = spec.begin(); it != spec.end(); ++it) { 00491 if (!(*it & flags)) 00492 retval |= *it; 00493 } 00494 return retval; 00495 } 00496 00499 template <class FlagType> 00500 typename boost::enable_if< 00501 is_flag_type<FlagType>, 00502 Flags<FlagType> 00503 >::type 00504 operator~(FlagType flag) 00505 { return ~Flags<FlagType>(flag); } 00506 00507 } // namespace GG 00508 00509 template <class FlagType> 00510 template <class Archive> 00511 void GG::Flags<FlagType>::serialize(Archive& ar, const unsigned int version) 00512 { ar & BOOST_SERIALIZATION_NVP(m_flags); } 00513 00514 #endif // _GG_Flags_h_