xbox-live-api/Source/Shared/enum_traits.h

174 строки
4.8 KiB
C++

// Copyright (c) Microsoft Corporation
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#pragma once
#define DEFAULT_ENUM_MIN 0u
#define DEFAULT_ENUM_MAX 30u
#define ENUM_RANGE_MAX 1000u
NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_BEGIN
namespace detail
{
template<size_t n>
class StaticString
{
public:
DISABLE_WARNING_PUSH;
SUPPRESS_WARNING_UNINITIALIZED_MEMBER;
constexpr StaticString(const char* name) noexcept
: StaticString{ name, std::make_index_sequence<n>{} }
{
}
DISABLE_WARNING_POP;
constexpr operator const char*() const noexcept
{
return chars;
}
private:
template<std::size_t... I>
constexpr StaticString(const char* name, std::index_sequence<I...>) noexcept
: chars{ name[I]..., 0 }
{
}
const char chars[n + 1]{};
};
template<>
class StaticString<0>
{
public:
constexpr StaticString(const char*) noexcept {};
constexpr operator const char*() const noexcept
{
return nullptr;
}
};
using StringView = std::pair<const char*, size_t>;
constexpr StringView ParseName(const char* funcName) noexcept
{
#if defined(_MSC_VER)
constexpr auto delim{ '>' };
#elif defined(__clang__)
constexpr auto delim{ ']' };
#endif
size_t end{ 0 };
for (; funcName[end] && funcName[end] != delim; ++end) {}
size_t begin{ end };
for (; begin > 0; --begin)
{
if (!((funcName[begin - 1] >= '0' && funcName[begin - 1] <= '9') ||
(funcName[begin - 1] >= 'a' && funcName[begin - 1] <= 'z') ||
(funcName[begin - 1] >= 'A' && funcName[begin - 1] <= 'Z') ||
(funcName[begin - 1] == '_')))
{
break;
}
}
// Symbol names cannot begin with a number
if (funcName[begin] >= '0' && funcName[begin] <= '9')
{
// Invalid enum value
return StringView{ nullptr, 0 };
}
return StringView{ funcName + begin, end - begin };
}
template<typename E, E V>
static constexpr auto N() noexcept
{
static_assert(std::is_enum<E>::value, "E must be an enum type.");
#if defined(_MSC_VER)
constexpr auto name = ParseName(__FUNCSIG__);
#elif defined(__clang__)
constexpr auto name = ParseName(__PRETTY_FUNCTION__);
#else
static_assert(false, "Unrecognized platform");
#endif
return StaticString<name.second>{ name.first };
}
template<typename E, E V>
constexpr auto EnumValueName = N<E, V>();
template<typename E, uint32_t MIN_E, uint32_t MAX_E>
class EnumTraits
{
private:
static_assert(std::is_enum<E>::value, "EnumTraits can only be instantiated for enum types.");
static_assert(std::is_same<std::underlying_type_t<E>, uint32_t>::value, "EnumTraits can only be instantiated for enums with uint32_t as their underlying type");
static constexpr uint32_t range = MAX_E - MIN_E + 1;
static_assert(range < ENUM_RANGE_MAX, "EnumTraits only supports a maximum value range of ENUM_RANGE_MAX");
template<std::size_t... I>
static constexpr auto Names(std::index_sequence<I...>) noexcept
{
return std::array<const char*, sizeof...(I)>{ { EnumValueName<E, static_cast<E>(I + MIN_E)>... } };
}
static constexpr std::array<const char*, range> names = Names(std::make_index_sequence<range>());
public:
static String Name(E v) noexcept
{
#if HC_PLATFORM == HC_PLATFORM_ANDROID
// This gets around the ODR-use rule on clang when compiling without C++17 support
constexpr auto names_{ names };
#else
auto& names_{ names };
#endif
auto i{ static_cast<size_t>(v) };
if (i < MIN_E || i > MAX_E)
{
// Value outside the specified range. Return empty string.
return String{};
}
return String{ names_[i - MIN_E] };
}
static E Value(const char* n) noexcept
{
#if HC_PLATFORM == HC_PLATFORM_ANDROID
constexpr auto names_{ names };
#else
auto& names_{ names };
#endif
for (size_t i = 0; i < names_.size(); ++i)
{
if (names_[i] && xbox::services::legacy::Stricmp(n, names_[i]) == 0)
{
return static_cast<E>(i + MIN_E);
}
}
// Provided string didn't map to an enum value. Return default value.
return E{};
}
};
}
template<typename E, size_t MIN_E = DEFAULT_ENUM_MIN, size_t MAX_E = DEFAULT_ENUM_MAX>
constexpr auto EnumName(E value) noexcept -> std::enable_if_t<std::is_enum<E>::value, String>
{
return detail::EnumTraits<E, MIN_E, MAX_E>::Name(value);
}
template<typename E, size_t MIN_E = DEFAULT_ENUM_MIN, size_t MAX_E = DEFAULT_ENUM_MAX>
constexpr auto EnumValue(const char* name) noexcept -> std::enable_if_t<std::is_enum<E>::value, E>
{
return detail::EnumTraits<E, MIN_E, MAX_E>::Value(name);
}
NAMESPACE_MICROSOFT_XBOX_SERVICES_CPP_END