diff --git a/enum.h b/enum.h index 2b1ce63..1366e4b 100644 --- a/enum.h +++ b/enum.h @@ -346,6 +346,8 @@ struct _Iterable { constexpr iterator begin() const { return iterator(_array); } constexpr iterator end() const { return iterator(_array + _size); } constexpr size_t size() const { return _size; } + constexpr const Element& operator [](size_t index) const + { return _array[index]; } constexpr _Iterable(const Element *array, size_t size) : _array(array), _size(size) { }; diff --git a/example/4-constexpr.cc b/example/4-constexpr.cc index 9796dc3..22cc5bb 100644 --- a/example/4-constexpr.cc +++ b/example/4-constexpr.cc @@ -33,6 +33,7 @@ constexpr bool should_be_invalid_3 = Channel::_is_valid_nocase("reed"); // _names and _values collections and iterators. constexpr Channel channel_5 = *(Channel::_values.begin() + 1); constexpr const char *name_through_iterator = *(Channel::_names.begin() + 1); +constexpr const char *name_through_subscript = Channel::_names[2]; // Range properties. constexpr Channel channel_6 = Channel::_max; @@ -89,6 +90,8 @@ int main() std::cout << "constexpr trimmed name: " << channel_1_name << std::endl; std::cout << "constexpr name through iterator: " << name_through_iterator << std::endl; + std::cout << "constexpr name through suscript: " + << name_through_subscript << std::endl; std::cout << "span: " << span << std::endl; std::cout << "type name: " << name << std::endl; diff --git a/example/8-constexpr-iterate.cc b/example/8-constexpr-iterate.cc new file mode 100644 index 0000000..c48a5e3 --- /dev/null +++ b/example/8-constexpr-iterate.cc @@ -0,0 +1,81 @@ +// Compile-time iteration. This example generates an approximation of enum +// declarations (without explicit "="" settings) at run time. The storage space +// for this is reserved at compile time, however. + +#include +#include +#include +#include + +ENUM(Channel, int, Red, Green, Blue); +ENUM(Depth, int, TrueColor, HighColor); + +// Computes the length of a string. +constexpr size_t string_length(const char *s, size_t index = 0) +{ + return s[index] == '\0' ? index : string_length(s, index + 1); +} + +// Runs over all the constants in an enum and adds up the lengths of their +// names. +template +constexpr size_t total_names_length(size_t accumulator = 0, size_t index = 0) +{ + return + index == Enum::_size ? accumulator : + total_names_length + (accumulator + string_length(Enum::_names[index]), index + 1); +} + +// Computes the total length of an ENUM declaration, assuming the type is int. +// The summands correspond to each of the tokens and spaces (i.e., "ENUM", "(", +// etc.). (Enum::_size - 1) * 2 is added to account for each comma and space +// following each constant name, except for the last one. The final 1 is added +// to account for the null terminator. +template +constexpr size_t declaration_length() +{ + return + 4 + 1 + string_length(Enum::_name) + 1 + 1 + 3 + 1 + 1 + + total_names_length() + (Enum::_size - 1) * 2 + 1 + 1 + 1; +} + +// Formats the declaration into space already reserved. +template +void format_declaration(char *storage) +{ + std::strcat(storage, "ENUM("); + std::strcat(storage, Enum::_name); + std::strcat(storage, ", int, "); + + for (auto name_iterator = Enum::_names.begin(); + name_iterator < Enum::_names.end() - 1; ++name_iterator) { + + std::strcat(storage, *name_iterator); + std::strcat(storage, ", "); + } + std::strcat(storage, (+Enum::_last).to_string()); + + std::strcat(storage, ");"); + + assert(std::strlen(storage) == declaration_length() - 1); +} + +// Reserve space for the formatted declaration of each enum. These buffers +// should be zeroed at load time or during code generation, so, semantically, +// they contain the empty string. +char channel_declaration[declaration_length()]; +char depth_declaration[declaration_length()]; + + + +int main() +{ + format_declaration(channel_declaration); + std::cout << channel_declaration << std::endl; + + format_declaration(depth_declaration); + std::cout << depth_declaration << std::endl; + + return 0; +} diff --git a/test/cxxtest/tests.h b/test/cxxtest/tests.h index adb15a2..17a0041 100644 --- a/test/cxxtest/tests.h +++ b/test/cxxtest/tests.h @@ -128,7 +128,13 @@ static_assert_1(!Channel::_is_valid_nocase("greeen")); // Iterables. static_assert_1(Channel::_values.size() == Channel::_size); static_assert_1(*Channel::_values.begin() == Channel::_first); +static_assert_1(*(Channel::_values.begin() + 1) == Channel::Green); +static_assert_1(*(Channel::_values.begin() + 2) == Channel::Blue); +static_assert_1(Channel::_values[1] == Channel::Green); +static_assert_1(Channel::_values[2] == Channel::Blue); static_assert_1(Channel::_names.size() == Channel::_size); +// The next one is a little janky, but actually the pointers should be the same. +static_assert_1(*Channel::_names.begin() == (+Channel::_first).to_string()); @@ -149,7 +155,8 @@ class EnumTests : public CxxTest::TestSuite { void test_string_conversions() { TS_ASSERT_EQUALS(strcmp((+Channel::Green).to_string(), "Green"), 0); - TS_ASSERT_EQUALS(strcmp((+Channel::Blue).to_string(), "Blue"), 0); + TS_ASSERT_EQUALS(strcmp((+Depth::HighColor).to_string(), + "HighColor"), 0); TS_ASSERT_THROWS(Channel::_from_string("green"), std::runtime_error); TS_ASSERT_THROWS(Channel::_from_string_nocase("a"), std::runtime_error);