Skip to content

Instantly share code, notes, and snippets.

@dpwright
Created August 18, 2015 01:27
Show Gist options
  • Save dpwright/28102279045a3c8859e8 to your computer and use it in GitHub Desktop.
Save dpwright/28102279045a3c8859e8 to your computer and use it in GitHub Desktop.
Experimenting with type-safe features of enum classes in C++ 11
// 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