improved support for using enums with value_or()

This commit is contained in:
Mark Gillard 2023-09-25 18:50:34 +03:00
parent 42a428f8ea
commit 941341fce6
5 changed files with 119 additions and 24 deletions

View File

@ -33,6 +33,10 @@ template:
- fixed `for_each()` compilation error on GCC <= 7 (#197) (@sagi-ottopia, @damirbarr)
- fixed `FLT_RADIX` check getting broken by Intel MKL headers (#202) (@iago-lito)
#### Additions
- improved support for using enums with `value_or()`
#### Changes:
- renamed header files to have `.hpp` extension (`toml.h` is still present for backwards-compatibility)

View File

@ -476,10 +476,22 @@ TOML_IMPL_NAMESPACE_START
template <typename T, typename... U>
inline constexpr bool first_is_same<T, T, U...> = true;
template <typename T, bool = std::is_enum_v<T>>
struct underlying_type_
{
using type = std::underlying_type_t<T>;
};
template <typename T>
struct underlying_type_<T, false>
{
using type = T;
};
template <typename T>
using underlying_type = typename underlying_type_<T>::type;
// general value traits
// (as they relate to their equivalent native TOML type)
template <typename T>
struct value_traits
struct default_value_traits
{
using native_type = void;
static constexpr bool is_native = false;
@ -489,6 +501,27 @@ TOML_IMPL_NAMESPACE_START
static constexpr auto type = node_type::none;
};
template <typename T>
struct value_traits;
template <typename T, bool = std::is_enum_v<T>>
struct value_traits_base_selector
{
static_assert(!is_cvref<T>);
using type = default_value_traits;
};
template <typename T>
struct value_traits_base_selector<T, true>
{
static_assert(!is_cvref<T>);
using type = value_traits<underlying_type<T>>;
};
template <typename T>
struct value_traits : value_traits_base_selector<T>::type
{};
template <typename T>
struct value_traits<const T> : value_traits<T>
{};
@ -509,32 +542,35 @@ TOML_IMPL_NAMESPACE_START
template <typename T>
struct integer_limits
{
static constexpr auto min = (std::numeric_limits<T>::min)();
static constexpr auto max = (std::numeric_limits<T>::max)();
static constexpr T min = T{ (std::numeric_limits<underlying_type<T>>::min)() };
static constexpr T max = T{ (std::numeric_limits<underlying_type<T>>::max)() };
};
template <typename T>
struct integer_traits_base : integer_limits<T>
{
using native_type = int64_t;
static constexpr bool is_native = std::is_same_v<T, native_type>;
static constexpr bool is_signed = static_cast<T>(-1) < T{}; // for impls not specializing std::is_signed<T>
static constexpr bool is_native = std::is_same_v<underlying_type<T>, native_type>;
static constexpr bool is_signed = static_cast<underlying_type<T>>(-1) < underlying_type<T>{};
static constexpr auto type = node_type::integer;
static constexpr bool can_partially_represent_native = true;
};
template <typename T>
struct unsigned_integer_traits : integer_traits_base<T>
{
static constexpr bool is_losslessly_convertible_to_native = integer_limits<T>::max <= 9223372036854775807ULL;
static constexpr bool can_represent_native = false;
static constexpr bool is_losslessly_convertible_to_native =
integer_limits<underlying_type<T>>::max <= 9223372036854775807ULL;
static constexpr bool can_represent_native = false;
};
template <typename T>
struct signed_integer_traits : integer_traits_base<T>
{
using native_type = int64_t;
static constexpr bool is_losslessly_convertible_to_native =
integer_limits<T>::min >= (-9223372036854775807LL - 1LL) && integer_limits<T>::max <= 9223372036854775807LL;
integer_limits<underlying_type<T>>::min >= (-9223372036854775807LL - 1LL)
&& integer_limits<underlying_type<T>>::max <= 9223372036854775807LL;
static constexpr bool can_represent_native =
integer_limits<T>::min <= (-9223372036854775807LL - 1LL) && integer_limits<T>::max >= 9223372036854775807LL;
integer_limits<underlying_type<T>>::min <= (-9223372036854775807LL - 1LL)
&& integer_limits<underlying_type<T>>::max >= 9223372036854775807LL;
};
template <typename T, bool S = integer_traits_base<T>::is_signed>
struct integer_traits : signed_integer_traits<T>

View File

@ -59,7 +59,7 @@ TOML_DISABLE_ARITHMETIC_WARNINGS;
TOML_SA_LIST_END \
\
TOML_SA_LIST_NXT "A non-view type capable of (reasonably) representing a native TOML value type" \
TOML_SA_LIST_BEG "any other integer type" \
TOML_SA_LIST_BEG "any other integral type" \
TOML_SA_LIST_SEP "any floating-point type" \
TOML_SA_LIST_END \
\
@ -1221,7 +1221,7 @@ TOML_NAMESPACE_START
TOML_SA_LIST_END
TOML_SA_LIST_NXT "A non-view type capable of (reasonably) representing a native TOML value type"
TOML_SA_LIST_BEG "any other integer type"
TOML_SA_LIST_BEG "any other integral type"
TOML_SA_LIST_SEP "any floating-point type"
TOML_SA_LIST_END

View File

@ -393,4 +393,23 @@ b = []
y = 2
)"sv);
}
SECTION("tomlplusplus/issues/207") // https://github.com/marzer/tomlplusplus/issues/207
{
enum class an_enum
{
zero,
one,
two,
three
};
parsing_should_succeed(FILE_LINE_ARGS,
"val = 2\n",
[](auto&& tbl)
{
const auto val = tbl["val"].template value_or<an_enum>(an_enum::zero);
CHECK(val == an_enum::two);
});
}
}

View File

@ -1742,10 +1742,22 @@ TOML_IMPL_NAMESPACE_START
template <typename T, typename... U>
inline constexpr bool first_is_same<T, T, U...> = true;
template <typename T, bool = std::is_enum_v<T>>
struct underlying_type_
{
using type = std::underlying_type_t<T>;
};
template <typename T>
struct underlying_type_<T, false>
{
using type = T;
};
template <typename T>
using underlying_type = typename underlying_type_<T>::type;
// general value traits
// (as they relate to their equivalent native TOML type)
template <typename T>
struct value_traits
struct default_value_traits
{
using native_type = void;
static constexpr bool is_native = false;
@ -1755,6 +1767,27 @@ TOML_IMPL_NAMESPACE_START
static constexpr auto type = node_type::none;
};
template <typename T>
struct value_traits;
template <typename T, bool = std::is_enum_v<T>>
struct value_traits_base_selector
{
static_assert(!is_cvref<T>);
using type = default_value_traits;
};
template <typename T>
struct value_traits_base_selector<T, true>
{
static_assert(!is_cvref<T>);
using type = value_traits<underlying_type<T>>;
};
template <typename T>
struct value_traits : value_traits_base_selector<T>::type
{};
template <typename T>
struct value_traits<const T> : value_traits<T>
{};
@ -1775,32 +1808,35 @@ TOML_IMPL_NAMESPACE_START
template <typename T>
struct integer_limits
{
static constexpr auto min = (std::numeric_limits<T>::min)();
static constexpr auto max = (std::numeric_limits<T>::max)();
static constexpr T min = T{ (std::numeric_limits<underlying_type<T>>::min)() };
static constexpr T max = T{ (std::numeric_limits<underlying_type<T>>::max)() };
};
template <typename T>
struct integer_traits_base : integer_limits<T>
{
using native_type = int64_t;
static constexpr bool is_native = std::is_same_v<T, native_type>;
static constexpr bool is_signed = static_cast<T>(-1) < T{}; // for impls not specializing std::is_signed<T>
static constexpr bool is_native = std::is_same_v<underlying_type<T>, native_type>;
static constexpr bool is_signed = static_cast<underlying_type<T>>(-1) < underlying_type<T>{};
static constexpr auto type = node_type::integer;
static constexpr bool can_partially_represent_native = true;
};
template <typename T>
struct unsigned_integer_traits : integer_traits_base<T>
{
static constexpr bool is_losslessly_convertible_to_native = integer_limits<T>::max <= 9223372036854775807ULL;
static constexpr bool can_represent_native = false;
static constexpr bool is_losslessly_convertible_to_native =
integer_limits<underlying_type<T>>::max <= 9223372036854775807ULL;
static constexpr bool can_represent_native = false;
};
template <typename T>
struct signed_integer_traits : integer_traits_base<T>
{
using native_type = int64_t;
static constexpr bool is_losslessly_convertible_to_native =
integer_limits<T>::min >= (-9223372036854775807LL - 1LL) && integer_limits<T>::max <= 9223372036854775807LL;
integer_limits<underlying_type<T>>::min >= (-9223372036854775807LL - 1LL)
&& integer_limits<underlying_type<T>>::max <= 9223372036854775807LL;
static constexpr bool can_represent_native =
integer_limits<T>::min <= (-9223372036854775807LL - 1LL) && integer_limits<T>::max >= 9223372036854775807LL;
integer_limits<underlying_type<T>>::min <= (-9223372036854775807LL - 1LL)
&& integer_limits<underlying_type<T>>::max >= 9223372036854775807LL;
};
template <typename T, bool S = integer_traits_base<T>::is_signed>
struct integer_traits : signed_integer_traits<T>
@ -4765,7 +4801,7 @@ TOML_DISABLE_ARITHMETIC_WARNINGS;
TOML_SA_LIST_END \
\
TOML_SA_LIST_NXT "A non-view type capable of (reasonably) representing a native TOML value type" \
TOML_SA_LIST_BEG "any other integer type" \
TOML_SA_LIST_BEG "any other integral type" \
TOML_SA_LIST_SEP "any floating-point type" \
TOML_SA_LIST_END \
\
@ -5743,7 +5779,7 @@ TOML_NAMESPACE_START
TOML_SA_LIST_END
TOML_SA_LIST_NXT "A non-view type capable of (reasonably) representing a native TOML value type"
TOML_SA_LIST_BEG "any other integer type"
TOML_SA_LIST_BEG "any other integral type"
TOML_SA_LIST_SEP "any floating-point type"
TOML_SA_LIST_END