diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index 01cbc8852..360eb4ab8 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -503,6 +503,7 @@ HEADERS += util/folderiterator.h \ util/stacktrace.h \ util/rsdeprecate.h \ util/cxx11retrocompat.h \ + util/cxx17retrocompat.h \ util/rsurl.h SOURCES += ft/ftchunkmap.cc \ 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) {} diff --git a/libretroshare/src/util/cxx17retrocompat.h b/libretroshare/src/util/cxx17retrocompat.h new file mode 100644 index 000000000..58cdfe84c --- /dev/null +++ b/libretroshare/src/util/cxx17retrocompat.h @@ -0,0 +1,35 @@ +/******************************************************************************* + * libretroshare/src/util: cxx17retrocompat.h * + * * + * libretroshare: retroshare core library * + * * + * 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 * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#pragma once + +#if __cplusplus < 201703L + +#include + +namespace std +{ +using namespace std; +template constexpr typename add_const::type& as_const(T& t) noexcept +{ return t; } +template void as_const(const T&&) = delete; +} +#endif // __cplusplus < 201703L diff --git a/libretroshare/src/util/rsmemory.h b/libretroshare/src/util/rsmemory.h index 8bbbc335a..01dc4d8d8 100644 --- a/libretroshare/src/util/rsmemory.h +++ b/libretroshare/src/util/rsmemory.h @@ -62,6 +62,35 @@ bool myFunnyFunction( */ #define RS_DEFAULT_STORAGE_PARAM(Type,...) *std::unique_ptr(new Type(__VA_ARGS__)) + +/** @brief Safely dynamic cast between std::unique_ptr of different types + * std::unique_ptr semantic rely on the invariant that only one instance own + * the object, when casting between differents types one would be tempted to do + * it in a one liner that easly end up breaking that condition ending up in a + * double delete and crash or in a silent memleak. + * With this function one can do that with same comfort of a plain dynamic_cast, + * plus the std::unique_ptr safety. + * @param[inout] src reference to source pointer. If the cast is successfull it + * is released, otherwise it is left untouched. + * @param[out] dst reference to destination pointer. If the cast is successful + * it get reseated to the object address, otherwise it is left untouched. + * @return true on success, false otherwise + */ +template +bool rs_unique_cast( + std::unique_ptr& src, std::unique_ptr& dst ) +{ + T_DST* dstPtr = dynamic_cast(src.get()); + if(dstPtr) + { + src.release(); + dst.reset(dstPtr); + return true; + } + return false; +} + + void *rs_malloc(size_t size) ; // This is a scope guard to release the memory block when going of of the current scope.