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 ()