Subscript operator for iterables and tests for constexpr iterators.

This commit is contained in:
Anton Bachin 2015-05-17 22:13:44 -05:00
parent 14bc834f7d
commit 6c60edcc46
4 changed files with 94 additions and 1 deletions

2
enum.h
View File

@ -346,6 +346,8 @@ struct _Iterable {
constexpr iterator begin() const { return iterator(_array); } constexpr iterator begin() const { return iterator(_array); }
constexpr iterator end() const { return iterator(_array + _size); } constexpr iterator end() const { return iterator(_array + _size); }
constexpr size_t size() const { return _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) : constexpr _Iterable(const Element *array, size_t size) :
_array(array), _size(size) { }; _array(array), _size(size) { };

View File

@ -33,6 +33,7 @@ constexpr bool should_be_invalid_3 = Channel::_is_valid_nocase("reed");
// _names and _values collections and iterators. // _names and _values collections and iterators.
constexpr Channel channel_5 = *(Channel::_values.begin() + 1); constexpr Channel channel_5 = *(Channel::_values.begin() + 1);
constexpr const char *name_through_iterator = *(Channel::_names.begin() + 1); constexpr const char *name_through_iterator = *(Channel::_names.begin() + 1);
constexpr const char *name_through_subscript = Channel::_names[2];
// Range properties. // Range properties.
constexpr Channel channel_6 = Channel::_max; 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 trimmed name: " << channel_1_name << std::endl;
std::cout << "constexpr name through iterator: " std::cout << "constexpr name through iterator: "
<< name_through_iterator << std::endl; << name_through_iterator << std::endl;
std::cout << "constexpr name through suscript: "
<< name_through_subscript << std::endl;
std::cout << "span: " << span << std::endl; std::cout << "span: " << span << std::endl;
std::cout << "type name: " << name << std::endl; std::cout << "type name: " << name << std::endl;

View File

@ -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 <cassert>
#include <cstring>
#include <iostream>
#include <enum.h>
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 <typename Enum>
constexpr size_t total_names_length(size_t accumulator = 0, size_t index = 0)
{
return
index == Enum::_size ? accumulator :
total_names_length<Enum>
(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 <typename Enum>
constexpr size_t declaration_length()
{
return
4 + 1 + string_length(Enum::_name) + 1 + 1 + 3 + 1 + 1 +
total_names_length<Enum>() + (Enum::_size - 1) * 2 + 1 + 1 + 1;
}
// Formats the declaration into space already reserved.
template <typename Enum>
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<Enum>() - 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<Channel>()];
char depth_declaration[declaration_length<Depth>()];
int main()
{
format_declaration<Channel>(channel_declaration);
std::cout << channel_declaration << std::endl;
format_declaration<Depth>(depth_declaration);
std::cout << depth_declaration << std::endl;
return 0;
}

View File

@ -128,7 +128,13 @@ static_assert_1(!Channel::_is_valid_nocase("greeen"));
// Iterables. // Iterables.
static_assert_1(Channel::_values.size() == Channel::_size); static_assert_1(Channel::_values.size() == Channel::_size);
static_assert_1(*Channel::_values.begin() == Channel::_first); 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); 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() void test_string_conversions()
{ {
TS_ASSERT_EQUALS(strcmp((+Channel::Green).to_string(), "Green"), 0); 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("green"), std::runtime_error);
TS_ASSERT_THROWS(Channel::_from_string_nocase("a"), std::runtime_error); TS_ASSERT_THROWS(Channel::_from_string_nocase("a"), std::runtime_error);