Skip to content

Commit

Permalink
Add LAF_ENUM_FLAGS() macro
Browse files Browse the repository at this point in the history
  • Loading branch information
dacap committed Sep 5, 2024
1 parent fc919ec commit 6abf784
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 0 deletions.
67 changes: 67 additions & 0 deletions base/enum_flags.h
Original file line number Diff line number Diff line change
@@ -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 <type_traits>

// 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<T>; \
return static_cast<T>(static_cast<U>(a) | \
static_cast<U>(b)); \
} \
\
constexpr inline T operator&(const T a, const T b) { \
using U = std::underlying_type_t<T>; \
return static_cast<T>(static_cast<U>(a) & \
static_cast<U>(b)); \
} \
\
constexpr inline T operator^(const T a, const T b) { \
using U = std::underlying_type_t<T>; \
return static_cast<T>(static_cast<U>(a) ^ \
static_cast<U>(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
53 changes: 53 additions & 0 deletions base/enum_flags_tests.cpp
Original file line number Diff line number Diff line change
@@ -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 <gtest/gtest.h>

#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();
}

0 comments on commit 6abf784

Please sign in to comment.