GG

Flags.h

Go to the documentation of this file.
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_