diff --git a/enum.h b/enum.h index ab5ea18a..2034824c 100644 --- a/enum.h +++ b/enum.h @@ -343,6 +343,12 @@ BETTER_ENUMS_CONSTEXPR_ static T* _or_null(optional maybe) return maybe ? *maybe : BETTER_ENUMS_NULLPTR; } +template +BETTER_ENUMS_CONSTEXPR_ static T _or_zero(optional maybe) +{ + return maybe ? *maybe : T::_from_integral_unchecked(0); +} + // Functional sequencing. This is essentially a comma operator wrapped in a @@ -615,6 +621,15 @@ class Enum { \ _from_integral_unchecked(_integral value); \ BETTER_ENUMS_CONSTEXPR_ static _optional \ _from_integral_nothrow(_integral value); \ + \ + BETTER_ENUMS_CONSTEXPR_ std::size_t _to_index() const; \ + BETTER_ENUMS_IF_EXCEPTIONS( \ + BETTER_ENUMS_CONSTEXPR_ static Enum _from_index(std::size_t value); \ + ) \ + BETTER_ENUMS_CONSTEXPR_ static Enum \ + _from_index_unchecked(std::size_t value); \ + BETTER_ENUMS_CONSTEXPR_ static _optional \ + _from_index_nothrow(std::size_t value); \ \ ToStringConstexpr const char* _to_string() const; \ BETTER_ENUMS_IF_EXCEPTIONS( \ @@ -727,6 +742,36 @@ BETTER_ENUMS_CONSTEXPR_ inline Enum::_integral Enum::_to_integral() const \ return _integral(_value); \ } \ \ +BETTER_ENUMS_CONSTEXPR_ inline std::size_t Enum::_to_index() const \ +{ \ + return *_from_value_loop(_value); \ +} \ + \ +BETTER_ENUMS_CONSTEXPR_ inline Enum \ +Enum::_from_index_unchecked(std::size_t index) \ +{ \ + return \ + ::better_enums::_or_zero(_from_index_nothrow(index)); \ +} \ + \ +BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional \ +Enum::_from_index_nothrow(std::size_t index) \ +{ \ + return \ + index >= _size() ? \ + _optional() : \ + _optional(BETTER_ENUMS_NS(Enum)::_value_array[index]); \ +} \ + \ +BETTER_ENUMS_IF_EXCEPTIONS( \ +BETTER_ENUMS_CONSTEXPR_ inline Enum Enum::_from_index(std::size_t index) \ +{ \ + return \ + ::better_enums::_or_throw(_from_index_nothrow(index), \ + #Enum "::_from_index: invalid argument"); \ +} \ +) \ + \ BETTER_ENUMS_CONSTEXPR_ inline Enum \ Enum::_from_integral_unchecked(_integral value) \ { \ diff --git a/test/cxxtest/general.h b/test/cxxtest/general.h index caa54146..7dbef050 100644 --- a/test/cxxtest/general.h +++ b/test/cxxtest/general.h @@ -313,6 +313,96 @@ class EnumTests : public CxxTest::TestSuite { +test::Namespaced::One); TS_ASSERT_EQUALS(strcmp(*test::Namespaced::_names().begin(), "One"), 0); } + + void test_to_index() + { + TS_ASSERT_EQUALS((+Channel::Red)._to_index(), 0); + TS_ASSERT_EQUALS((+Channel::Green)._to_index(), 1); + TS_ASSERT_EQUALS((+Channel::Blue)._to_index(), 2); + + TS_ASSERT_EQUALS((+Depth::HighColor)._to_index(), 0); + TS_ASSERT_EQUALS((+Depth::TrueColor)._to_index(), 1); + + TS_ASSERT_EQUALS((+Compression::None)._to_index(), 0); + TS_ASSERT_EQUALS((+Compression::Huffman)._to_index(), 1); +// TS_ASSERT_EQUALS((+Compression::Default)._to_index(), 2); // This won't pass as Compression::Huffman == Compression::Default + } + + void test_from_index() + { + TS_ASSERT_EQUALS((+Channel::Red), Channel::_from_index(0)); + TS_ASSERT_EQUALS((+Channel::Green), Channel::_from_index(1)); + TS_ASSERT_EQUALS((+Channel::Blue), Channel::_from_index(2)); + TS_ASSERT_THROWS(Channel::_from_index(42), std::runtime_error); + + TS_ASSERT_EQUALS((+Depth::HighColor), Depth::_from_index(0)); + TS_ASSERT_EQUALS((+Depth::TrueColor), Depth::_from_index(1)); + TS_ASSERT_THROWS(Depth::_from_index(42), std::runtime_error); + + TS_ASSERT_EQUALS((+Compression::None), Compression::_from_index(0)); + TS_ASSERT_EQUALS((+Compression::Huffman), Compression::_from_index(1)); + TS_ASSERT_EQUALS((+Compression::Default), Compression::_from_index(2)); + TS_ASSERT_THROWS(Compression::_from_index(42), std::runtime_error); + } + + void test_from_index_nothrow() + { + better_enums::optional maybe_channel = Channel::_from_index_nothrow(0); + TS_ASSERT(maybe_channel); + TS_ASSERT_EQUALS(*maybe_channel, +Channel::Red); + + maybe_channel = Channel::_from_index_nothrow(1); + TS_ASSERT(maybe_channel); + TS_ASSERT_EQUALS(*maybe_channel, +Channel::Green); + + maybe_channel = Channel::_from_index_nothrow(2); + TS_ASSERT(maybe_channel); + TS_ASSERT_EQUALS(*maybe_channel, +Channel::Blue); + + maybe_channel = Channel::_from_index_nothrow(45); + TS_ASSERT(!maybe_channel); + + better_enums::optional maybe_depth = Depth::_from_index_nothrow(0); + TS_ASSERT(maybe_depth); + TS_ASSERT_EQUALS(*maybe_depth, +Depth::HighColor); + + maybe_depth = Depth::_from_index_nothrow(1); + TS_ASSERT(maybe_depth); + TS_ASSERT_EQUALS(*maybe_depth, +Depth::TrueColor); + + maybe_depth = Depth::_from_index_nothrow(45); + TS_ASSERT(!maybe_depth); + + better_enums::optional maybe_compression = Compression::_from_index_nothrow(0); + TS_ASSERT(maybe_compression); + TS_ASSERT_EQUALS(*maybe_compression, +Compression::None); + + maybe_compression = Compression::_from_index_nothrow(1); + TS_ASSERT(maybe_compression); + TS_ASSERT_EQUALS(*maybe_compression, +Compression::Huffman); + + maybe_compression = Compression::_from_index_nothrow(2); + TS_ASSERT(maybe_compression); + TS_ASSERT_EQUALS(*maybe_compression, +Compression::Default); + + maybe_compression = Compression::_from_index_nothrow(45); + TS_ASSERT(!maybe_compression); + } + + void test_from_index_unchecked() + { + + TS_ASSERT_EQUALS((+Channel::Red), Channel::_from_index_unchecked(0)); + TS_ASSERT_EQUALS((+Channel::Green), Channel::_from_index_unchecked(1)); + TS_ASSERT_EQUALS((+Channel::Blue), Channel::_from_index_unchecked(2)); + + TS_ASSERT_EQUALS((+Depth::HighColor), Depth::_from_index_unchecked(0)); + TS_ASSERT_EQUALS((+Depth::TrueColor), Depth::_from_index_unchecked(1)); + + TS_ASSERT_EQUALS((+Compression::None), Compression::_from_index_unchecked(0)); + TS_ASSERT_EQUALS((+Compression::Huffman), Compression::_from_index_unchecked(1)); + TS_ASSERT_EQUALS((+Compression::Default), Compression::_from_index_unchecked(2)); + } };