Skip to content

Instantly share code, notes, and snippets.

@Trass3r
Last active November 7, 2022 13:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Trass3r/6fae8f543863a8853226d8c086efe7b1 to your computer and use it in GitHub Desktop.
Save Trass3r/6fae8f543863a8853226d8c086efe7b1 to your computer and use it in GitHub Desktop.
proper enum flags based on conventional "Flags" suffix (try with -Wall -W -Og -std=c++17 / -std:c++17 -O1)
#pragma once
/*!
* Copyright (C) 2022 Andreas Hollandt
*
* Distributed under the Boost Software License, Version 1.0.
* See copy at http://boost.org/LICENSE_1_0.txt.
*/
// requires clang 3.6, msvc 2017 or gcc 9 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66639)
#include <type_traits>
#include <stddef.h>
namespace detail
{
// using own type to avoid the include and various constexpr limitations or missing methods like ends_with
struct StringView
{
const char* data = "";
size_t size = 0;
constexpr StringView() = default;
template <size_t N>
constexpr StringView(const char (&str)[N]) { data = str; size = N-1; }
template <size_t N>
constexpr void operator=(const char (&str)[N]) { data = str; size = N-1; }
constexpr size_t find(char c)
{
for (size_t i = 0; i < size; ++i)
if (data[i] == c)
return i;
return ~0u;
}
constexpr void remove_prefix(size_t n)
{
if (n > size)
n = size;
data += n;
size -= n;
}
constexpr void remove_suffix(size_t n)
{
if (n > size)
n = size;
size -= n;
}
constexpr int compare(size_t pos, StringView str) const
{
for (size_t i = 0; i < str.size; ++i)
if (data[pos + i] != str.data[i])
return 1;
return 0;
}
constexpr bool operator==(StringView s) const
{ return size == s.size && compare(0, s) == 0; }
constexpr bool ends_with(StringView suffix) const
{
return size >= suffix.size && compare(size - suffix.size, suffix) == 0;
}
};
template <typename T>
constexpr auto typeName_()
{
StringView name;
#if defined(__clang__) || defined(__GNUC__)
name = __PRETTY_FUNCTION__; // "constexpr auto typeName() [with T = ...]"
name.remove_prefix(name.find('=') + 2);
name.remove_suffix(sizeof(']'));
#elif defined(_MSC_VER)
name = __FUNCSIG__; // "auto __cdecl typeName<...>(void)"
name.remove_prefix(name.find('<') + 1);
name.remove_suffix(sizeof(">(void)")-1);
#endif
return name;
}
template <typename T>
constexpr StringView typeName = typeName_<T>();
static_assert(typeName<StringView>.ends_with("StringView"), "fail");
template <auto E>
constexpr auto enumName_()
{
StringView name;
#if defined(__clang__) || defined(__GNUC__)
name = __PRETTY_FUNCTION__; // "constexpr auto enumName() [with auto E = ...]"
// utest::TestFlags::C vs (utest::TestFlags)3
name.remove_prefix(name.find('=') + 2);
name.remove_suffix(sizeof(']'));
#elif defined(_MSC_VER)
name = __FUNCSIG__; // "auto __cdecl enumName<...>(void)"
// utest::TestFlags::C vs. (enum utest::TestFlags)0x3
name.remove_prefix(name.find('<') + 1);
name.remove_suffix(sizeof(">(void)")-1);
#endif
return name;
}
template <auto T>
constexpr StringView enumName = enumName_<T>();
}
template <typename T>
using is_scoped_enum = std::integral_constant<bool, std::is_enum<std::decay_t<T>>::value && !std::is_convertible<T, int>::value>;
template <typename T>
constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value;
template <typename T, typename =
std::enable_if_t<is_scoped_enum_v<T>>>
constexpr auto castToBaseType(T t)
{
return std::underlying_type_t<T>(t);
}
template <typename T>
constexpr bool is_flags_enum = is_scoped_enum_v<T> && detail::typeName<std::decay_t<T>>.ends_with("Flags");
template <typename T, typename =
std::enable_if_t<is_flags_enum<T>>>
constexpr T operator|(T lhs, T rhs)
{
return (T)(castToBaseType(lhs) | castToBaseType(rhs));
}
template <typename T, typename =
std::enable_if_t<is_flags_enum<T>>>
constexpr T operator&(T lhs, T rhs)
{
return (T)(castToBaseType(lhs) & castToBaseType(rhs));
}
template <typename T, typename =
std::enable_if_t<is_flags_enum<T>>>
constexpr bool operator!(T lhs)
{
return !castToBaseType(lhs);
}
namespace utest
{
enum class TestScoped : unsigned long long {};
enum TestUnScoped {};
enum class TestFlags { A, B, C };
static_assert(is_scoped_enum_v<TestScoped>, "fail");
static_assert(is_scoped_enum_v<const TestScoped&>, "fail");
static_assert(!is_scoped_enum_v<TestUnScoped>, "fail");
static_assert(!is_scoped_enum_v<TestUnScoped&&>, "fail");
static_assert(!is_flags_enum<TestScoped>, "fail");
static_assert(!is_flags_enum<const TestScoped>, "fail");
static_assert(is_flags_enum<TestFlags>, "fail");
static_assert(is_flags_enum<const TestFlags&>, "fail");
static_assert(castToBaseType(TestFlags::A & TestFlags::B) == 0, "fail");
static_assert(!(TestFlags::A & TestFlags::B), "fail");
static_assert(!!(TestFlags::A | TestFlags::B), "fail");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment