From 646632e43af96153249aa902c9c9a0bcee8688e4 Mon Sep 17 00:00:00 2001 From: Piotr Kosek Date: Fri, 31 Aug 2018 16:42:57 +0200 Subject: [PATCH] Added indexable properties on enums: - _to_index which return value 0... size-1 (even if enums are note sequential) - _from_index used for other way around - _from_index_nothrow no throw version - _from_index_unchecked returns invalid enum in case arg>=size Code and test cases added. No documentation updates (my English is too poor for that) --- enum.h | 55 ++++++++++++++++++++++++ test/cxxtest/general.h | 97 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) diff --git a/enum.h b/enum.h index ab5ea18..b24932f 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 : static_cast(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_ _integral _to_index() const; \ + BETTER_ENUMS_IF_EXCEPTIONS( \ + BETTER_ENUMS_CONSTEXPR_ static Enum _from_index(_integral value); \ + ) \ + BETTER_ENUMS_CONSTEXPR_ static Enum \ + _from_index_unchecked(_integral value); \ + BETTER_ENUMS_CONSTEXPR_ static _optional \ + _from_index_nothrow(_integral value); \ \ ToStringConstexpr const char* _to_string() const; \ BETTER_ENUMS_IF_EXCEPTIONS( \ @@ -661,6 +676,8 @@ class Enum { \ BETTER_ENUMS_CONSTEXPR_ static _optional_index \ _from_value_loop(_integral value, std::size_t index = 0); \ BETTER_ENUMS_CONSTEXPR_ static _optional_index \ + _from_index_loop(_integral value, std::size_t index = 0); \ + BETTER_ENUMS_CONSTEXPR_ static _optional_index \ _from_string_loop(const char *name, std::size_t index = 0); \ BETTER_ENUMS_CONSTEXPR_ static _optional_index \ _from_string_nocase_loop(const char *name, std::size_t index = 0); \ @@ -700,6 +717,17 @@ Enum::_from_value_loop(Enum::_integral value, std::size_t index) \ _from_value_loop(value, index + 1); \ } \ \ +BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional \ +Enum::_from_index_loop(Enum::_integral value, std::size_t index) \ +{ \ + return \ + index == _size() ? \ + _optional() : \ + BETTER_ENUMS_NS(Enum)::index == value ? \ + _optional(_value_array[index]) : \ + _from_value_loop(value, index + 1); \ +} \ + \ BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional_index \ Enum::_from_string_loop(const char *name, std::size_t index) \ { \ @@ -727,6 +755,33 @@ BETTER_ENUMS_CONSTEXPR_ inline Enum::_integral Enum::_to_integral() const \ return _integral(_value); \ } \ \ +BETTER_ENUMS_CONSTEXPR_ inline Enum::_integral Enum::_to_index() const \ +{ \ + return _from_value_loop(value) \ +} \ + \ +BETTER_ENUMS_CONSTEXPR_ inline Enum \ +Enum::_from_index_unchecked(_integral value) \ +{ \ + return \ + ::better_enums::_or_zero(_from_index_nothrow(value)); \ +} \ + \ +BETTER_ENUMS_CONSTEXPR_ inline Enum::_optional \ +Enum::_from_index_nothrow(_integral value) \ +{ \ + return _from_index_loop(value); \ +} \ + \ +BETTER_ENUMS_IF_EXCEPTIONS( \ +BETTER_ENUMS_CONSTEXPR_ inline Enum Enum::_from_index(_integral value) \ +{ \ + return \ + ::better_enums::_or_throw(_from_index_nothrow(value), \ + #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 caa5414..808c73a 100644 --- a/test/cxxtest/general.h +++ b/test/cxxtest/general.h @@ -313,6 +313,103 @@ 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); + } + + void test_from_index() + { + TS_ASSERT_EQUALS((+Channel::Red)), Depth::_from_index(0)); + TS_ASSERT_EQUALS((+Channel::Green), Depth::_from_index(1)); + TS_ASSERT_EQUALS((+Channel::Blue), Depth::_from_index(1)); + 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(0); + TS_ASSERT(maybe_channel); + TS_ASSERT_EQUALS(*maybe_channel, +Channel::Red); + + maybe_channel = Channel::_from_index(1); + TS_ASSERT(maybe_channel); + TS_ASSERT_EQUALS(*maybe_channel, +Channel::Green); + + maybe_channel = Channel::_from_index(2); + TS_ASSERT(maybe_channel); + TS_ASSERT_EQUALS(*maybe_channel, +Channel::Blue); + TS_ASSERT(!Channel::_from_index(45)); + + better_enums::optional maybe_depth = Depth::_from_index(0); + TS_ASSERT(maybe_depth); + TS_ASSERT_EQUALS(*maybe_depth, +Depth::HighColor); + + maybe_depth = Depth::_from_index(1); + TS_ASSERT(maybe_depth); + TS_ASSERT_EQUALS(*maybe_depth, +Depth::TrueColor); + TS_ASSERT(!Channel::_from_index(45)); + + better_enums::optional maybe_depth = Depth::_from_index(0); + TS_ASSERT(maybe_depth); + TS_ASSERT_EQUALS(*maybe_depth, +Depth::HighColor); + + maybe_depth = Depth::_from_index(1); + TS_ASSERT(maybe_depth); + TS_ASSERT_EQUALS(*maybe_depth, +Depth::TrueColor); + TS_ASSERT(!Channel::_from_index(45)); + + better_enums::optional maybe_depth = Depth::_from_index(0); + TS_ASSERT(maybe_depth); + TS_ASSERT_EQUALS(*maybe_depth, +Depth::HighColor); + + better_enums::optional maybe_compression = Compression::_from_index(0); + TS_ASSERT(maybe_compression); + TS_ASSERT_EQUALS(*maybe_compression, +Compression::None); + + maybe_compression = Compression::_from_index(1); + TS_ASSERT(maybe_compression); + TS_ASSERT_EQUALS(*maybe_compression, +Compression::Huffman); + + maybe_compression = Compression::_from_index(2); + TS_ASSERT(maybe_compression); + TS_ASSERT_EQUALS(*maybe_compression, +Compression::Default); + TS_ASSERT(!Compression::_from_index(45)); + } + + 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)); + } };