diff --git a/libretroshare/src/retroshare/rsflags.h b/libretroshare/src/retroshare/rsflags.h index 51e91e9a1..cdea3f5be 100644 --- a/libretroshare/src/retroshare/rsflags.h +++ b/libretroshare/src/retroshare/rsflags.h @@ -3,7 +3,8 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2012-2019 by Retroshare Team * + * Copyright (C) 2012-2019 by Retroshare Team * + * Copyright (C) 2019 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -21,18 +22,129 @@ *******************************************************************************/ #pragma once +#include +#include + +/** Check if given type is a scoped enum */ +template +using rs_is_scoped_enum = std::integral_constant< bool, + std::is_enum::value && !std::is_convertible::value >; + +/** + * @brief Register enum class as flags type + * To use this macro define a scoped enum with your flag values, then register + * it as flags type passing it as parameter of this macro. + * The result will be type safe flags, that cannot be mixed up with flag of a + * different type, but that are very comfortable to operate like plain old + * integers. + * This macro support flag fields of different lenght depending on what + * underlining type (usually from uint8_t up to uint64_t) has been declared for + * the enum class. + * If you plan to serialize those flags it is important to specify the + * underlining type of the enum otherwise different compilers may serialize a + * flag variable with different lenght, potentially causing interoperability + * issues between differents builds. + * Usage example: +@code{.cpp} +enum class RsGrouterItemFlags : uint32_t +{ + NONE = 0x0, + ENCRYPTED = 0x1, + SERVICE_UNKNOWN = 0x2 +}; +RS_REGISTER_ENUM_FLAGS_TYPE(RsGrouterItemFlags) +@endcode + */ +#define RS_REGISTER_ENUM_FLAGS_TYPE(eft) \ +template<> struct Rs__BitFlagsOps \ +{ \ + static_assert( std::is_enum::value, \ + "Are you trying to register a non-enum type as flags?" ); \ + static_assert( rs_is_scoped_enum::value, \ + "Are you trying to register an unscoped enum as flags?" ); \ + static constexpr bool enabled = true; \ +}; + +// By defaults types are not valid flags, so bit flags operators are disabled +template struct Rs__BitFlagsOps +{ static constexpr bool enabled = false; }; + +template +typename std::enable_if::enabled, EFT>::type +/*EFT*/ operator &(EFT lhs, EFT rhs) +{ + using u_t = typename std::underlying_type::type; + return static_cast(static_cast(lhs) & static_cast(rhs)); +} + +template +typename std::enable_if::enabled, EFT>::type +/*EFT*/ operator &=(EFT& lhs, EFT rhs) { lhs = lhs & rhs; return lhs; } + +template +typename std::enable_if::enabled, EFT>::type +/*EFT*/ operator |(EFT lhs, EFT rhs) +{ + using u_t = typename std::underlying_type::type; + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +template +typename std::enable_if::enabled, EFT>::type +/*EFT*/ operator |=(EFT& lhs, EFT rhs) { lhs = lhs | rhs; return lhs; } + + +template +typename std::enable_if::enabled, EFT>::type +/*EFT*/ operator ^(EFT lhs, EFT rhs) +{ + using u_t = typename std::underlying_type::type; + return static_cast(static_cast(lhs) ^ static_cast(rhs)); +} + +template +typename std::enable_if::enabled, EFT>::type +/*EFT*/ operator ^=(EFT& lhs, EFT rhs) { lhs = lhs ^ rhs; return lhs; } + +template +typename std::enable_if::enabled, EFT>::type +operator ~(EFT val) +{ + using u_t = typename std::underlying_type::type; + return static_cast(~static_cast(val)); +} + +template +typename std::enable_if::enabled, bool>::type +operator !(EFT val) +{ + using u_t = typename std::underlying_type::type; + return static_cast(val) == 0; +} + +/// Nicely print flags bits as 1 and 0 +template +typename std::enable_if::enabled, std::ostream>::type& +operator <<(std::ostream& stream, EFT flags) +{ + using u_t = typename std::underlying_type::type; + + for(int i = sizeof(u_t); i>=0; --i) + { + stream << (flags & ( 1 << i ) ? "1" : "0"); + if( i % 8 == 0 ) stream << " "; + } + return stream; +} + #include +#include "util/rsdeprecate.h" -/* G10h4ck: TODO we should redefine flags in a way that the flag declaration and - * the flags values (bit fields) would be strongly logically linked. - * A possible way is to take an enum class containing the names of each - * bitfield and corresponding value as template parameter, this way would also - * avoid the need of dumb template parameter that is used only to make the - * types incompatible but that doesn't help finding what are the possible values - * for a kind of flag. Another appealing approach seems the first one described - * here https://softwareengineering.stackexchange.com/questions/194412/using-scoped-enums-for-bit-flags-in-c - * a few simple macros could be used instead of the template class */ +/** + * @deprecated t_RsFlags32 has been deprecated because the newer + * @see RS_REGISTER_ENUM_FLAGS_TYPE provide more convenient flags facilities. + * // This class provides a representation for flags that can be combined with bitwise // operations. However, because the class is templated with an id, it's not possible to // mixup flags belonging to different classes. This avoids many bugs due to confusion of flags types @@ -51,7 +163,8 @@ // - an explicit constructor from uint32_t // - an implicit bool operator, that allows test like if(flags & FLAGS_VALUE) // -template class t_RsFlags32 +*/ +template class RS_DEPRECATED_FOR(RS_REGISTER_ENUM_FLAGS_TYPE) t_RsFlags32 { public: inline t_RsFlags32() : _bits(0) {}