vendredi 6 mars 2015

Template for type safe c++11 enum class flags

I am trying to create type safe C++ flags using templates. I also want to distinguish between a flag and flags (being zero, one or many flags).


The solution below works good, except for EnumFlag<T> operator | (T, T) which causes all |-operations on enums to return type EnumFlag. This breaks lots of code. Any trick fix this? In my code I do the following, however hard coding Option here is not an option. How to make this generic?



EnumFlag<typename std::enable_if<std::is_same<T, Option>::value, T>::type> operator | (T l, T r)


Changing this to...



EnumFlag<T> operator | (T l, T r)


...of cause breaks everything. I would like something like this (not compilabel code). Or any other better idea!



EnumFlag<typename std::enable_if<std::already_expanded<EnumFlag<T>>::value, T>::type> operator | (T l, T r)


Complete compilable code:


EnumFlag.h



#ifndef __classwith_flags_h_
#define __classwith_flags_h_

#include <type_traits>

enum class Option
{
PrintHi = 1 << 0,
PrintYo = 1 << 1,
PrintAlot = 1 << 2
};

template <typename T>
class EnumFlag
{
public:
using UnderlayingType = typename std::underlying_type<T>::type;

EnumFlag(const T& flags)
: m_flags(static_cast<UnderlayingType>(flags))
{}

bool operator & (T r) const
{
return 0 != (m_flags & static_cast<UnderlayingType>(r));
}

static const T NoFlag = static_cast<T>(0);

private:
UnderlayingType m_flags;
};
template<typename T>
EnumFlag<typename std::enable_if<std::is_same<T, Option>::value, T>::type> operator | (T l, T r)
{
return static_cast<T>(static_cast<EnumFlag<T>::UnderlayingType>(l) | static_cast<EnumFlag<T>::UnderlayingType>(r));
}

class ClassWithFlags
{
public:
using Options = EnumFlag < Option >;

void doIt(const Options &options);
};

#endif


EnumFlag.cpp



#include "EnumFlag.h"

#include <iostream>

void ClassWithFlags::doIt(const Options &options)
{
if (options & Option::PrintHi)
{
std::cout << "Hi" << std::endl;
}
if (options & Option::PrintYo)
{
std::cout << "Yo!" << std::endl;
}
}

int main()
{
ClassWithFlags classWithFlags;
classWithFlags.doIt(Option::PrintHi | Option::PrintAlot);
}


The actual code will contain a lot more operators, however this is enough to illustrate the problem.


One less but still intrusive solution is this



template<typename T>
typename std::underlying_type<T>::type operator | (T l, T r)
{
return (static_cast<typename std::underlying_type<T>::type>(l) | static_cast<typename std::underlying_type<T>::type>(r));
}


Not god enough, then EnumFlag(const std::underlying_type<T> &flags) must exist and I lose type safty. Also, I would like for the global operator overloads to only be created for the types actually needed. Macros is also no god because I want ta allow declaration of EnumFlags inside classes. The global overloads can not be there, hence I need two macros calls at different locations to create on EnumFlag.


The solution must be pure C++11/stl.


Aucun commentaire:

Enregistrer un commentaire