Created
August 18, 2015 01:27
-
-
Save dpwright/28102279045a3c8859e8 to your computer and use it in GitHub Desktop.
Experimenting with type-safe features of enum classes in C++ 11
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Tested with clang version: Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn) | |
// clang++ --std=c++11 enum-classes.cpp | |
#include <iostream> | |
#include <cassert> | |
#include <array> | |
enum class MaleNames { | |
JACK, | |
JOHN, | |
ALEX, | |
DAVE, | |
COUNT | |
}; | |
enum class FemaleNames { | |
SARAH, | |
JILL, | |
CAROLYN, | |
SAMANTHA, | |
ALEX, | |
COUNT | |
}; | |
// This weird define is to try and give an idea of what life would be like if | |
// you didn't have to cast to int. Try and filter it out in your mind ;-) | |
#define _(x) (size_t)(x) | |
void problems() | |
{ | |
// Some arrays | |
std::string MaleNameStrings[_(MaleNames::COUNT)] = { "Jack", "John", "Alex", "Dave" }; | |
std::string FemaleNameStrings[_(FemaleNames::COUNT)] = { "Sarah", "Jill", "Carolyn", "Samantha", "Alex" }; | |
// Uh-oh! Array overflow | |
std::cout << "Hello! My name is " << MaleNameStrings[_(FemaleNames::ALEX)] << std::endl; | |
// This was intended to map to the MaleNames enum, but I accidentally used | |
// FemaleNames::COUNT, so it has an extra entry at the end which is undefined. | |
bool namesWhichStartWithJ[_(FemaleNames::COUNT)] = { true, true, false, false }; | |
} | |
//Let's assume a convention that all Enums we're going to use as indexers have | |
//a member COUNT at the end giving the number of entries... | |
template<typename T, typename Indexer> | |
class IndexedArray : std::array<T, (size_t)Indexer::COUNT> | |
{ | |
typedef std::array<T, (size_t)Indexer::COUNT> super; | |
public: | |
/** Basic interface **/ | |
IndexedArray(std::initializer_list<T> il) { | |
// Annoyingly, il.size() is not a constexpr in C++ 11 so this won't work. | |
// This is apparently going to be fixed in C++ 14. Maybe some compilers have fixed it already? | |
// See http://en.cppreference.com/w/cpp/utility/initializer_list/size | |
// static_assert(il.size() == super::size(), "Array not initialized with the right number of elements"); | |
// Putting the check at runtime sort of defeats the object :-( | |
assert(il.size() == super::size() && "Array not initialized with the right number of elements"); | |
// Copy the elements | |
std::copy(il.begin(), il.end(), super::begin()); | |
} | |
// These are still casting under the hood, but the interface is type-safe | |
T& operator[](Indexer i) { return super::at((size_t)i); } | |
const T& operator[](Indexer i) const { return super::at((size_t)i); } | |
/** Comment the following out if you want to be more strict. **/ | |
// Allow creating an uninitialized array | |
// (so you can force initialization of all elements by commenting this out) | |
IndexedArray() : super() {} | |
// Allow accessing elements by position rather than the enum value | |
// (fragile if the order changes) | |
using super::operator[]; | |
/** If we were doing this for real, we'd expose std::array<>'s iterator, operator==, etc here **/ | |
}; | |
void solutions() | |
{ | |
IndexedArray<std::string, MaleNames> MaleNameStrings = { "Jack", "John", "Alex", "Dave" }; | |
IndexedArray<std::string, FemaleNames> FemaleNameStrings = { "Sarah", "Jill", "Carolyn", "Samantha", "Alex" }; | |
// Now this fails to compile with: | |
// error: no viable overloaded operator[] for type 'IndexedArray<std::string, MaleNames>' | |
// note: candidate function not viable: no known conversion from 'FemaleNames' to 'MaleNames' for 1st argument | |
//std::cout << "Hello! My name is " << MaleNameStrings[FemaleNames::ALEX] << std::endl; | |
// Working version | |
std::cout << "Hello! My name is " << MaleNameStrings[MaleNames::ALEX] << std::endl; | |
// This will now trigger a runtime assert, which would be a compile-time assert if we could | |
// use static_assert as we will be able to in C++ 14 (See the note in IndexedArray's constructor) | |
//IndexedArray<bool, FemaleNames> namesWhichStartWithJ = { true, true, false, false }; | |
// Working version | |
IndexedArray<bool, MaleNames> namesWhichStartWithJ = { true, true, false, false }; | |
// Access elements by index, C-style (commenting out the "using super::operator[]" prevents this) | |
std::cout << "The third male name is " << MaleNameStrings[2] << std::endl; | |
std::cout << "The third female name is " << FemaleNameStrings[2] << std::endl; | |
// Create an uninitialized array (commenting out the default constructor prevents this) | |
IndexedArray<std::string, MaleNames> UninitialisedNameStrings; | |
} | |
int main(int argc, char**argv) | |
{ | |
problems(); | |
solutions(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment