diff --git a/Telegram/SourceFiles/base/flags.h b/Telegram/SourceFiles/base/flags.h new file mode 100644 index 0000000000..d51e84f1a4 --- /dev/null +++ b/Telegram/SourceFiles/base/flags.h @@ -0,0 +1,370 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It 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 General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include + +#if defined _MSC_VER && _MSC_VER < 1910 +#define FLAGS_CONSTEXPR +#else // MSVS2015 +#define FLAGS_CONSTEXPR constexpr +#endif // MSVS2015 + +namespace base { + +template +class flags; + +template +struct extended_flags; + +template +using extended_flags_t = typename extended_flags::type; + +namespace details { + +struct flags_zero_helper_struct { +}; + +using flags_zero_helper = void(base::details::flags_zero_helper_struct::*)(); + +template ::type> +inline constexpr auto extended_flag_convert(ExtendedEnum value) { + return static_cast(value); +} + +template ::type> +inline constexpr auto extended_flags_convert(ExtendedEnum value) { + return flags(extended_flag_convert(value)); +} + +} // namespace details + +template +class flags { +public: + using Enum = EnumType; + using Type = std::underlying_type_t; + + constexpr flags() = default; + constexpr flags(details::flags_zero_helper) noexcept { + } + constexpr flags(Enum value) noexcept : _value(static_cast(value)) { + } + explicit constexpr flags(Type value) noexcept : _value(value) { + } + + constexpr auto value() const noexcept { + return _value; + } + constexpr operator Type() const noexcept { + return value(); + } + + FLAGS_CONSTEXPR auto &operator|=(flags b) noexcept { + _value |= b.value(); + return *this; + } + FLAGS_CONSTEXPR auto &operator&=(flags b) noexcept { + _value &= b.value(); + return *this; + } + FLAGS_CONSTEXPR auto &operator^=(flags b) noexcept { + _value ^= b.value(); + return *this; + } + + FLAGS_CONSTEXPR auto operator~() const noexcept { + return flags(~value()); + } + + FLAGS_CONSTEXPR auto operator|(flags b) const noexcept { + return (flags(*this) |= b); + } + FLAGS_CONSTEXPR auto operator&(flags b) const noexcept { + return (flags(*this) &= b); + } + FLAGS_CONSTEXPR auto operator^(flags b) const noexcept { + return (flags(*this) ^= b); + } + + FLAGS_CONSTEXPR auto operator|(Enum b) const noexcept { + return (flags(*this) |= b); + } + FLAGS_CONSTEXPR auto operator&(Enum b) const noexcept { + return (flags(*this) &= b); + } + FLAGS_CONSTEXPR auto operator^(Enum b) const noexcept { + return (flags(*this) ^= b); + } + + constexpr auto operator==(Enum b) const noexcept { + return (value() == static_cast(b)); + } + constexpr auto operator!=(Enum b) const noexcept { + return !(*this == b); + } + constexpr auto operator<(Enum b) const noexcept { + return value() < static_cast(b); + } + constexpr auto operator>(Enum b) const noexcept { + return (b < *this); + } + constexpr auto operator<=(Enum b) const noexcept { + return !(b < *this); + } + constexpr auto operator>=(Enum b) const noexcept { + return !(*this < b); + } + +private: + Type _value = 0; + +}; + +template +constexpr auto make_flags(Enum value) noexcept { + return flags(value); +} + +template ::value>, + typename = std::enable_if_t> +inline constexpr auto operator|(Enum a, flags b) noexcept { + return b | a; +} + +template ::value>, + typename = std::enable_if_t> +inline constexpr auto operator&(Enum a, flags b) noexcept { + return b & a; +} + +template ::value>, + typename = std::enable_if_t> +inline constexpr auto operator^(Enum a, flags b) noexcept { + return b ^ a; +} + +template ::type> +inline constexpr auto operator|(flags> a, ExtendedEnum b) { + return a | details::extended_flags_convert(b); +} + +template ::type> +inline constexpr auto operator|(ExtendedEnum a, flags> b) { + return b | a; +} + +template > +inline constexpr auto operator&(flags> a, ExtendedEnum b) { + return a & details::extended_flags_convert(b); +} + +template ::type> +inline constexpr auto operator&(ExtendedEnum a, flags> b) { + return b & a; +} + +template > +inline constexpr auto operator^(flags> a, ExtendedEnum b) { + return a ^ details::extended_flags_convert(b); +} + +template ::type> +inline constexpr auto operator^(ExtendedEnum a, flags> b) { + return b ^ a; +} + +template ::type> +inline constexpr auto &operator&=(flags> &a, ExtendedEnum b) { + return (a &= details::extended_flags_convert(b)); +} + +template ::type> +inline constexpr auto &operator|=(flags> &a, ExtendedEnum b) { + return (a |= details::extended_flags_convert(b)); +} + +template ::type> +inline constexpr auto &operator^=(flags> &a, ExtendedEnum b) { + return (a ^= details::extended_flags_convert(b)); +} + +template ::type> +inline constexpr auto operator==(flags> a, ExtendedEnum b) { + return a == details::extended_flags_convert(b); +} + +template ::type> +inline constexpr auto operator==(ExtendedEnum a, flags> b) { + return (b == a); +} + +template ::type> +inline constexpr auto operator!=(flags> a, ExtendedEnum b) { + return !(a == b); +} + +template ::type> +inline constexpr auto operator!=(ExtendedEnum a, flags> b) { + return !(a == b); +} + +template ::type> +inline constexpr auto operator<(flags> a, ExtendedEnum b) { + return a < details::extended_flags_convert(b); +} + +template ::type> +inline constexpr auto operator<(ExtendedEnum a, flags> b) { + return details::extended_flags_convert(a) < b; +} + +template ::type> +inline constexpr auto operator>(flags> a, ExtendedEnum b) { + return (b < a); +} + +template ::type> +inline constexpr auto operator>(ExtendedEnum a, flags> b) { + return (b < a); +} + +template ::type> +inline constexpr auto operator<=(flags> a, ExtendedEnum b) { + return !(b < a); +} + +template ::type> +inline constexpr auto operator<=(ExtendedEnum a, flags> b) { + return !(b < a); +} + +template ::type> +inline constexpr auto operator>=(flags> a, ExtendedEnum b) { + return !(a < b); +} + +template ::type> +inline constexpr auto operator>=(ExtendedEnum a, flags> b) { + return !(a < b); +} + +} // namespace base + +#undef FLAGS_CONSTEXPR + +template ::value>, + typename = std::enable_if_t> +inline constexpr auto operator!(Enum a) noexcept { + return !base::make_flags(a); +} + +template ::value>, + typename = std::enable_if_t> +inline constexpr auto operator~(Enum a) noexcept { + return ~base::make_flags(a); +} + +template ::value>, + typename = std::enable_if_t> +inline constexpr auto operator|(Enum a, Enum b) noexcept { + return base::make_flags(a) | b; +} + +template ::value>, + typename = std::enable_if_t> +inline constexpr auto operator|(Enum a, base::details::flags_zero_helper) noexcept { + return base::make_flags(a); +} + +template ::value>, + typename = std::enable_if_t> +inline constexpr auto operator|(base::details::flags_zero_helper, Enum b) noexcept { + return base::make_flags(b); +} + +template ::type> +inline constexpr auto operator|(ExtendedEnum a, ExtendedEnum b) { + return base::details::extended_flags_convert(a) | b; +} + +template ::type> +inline constexpr auto operator|(ExtendedEnum a, typename base::extended_flags::type b) { + return base::details::extended_flags_convert(a) | b; +} + +template ::type> +inline constexpr auto operator|(typename base::extended_flags::type a, ExtendedEnum b) { + return b | a; +} + +template ::type> +inline constexpr auto operator|(base::details::flags_zero_helper, ExtendedEnum b) { + return 0 | base::details::extended_flag_convert(b); +} + +template ::type> +inline constexpr auto operator|(ExtendedEnum a, base::details::flags_zero_helper) { + return base::details::extended_flag_convert(a) | 0; +} + +template ::type> +inline constexpr auto operator~(ExtendedEnum b) { + return ~base::details::extended_flags_convert(b); +} diff --git a/Telegram/SourceFiles/base/flags_tests.cpp b/Telegram/SourceFiles/base/flags_tests.cpp new file mode 100644 index 0000000000..f2da24d53b --- /dev/null +++ b/Telegram/SourceFiles/base/flags_tests.cpp @@ -0,0 +1,142 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It 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 General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "catch.hpp" + +#include "base/flags.h" + +namespace MethodNamespace { + +template +void TestFlags(Enum a, Enum b, Enum c) { + auto abc = a | b; + abc |= c; + auto test = abc != a; + CHECK(abc != a); + CHECK(abc != (a | b)); + CHECK((abc & a) == a); + CHECK((abc & b) == b); + CHECK((abc & c) == c); + CHECK((abc & ~a) == (b | c)); + CHECK((abc & ~(b | c)) == a); + CHECK((abc ^ a) == (abc & ~a)); + + auto another = a | b; + another |= c; + CHECK(abc == another); + another &= ~b; + CHECK(another == (a | c)); + another ^= a; + CHECK(another == c); + another = 0; + another = nullptr; + auto is_zero = ((another & abc) == 0); + CHECK(is_zero); + CHECK(!(another & abc)); + auto more = a | another; + auto just = a | 0; + CHECK(more == just); + CHECK(just); +} + +} // namespace MethodNamespace + +namespace FlagsNamespace { + +enum class Flag : int { + one = (1 << 0), + two = (1 << 1), + three = (1 << 2), +}; +inline constexpr auto is_flag_type(Flag) { return true; } + +class Class { +public: + enum class Public : long { + one = (1 << 2), + two = (1 << 1), + three = (1 << 0), + }; + friend inline constexpr auto is_flag_type(Public) { return true; } + + static void TestPrivate(); + +private: + enum class Private : long { + one = (1 << 0), + two = (1 << 1), + three = (1 << 2), + }; + friend inline constexpr auto is_flag_type(Private) { return true; } + +}; + +void Class::TestPrivate() { + MethodNamespace::TestFlags(Private::one, Private::two, Private::three); +} + +} // namespace FlagsNamespace + +namespace ExtendedNamespace { + +enum class Flag : int { + one = (1 << 3), + two = (1 << 4), + three = (1 << 5), +}; + +} // namespace ExtendedNamespace + +namespace base { + +template<> +struct extended_flags { + using type = FlagsNamespace::Flag; +}; + +} // namespace base + +TEST_CASE("flags operators on scoped enums", "[flags]") { + SECTION("testing non-member flags") { + MethodNamespace::TestFlags( + FlagsNamespace::Flag::one, + FlagsNamespace::Flag::two, + FlagsNamespace::Flag::three); + } + SECTION("testing public member flags") { + MethodNamespace::TestFlags( + FlagsNamespace::Class::Public::one, + FlagsNamespace::Class::Public::two, + FlagsNamespace::Class::Public::three); + } + SECTION("testing private member flags") { + FlagsNamespace::Class::TestPrivate(); + } + SECTION("testing extended flags") { + MethodNamespace::TestFlags( + ExtendedNamespace::Flag::one, + ExtendedNamespace::Flag::two, + ExtendedNamespace::Flag::three); + + auto onetwo = FlagsNamespace::Flag::one | ExtendedNamespace::Flag::two; + auto twoone = ExtendedNamespace::Flag::two | FlagsNamespace::Flag::one; + CHECK(onetwo == twoone); + } +} diff --git a/Telegram/gyp/tests/tests.gyp b/Telegram/gyp/tests/tests.gyp index 105554be9e..42284ddffb 100644 --- a/Telegram/gyp/tests/tests.gyp +++ b/Telegram/gyp/tests/tests.gyp @@ -73,5 +73,14 @@ '<(src_loc)/base/flat_set.h', '<(src_loc)/base/flat_set_tests.cpp', ], + }, { + 'target_name': 'tests_flags', + 'includes': [ + 'common_test.gypi', + ], + 'sources': [ + '<(src_loc)/base/flags.h', + '<(src_loc)/base/flags_tests.cpp', + ], }], } diff --git a/Telegram/gyp/tests/tests_list.txt b/Telegram/gyp/tests/tests_list.txt index 6d79c8e1e5..6cb15eda32 100644 --- a/Telegram/gyp/tests/tests_list.txt +++ b/Telegram/gyp/tests/tests_list.txt @@ -1,2 +1,3 @@ tests_flat_map tests_flat_set +tests_flags