Mercurial > code
changeset 261:0c7bc55e0d36
Add Flags, a small wrapper for creating enum flags
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 09 Oct 2014 13:28:24 +0200 |
parents | 2096edb63a4f |
children | a9946b5ca0cb |
files | C++/Flags.h C++/Tests/Flags/CMakeLists.txt C++/Tests/Flags/main.cpp CMakeLists.txt |
diffstat | 4 files changed, 669 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Flags.h Thu Oct 09 13:28:24 2014 +0200 @@ -0,0 +1,341 @@ +/* + * Flags.h -- safe wrapper for enum flags + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FLAGS_H_ +#define _FLAGS_H_ + +/** + * @file Flags.h + * @brief Provide template class Flags and operators for enum classes + */ + +#include <type_traits> + +/** + * @class Flags + * @brief Wrapper that store enum flags + * + * This class is used to store an enum and make bitwise operations on it. User do not need + * to supply these operators because they are implemented in the class. + * + * This class is very cheap and store the real underlying type from the enum and all functions + * are inlined. + */ +template <typename Enum, typename Type = std::underlying_type_t<Enum>> +class Flags final { +private: + Type m_value { 0 }; + +public: + /** + * Construct flags to 0. + */ + constexpr Flags() noexcept = default; + + /** + * Construct flags with the enum value. + * + * @param m the mask + */ + constexpr Flags(Enum m) noexcept + : m_value(static_cast<Type>(m)) + { + } + + /** + * Construct flags with the enum value. + * + * @param m the mask + */ + constexpr Flags(Type m) noexcept + : m_value(m) + { + } + + /** + * Move constructor. + * + * @param other the other + */ + inline Flags(Flags &&other) noexcept = default; + + /** + * Copy constructor. + * + * @param other the other + */ + inline Flags(const Flags &other) noexcept = default; + + /** + * Move operator. + * + * @param other the other + */ + inline Flags &operator=(Flags &&other) noexcept = default; + + /** + * Copy operator. + * + * @param other the other + */ + inline Flags &operator=(const Flags &other) noexcept = default; + + /** + * &= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator&=(Enum m) noexcept + { + m_value &= static_cast<Type>(m); + + return *this; + } + + /** + * &= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator&=(Type m) noexcept + { + m_value &= m; + + return *this; + } + + /** + * |= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator|=(Enum m) noexcept + { + m_value |= static_cast<Type>(m); + + return *this; + } + + /** + * |= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator|=(Type m) noexcept + { + m_value |= m; + + return *this; + } + + /** + * ^= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator^=(Enum m) noexcept + { + m_value ^= static_cast<Type>(m); + + return *this; + } + + /** + * ^= operator. + * + * @param m the mask + * @return *this + */ + inline Flags &operator^=(Type m) noexcept + { + m_value ^= m; + + return *this; + } + + /** + * & operator. + * + * @param m the mask + * @return & combination + */ + constexpr Flags operator&(Enum m) const noexcept + { + return m_value & static_cast<Type>(m); + } + + /** + * & operator. + * + * @param m the mask + * @return & combination + */ + constexpr Flags operator&(Type m) const noexcept + { + return m_value & m; + } + + /** + * | operator. + * + * @param m the mask + * @return | combination + */ + constexpr Flags operator|(Enum m) const noexcept + { + return m_value | static_cast<Type>(m); + } + + /** + * | operator. + * + * @param m the mask + * @return | combination + */ + constexpr Flags operator|(Type m) const noexcept + { + return m_value | m; + } + + /** + * ^ operator. + * + * @param m the mask + * @return ^ combination + */ + constexpr Flags operator^(Enum m) const noexcept + { + return m_value & static_cast<Type>(m); + } + + /** + * ^ operator. + * + * @param m the mask + * @return ^ combination + */ + constexpr Flags operator^(Type m) const noexcept + { + return m_value & m; + } + + /** + * ~ unary operator. + * + * @return ~ + */ + constexpr Flags operator~() const noexcept + { + return ~m_value; + } + + /** + * Convert to bool. Simply check if value is not 0. + * + * @return true if value != 0 + */ + constexpr operator bool() const noexcept + { + return m_value != 0; + } + + /** + * Operator !. Simply check if value is 0. + * + * @return true if value == 0 + */ + constexpr operator!() const noexcept + { + return m_value == 0; + } + + /** + * Test comparison with other mask. + * + * @return true if this value == m + */ + constexpr bool operator==(Enum m) const noexcept + { + return m_value == static_cast<Type>(m); + } + + /** + * Test comparison with other mask. + * + * @return true if this value == m + */ + constexpr bool operator==(Type m) const noexcept + { + return m_value == m; + } +}; + +/** + * Apply & operator on the enum. + * + * @param x1 the first value + * @param x2 the second value + */ +template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> +constexpr Value operator&(Value x1, Value x2) noexcept +{ + return static_cast<Value>(static_cast<Type>(x1) & static_cast<Type>(x2)); +} + +/** + * Apply | operator on the enum. + * + * @param x1 the first value + * @param x2 the second value + */ +template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> +constexpr Value operator|(Value x1, Value x2) noexcept +{ + return static_cast<Value>(static_cast<Type>(x1) | static_cast<Type>(x2)); +} + +/** + * Apply ^ operator on the enum. + * + * @param x1 the first value + * @param x2 the second value + */ +template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> +constexpr Value operator^(Value x1, Value x2) noexcept +{ + return static_cast<Value>(static_cast<Type>(x1) ^ static_cast<Type>(x2)); +} + +/** + * Apply ~ operator on the enum. + * + * @param x1 the first value + * @param x2 the second value + */ +template <typename Value, typename Type = std::enable_if_t<std::is_enum<Value>::value, std::underlying_type_t<Value>>> +constexpr Value operator~(Value x) noexcept +{ + return static_cast<Value>(~static_cast<Type>(x)); +} + +#endif // !_FLAGS_H_ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Tests/Flags/CMakeLists.txt Thu Oct 09 13:28:24 2014 +0200 @@ -0,0 +1,25 @@ +# +# CMakeLists.txt -- tests for Flags +# +# Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +set( + SOURCES + ${code_SOURCE_DIR}/C++/Flags.h + main.cpp +) + +define_test(flags "${SOURCES}") \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Tests/Flags/main.cpp Thu Oct 09 13:28:24 2014 +0200 @@ -0,0 +1,298 @@ +/* + * main.cpp -- main test file for Flags + * + * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cstdint> + +#include <gtest/gtest.h> + +#include <Flags.h> + +enum Standard : uint8_t { + Fullscreen = (1 << 0), + Audio = (1 << 1) +}; + +enum class Strong : uint8_t { + NoLog = (1 << 0), + NoCheck = (1 << 1) +}; + +/* -------------------------------------------------------- + * Global operators on standard enum + * -------------------------------------------------------- */ + +TEST(OperatorsStandard, opand) +{ + Standard s(Fullscreen | Audio); + + ASSERT_EQ(Fullscreen, (s & Fullscreen)); + ASSERT_EQ(Audio, (s & Audio)); +} + +TEST(OperatorsStandard, opor) +{ + Standard s(Fullscreen); + + ASSERT_EQ(3, (s | Audio)); +} + +TEST(OperatorsStandard, opxor) +{ + Standard s(Fullscreen); + + ASSERT_EQ(3, (s ^ Audio)); +} + +TEST(OperatorsStandard, opnot) +{ + Standard s(Fullscreen); + + ASSERT_EQ(254, ~s); +} + +/* -------------------------------------------------------- + * Global operators on strong enum + * -------------------------------------------------------- */ + +TEST(OperatorsStrong, opand) +{ + Strong s(Strong::NoLog | Strong::NoCheck); + + ASSERT_EQ(Strong::NoLog, (s & Strong::NoLog)); + ASSERT_EQ(Strong::NoCheck, (s & Strong::NoCheck)); +} + +TEST(OperatorsStrong, opor) +{ + Strong s(Strong::NoLog); + + ASSERT_EQ(3, static_cast<int>((s | Strong::NoCheck))); +} + +TEST(OperatorsStrong, opxor) +{ + Strong s(Strong::NoLog); + + ASSERT_EQ(3, static_cast<int>((s ^ Strong::NoCheck))); +} + +TEST(OperatorsStrong, opnot) +{ + Strong s(Strong::NoLog); + + ASSERT_EQ(254, static_cast<int>(~s)); +} + +/* -------------------------------------------------------- + * Flags with standard enums + * -------------------------------------------------------- */ + +TEST(Standard, construct) +{ + Flags<Standard> s; + + ASSERT_FALSE(s); + ASSERT_TRUE(!s); + + Flags <Standard> s2(Fullscreen | Audio); + + ASSERT_TRUE(s2); + ASSERT_FALSE(!s2); + ASSERT_TRUE(s2 & Fullscreen); + ASSERT_TRUE(s2 & Audio); + ASSERT_TRUE((s2 & Fullscreen) == Fullscreen); + ASSERT_TRUE((s2 & Audio) == Audio); + ASSERT_TRUE(s2 == (Audio | Fullscreen)); +} + +TEST(Standard, addByOne) +{ + Flags<Standard> s; + + ASSERT_FALSE(s); + ASSERT_TRUE(!s); + ASSERT_TRUE(!(s & Fullscreen)); + ASSERT_TRUE(!(s & Audio)); + + s |= Fullscreen; + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_TRUE(s & Fullscreen); + ASSERT_TRUE((s & Fullscreen) == Fullscreen); + ASSERT_FALSE(s & Audio); + ASSERT_FALSE((s & Audio) == Audio); + ASSERT_TRUE(s == Fullscreen); + + s |= Audio; + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_TRUE(s & Fullscreen); + ASSERT_TRUE((s & Fullscreen) == Fullscreen); + ASSERT_TRUE(s & Audio); + ASSERT_TRUE((s & Audio) == Audio); + ASSERT_TRUE(s == (Fullscreen | Audio)); +} + +TEST(Standard, add) +{ + Flags<Standard> s; + + s |= Fullscreen | Audio; + ASSERT_TRUE(s & (Fullscreen | Audio)); + ASSERT_TRUE((s & (Fullscreen | Audio)) == (Fullscreen | Audio)); +} + +TEST(Standard, removeByOne) +{ + Flags<Standard> s(Fullscreen | Audio); + + s &= ~(Fullscreen); + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_FALSE(s & Fullscreen); + ASSERT_FALSE((s & Fullscreen) == Fullscreen); + ASSERT_TRUE(s & Audio); + ASSERT_TRUE((s & Audio) == Audio); + + s &= ~(Audio); + ASSERT_FALSE(s); + ASSERT_TRUE(!s); +} + +TEST(Standard, remove) +{ + Flags<Standard> s(Fullscreen | Audio); + + s &= ~(Fullscreen | Audio); + ASSERT_FALSE(s); + ASSERT_TRUE(!s); +} + +TEST(Standard, xorAdd) +{ + Flags<Standard> s(Fullscreen | Audio); + + s ^= Audio; + ASSERT_TRUE(s & Fullscreen); + ASSERT_TRUE((s & Fullscreen) == Fullscreen); + ASSERT_FALSE(s & Audio); + ASSERT_FALSE((s & Audio) == Audio); +} + +/* -------------------------------------------------------- + * Flags with strong enums + * -------------------------------------------------------- */ + +TEST(Strong, construct) +{ + Flags<Strong> s; + + ASSERT_FALSE(s); + ASSERT_TRUE(!s); + + Flags <Strong> s2(Strong::NoLog | Strong::NoCheck); + + ASSERT_TRUE(s2); + ASSERT_FALSE(!s2); + ASSERT_TRUE(s2 & Strong::NoLog); + ASSERT_TRUE(s2 & Strong::NoCheck); + ASSERT_TRUE((s2 & Strong::NoLog) == Strong::NoLog); + ASSERT_TRUE((s2 & Strong::NoCheck) == Strong::NoCheck); + ASSERT_TRUE(s2 == (Strong::NoLog | Strong::NoCheck)); +} + +TEST(Strong, addByOne) +{ + Flags<Strong> s; + + ASSERT_FALSE(s); + ASSERT_TRUE(!s); + ASSERT_TRUE(!(s & Strong::NoLog)); + ASSERT_TRUE(!(s & Strong::NoCheck)); + + s |= Strong::NoLog; + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_TRUE(s & Strong::NoLog); + ASSERT_TRUE((s & Strong::NoLog) == Strong::NoLog); + ASSERT_FALSE(s & Strong::NoCheck); + ASSERT_FALSE((s & Strong::NoCheck) == Strong::NoCheck); + ASSERT_TRUE(s == Strong::NoLog); + + s |= Strong::NoCheck; + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_TRUE(s & Strong::NoLog); + ASSERT_TRUE((s & Strong::NoLog) == Strong::NoLog); + ASSERT_TRUE(s & Strong::NoCheck); + ASSERT_TRUE((s & Strong::NoCheck) == Strong::NoCheck); + ASSERT_TRUE(s == (Strong::NoLog | Strong::NoCheck)); +} + +TEST(Strong, add) +{ + Flags<Strong> s; + + s |= Strong::NoLog | Strong::NoCheck; + ASSERT_TRUE(s & (Strong::NoLog | Strong::NoCheck)); + ASSERT_TRUE((s & (Strong::NoLog | Strong::NoCheck)) == (Strong::NoLog | Strong::NoCheck)); +} + +TEST(Strong, removeByOne) +{ + Flags<Strong> s(Strong::NoLog | Strong::NoCheck); + + s &= ~(Strong::NoLog); + ASSERT_TRUE(s); + ASSERT_FALSE(!s); + ASSERT_FALSE(s & Strong::NoLog); + ASSERT_FALSE((s & Strong::NoLog) == Strong::NoLog); + ASSERT_TRUE(s & Strong::NoCheck); + ASSERT_TRUE((s & Strong::NoCheck) == Strong::NoCheck); + + s &= ~(Strong::NoCheck); + ASSERT_FALSE(s); + ASSERT_TRUE(!s); +} + +TEST(Strong, remove) +{ + Flags<Strong> s(Strong::NoLog | Strong::NoCheck); + + s &= ~(Strong::NoLog | Strong::NoCheck); + ASSERT_FALSE(s); + ASSERT_TRUE(!s); +} + +TEST(Strong, xorAdd) +{ + Flags<Strong> s(Strong::NoLog | Strong::NoCheck); + + s ^= Strong::NoCheck; + ASSERT_TRUE(s & Strong::NoLog); + ASSERT_TRUE((s & Strong::NoLog) == Strong::NoLog); + ASSERT_FALSE(s & Strong::NoCheck); + ASSERT_FALSE((s & Strong::NoCheck) == Strong::NoCheck); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} \ No newline at end of file
--- a/CMakeLists.txt Sun Oct 05 13:13:08 2014 +0200 +++ b/CMakeLists.txt Thu Oct 09 13:28:24 2014 +0200 @@ -50,6 +50,7 @@ option(WITH_DIRECTORY "Enable directory tests" On) option(WITH_DRIVER "Enable SQL drivers tests" On) option(WITH_DYNLIB "Enable DynLib tests" On) +option(WITH_FLAGS "Enable Flags tests" On) option(WITH_HASH "Enable hash functions tests" On) option(WITH_LUAE "Enable Luae tests" On) option(WITH_OPTIONPARSER "Enable option parser tests" On) @@ -76,6 +77,10 @@ add_subdirectory(C++/Tests/DynLib) endif () +if (WITH_FLAGS) + add_subdirectory(C++/Tests/Flags) +endif () + if (WITH_HASH) add_subdirectory(C++/Tests/Hash) endif ()