From 6abf784cd9a131c85cab016ddbc5fa22199c9211 Mon Sep 17 00:00:00 2001 From: David Capello Date: Thu, 5 Sep 2024 18:29:26 -0300 Subject: [PATCH] Add LAF_ENUM_FLAGS() macro --- base/enum_flags.h | 67 +++++++++++++++++++++++++++++++++++++++ base/enum_flags_tests.cpp | 53 +++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 base/enum_flags.h create mode 100644 base/enum_flags_tests.cpp diff --git a/base/enum_flags.h b/base/enum_flags.h new file mode 100644 index 000000000..388e9e1a7 --- /dev/null +++ b/base/enum_flags.h @@ -0,0 +1,67 @@ +// LAF Base Library +// Copyright (C) 2024 Igara Studio S.A. +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifndef BASE_ENUM_FLAGS_H_INCLUDED +#define BASE_ENUM_FLAGS_H_INCLUDED +#pragma once + +#include + +// When C++0x was being redacted, I was expecting that the final C++11 +// was going to introduce something similar to the C# [Flags] +// attribute, but that never happened. I tried some approaches in the +// past even to emulate C++11 enums in the C++03 era (enclosing the +// values in its own struct/namespace): +// +// https://github.com/dacap/vaca/blob/main/vaca/Enum.h +// +// But now that we have "class enum" to define the type and values in +// its own scope/namespace, here is a straightforward approach: we +// just define the "enum class" and then use a macro to define all +// required operators: +// +// enum class Name { A=1, B=2, C=4 }; +// LAF_ENUM_FLAGS(Name); +// +// There are libraries that do something similar like enum-flags: +// +// https://github.com/grisumbras/enum-flags +// +#define LAF_ENUM_FLAGS(T) \ + constexpr inline T operator|(const T a, const T b) { \ + using U = std::underlying_type_t; \ + return static_cast(static_cast(a) | \ + static_cast(b)); \ + } \ + \ + constexpr inline T operator&(const T a, const T b) { \ + using U = std::underlying_type_t; \ + return static_cast(static_cast(a) & \ + static_cast(b)); \ + } \ + \ + constexpr inline T operator^(const T a, const T b) { \ + using U = std::underlying_type_t; \ + return static_cast(static_cast(a) ^ \ + static_cast(b)); \ + } \ + \ + constexpr inline T& operator|=(T& a, const T b) { \ + a = a | b; \ + return a; \ + } \ + \ + constexpr inline T& operator&=(T& a, const T b) { \ + a = a & b; \ + return a; \ + } \ + \ + constexpr inline T& operator^=(T& a, const T b) { \ + a = a ^ b; \ + return a; \ + } + +#endif diff --git a/base/enum_flags_tests.cpp b/base/enum_flags_tests.cpp new file mode 100644 index 000000000..27547dbe2 --- /dev/null +++ b/base/enum_flags_tests.cpp @@ -0,0 +1,53 @@ +// LAF Base Library +// Copyright (C) 2024 Igara Studio S.A. +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#include + +#include "base/enum_flags.h" + +#include "base/ints.h" + +enum class U8 : uint8_t { A=1, B=2, C=4 }; +enum class U32 : uint32_t { A=1, B=256 }; + +LAF_ENUM_FLAGS(U8); +LAF_ENUM_FLAGS(U32); + +TEST(EnumFlags, Uint8) +{ + U8 a = { }; + EXPECT_EQ(U8(0), a); + + a |= U8::A; EXPECT_EQ(U8::A, a); + a |= U8::B; EXPECT_EQ(U8::A | U8::B, a); + + EXPECT_EQ(U8::B, a & U8::B); + EXPECT_EQ(U8(0), a & U8::C); + EXPECT_EQ(U8::A | U8::B, a & (U8::A | U8::B)); + + a &= U8::B; + EXPECT_EQ(U8::B, a); + + a ^= U8::C; + EXPECT_EQ(U8::B | U8::C, a); +} + +TEST(EnumFlags, Conversion) +{ + U32 a = U32::A | U32::B; + EXPECT_EQ(U32::A | U32::B, a); + + EXPECT_EQ(uint32_t(U8::A), uint32_t(U32::A)); + + U8 b = U8(a); + EXPECT_EQ(U8::A, b); // U32::B is lost in uint8_t +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}