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(); +} diff --git a/os/osx/dnd.mm b/os/osx/dnd.mm index f65afae33..f7e6b4a38 100644 --- a/os/osx/dnd.mm +++ b/os/osx/dnd.mm @@ -72,7 +72,7 @@ NSDragOperation as_nsdragoperation(const os::DropOperation op) { - NSDragOperation nsdop; + NSDragOperation nsdop = NSDragOperationNone; if (static_cast(op) & static_cast(os::DropOperation::Copy)) nsdop |= NSDragOperationCopy; @@ -87,7 +87,7 @@ NSDragOperation as_nsdragoperation(const os::DropOperation op) os::DropOperation as_dropoperation(const NSDragOperation nsdop) { - int op = 0; + int op = static_cast(os::DropOperation::None); if (nsdop & NSDragOperationCopy) op |= static_cast(os::DropOperation::Copy); diff --git a/os/win/dnd.cpp b/os/win/dnd.cpp index 4f3b8002f..5af81407e 100644 --- a/os/win/dnd.cpp +++ b/os/win/dnd.cpp @@ -33,7 +33,7 @@ DWORD as_dropeffect(const os::DropOperation op) os::DropOperation as_dropoperation(DWORD pdwEffect) { - int op = 0; + int op = static_cast(os::DropOperation::None); if (pdwEffect & DROPEFFECT_COPY) op |= static_cast(os::DropOperation::Copy);