diff --git a/.gitignore b/.gitignore index 7e4e99d..50d1ca9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.pyc +samples/*.exe scratch/ diff --git a/samples/1-basic.cc b/samples/1-basic.cc new file mode 100644 index 0000000..f16be45 --- /dev/null +++ b/samples/1-basic.cc @@ -0,0 +1,89 @@ +// Basic conversions to/from strings and the underlying integral type. + +#include +#include + +ENUM(Channel, uint8_t, Red, Green = 2, Blue, Alias = Red); + +void print_channel(Channel channel) +{ + std::cout + << "channel \'" + << channel.to_string() + << "\' has value " + << (int)channel.to_integral() << std::endl; +} + +int main() +{ + // A value must be assigned upon construction. + Channel channel = Channel::Green; + print_channel(channel); + + // This will not work. + // Channel default_constructed_channel; + + + + // Conversions from strings and the integral type. Function names are + // prefixed with _ to avoid conflicts with constant names. _from_integral is + // a checked cast. + channel = Channel::_from_integral(0); + print_channel(channel); + + channel = Channel::_from_string("Blue"); + print_channel(channel); + + channel = Channel::_from_string_nocase("bluE"); + print_channel(channel); + + + + // Bad conversions. + try { + channel = Channel::_from_integral(15); + throw std::logic_error("expected an exception"); + } + catch (const std::runtime_error &e) { } + + try { + channel = Channel::_from_string("Purple"); + throw std::logic_error("expected an exception"); + } + catch (const std::runtime_error &e) { } + + try { + channel = Channel::_from_string_nocase("bluee"); + throw std::logic_error("expected an exception"); + } + catch (const std::runtime_error &e) { } + + + + // Unsafe unchecked cast. + channel = Channel::_from_integral_unchecked(2); + + + + // Direct conversion of a constant unfortunately requires an explicit + // promotion. + std::cout << (+Channel::Green).to_string() << std::endl; + + + + // The type name is available as a string. + std::cout << Channel::_name << std::endl; + + + + return 0; +} + +static_assert(sizeof(Channel) == sizeof(uint8_t), + "enum has the same size as its underlying integral type"); + +static_assert(alignof(Channel) == alignof(uint8_t), + "enum has the same alignment as its underlying integral type"); + +static_assert(std::is_same(), + "the underlying integral type is accessible as a member"); diff --git a/samples/2-iterate.cc b/samples/2-iterate.cc new file mode 100644 index 0000000..c58ca53 --- /dev/null +++ b/samples/2-iterate.cc @@ -0,0 +1,39 @@ +// Range properties and iteration over all constants. + +#include +#include + +ENUM(Channel, int, Red = 3, Green = 4, Blue = 0); + +int main() +{ + // Static range properties. + std::cout << "first: " << (+Channel::_first).to_string() << std::endl; + std::cout << "last: " << (+Channel::_last).to_string() << std::endl; + std::cout << "minimum: " << (+Channel::_min).to_string() << std::endl; + std::cout << "maximum: " << (+Channel::_max).to_string() << std::endl; + std::cout << "count: " << Channel::_size << std::endl; + std::cout << "span: " << Channel::_span << std::endl; + + + + // Listing declared values. + for (Channel channel : Channel::_values) + std::cout << channel.to_integral() << " "; + std::cout << std::endl; + + // Listing declared names. + for (const char *name : Channel::_names) + std::cout << name << " "; + std::cout << std::endl; + + + + // Direct iterator usage. + std::cout + << "first (using iterator): " + << *Channel::_names.begin() + << std::endl; + + return 0; +} diff --git a/samples/3-switch.cc b/samples/3-switch.cc new file mode 100644 index 0000000..7913b5b --- /dev/null +++ b/samples/3-switch.cc @@ -0,0 +1,38 @@ +// Switch case exhaustiveness checking. + +#include +#include + +ENUM(Channel, int, Red, Green, Blue); + +void respond_to_channel(Channel channel) +{ + // Try adding an extra case or removing one. Your compiler should issue a + // warning. + switch (channel) { + case Channel::Red: + std::cout << "red channel" << std::endl; + break; + + case Channel::Green: + std::cout << "green channel" << std::endl; + break; + + case Channel::Blue: + std::cout << "blue channel" << std::endl; + break; + + // A redundant case. + // case 3: + // break; + } +} + +int main() +{ + respond_to_channel(Channel::Red); + respond_to_channel(Channel::Blue); + respond_to_channel(Channel::Green); + + return 0; +} diff --git a/samples/4-constexpr.cc b/samples/4-constexpr.cc new file mode 100644 index 0000000..9cbed71 --- /dev/null +++ b/samples/4-constexpr.cc @@ -0,0 +1,92 @@ +// Usage in constexpr expressions. All members of an ENUM are constexpr when +// given constant arguments, with the exception of _to_string and dereferencing +// the _names iterator. + +#include +#include + +ENUM(Channel, int, Red, Green, Blue); + +// Initialization. +constexpr Channel channel_1 = Channel::Green; + +constexpr Channel channel_4 = Channel::_from_integral(2); + +constexpr Channel channel_2 = Channel::_from_string("Blue"); +constexpr Channel channel_3 = Channel::_from_string_nocase("gReEn"); + +// Conversion to integer (but not to string). +constexpr int channel_1_representation = channel_1.to_integral(); + +// Validity checks (including against strings). +constexpr bool should_be_valid_1 = Channel::_is_valid(2); +constexpr bool should_be_invalid_1 = Channel::_is_valid(42); + +constexpr bool should_be_valid_2 = Channel::_is_valid("Red"); +constexpr bool should_be_invalid_2 = Channel::_is_valid("red"); + +constexpr bool should_be_valid_3 = Channel::_is_valid_nocase("red"); +constexpr bool should_be_invalid_3 = Channel::_is_valid_nocase("reed"); + +// _names and _values collections and iterator creation. +constexpr Channel channel_5 = *Channel::_values.begin(); +constexpr auto name_iterator = Channel::_names.begin(); + +// Range properties. +constexpr Channel channel_6 = Channel::_max; +constexpr size_t span = Channel::_span; + +// Type name. +constexpr auto name = Channel::_name; + +// Explicit promotion. +constexpr int converted = (+Channel::Green).to_integral(); + + + +// The above, printed for verification. +void print_channel(int number, Channel channel) +{ + std::cout + << "channel_" + << number + << " is " + << channel.to_string() + << std::endl; +} + +#define PRINT(n) print_channel(n, channel_ ## n) + +void print_validity(bool expected, bool actual) +{ + std::cout + << "should be " + << expected + << ": " + << actual + << std::endl; +} + +int main() +{ + PRINT(1); + PRINT(2); + PRINT(3); + PRINT(4); + + print_validity(true, should_be_valid_1); + print_validity(false, should_be_invalid_1); + print_validity(true, should_be_valid_2); + print_validity(false, should_be_invalid_2); + print_validity(true, should_be_valid_3); + print_validity(false, should_be_invalid_3); + + PRINT(5); + PRINT(6); + + std::cout << *name_iterator << std::endl; + std::cout << "span: " << span << std::endl; + std::cout << "type name: " << name << std::endl; + + return 0; +} diff --git a/samples/5-containers.cc b/samples/5-containers.cc new file mode 100644 index 0000000..aa0236b --- /dev/null +++ b/samples/5-containers.cc @@ -0,0 +1,62 @@ +// Usage with STL containers. + +#include +#include +#include + +#include + +ENUM(Channel, int, Red, Green, Blue); + +int main() +{ + // Vectors. + std::vector vector = {Channel::Red, Channel::Green}; + + vector.push_back(Channel::Red); + vector.push_back(Channel::Blue); + vector.push_back(Channel::Blue); + vector.push_back(Channel::Red); + + for (Channel channel : vector) + std::cout << channel.to_string() << " "; + std::cout << std::endl; + + + + // Maps. Lack of a default constructor in the current version means that + // std::map::operator[] usage is complicated. Insertion can still be done + // with ::insert, and access with ::find. + std::map map = {{"first", Channel::Blue}}; + map.insert({"second", Channel::Green}); + + for (Channel channel : Channel::_values) + map.insert({channel.to_string(), channel}); + + bool first = true; + for (auto item : map) { + if (first) + first = false; + else + std::cout << ", "; + + std::cout + << item.first + << " -> " + << item.second.to_string(); + } + std::cout << std::endl; + + + + // As map keys. + std::map descriptions = + {{Channel::Red, "the red channel"}, + {Channel::Green, "the green channel"}, + {Channel::Blue, "the blue channel"}}; + + for (auto item : descriptions) + std::cout << item.second << std::endl; + + return 0; +} diff --git a/samples/Makefile b/samples/Makefile new file mode 100644 index 0000000..e8e72aa --- /dev/null +++ b/samples/Makefile @@ -0,0 +1,27 @@ +SOURCES := $(wildcard *.cc) +BINARIES := $(SOURCES:.cc=.exe) + +CXX := c++ +CXXFLAGS := -std=c++11 -Wall -I .. + +.PHONY : default +default : run + @: + +.PHONY : all +all : $(BINARIES) + +%.exe : %.cc ../*.h Makefile + $(CXX) $(CXXFLAGS) -o $@ $< + +.PHONY : clean +clean : + rm -rf *.exe + +.PHONY : run +run : all + @for BINARY in $(BINARIES) ; \ + do \ + echo ./$$BINARY ; \ + ./$$BINARY | sed -e "s/^/ /" ; \ + done