Serializing vulkan.hpp enumerator with cereal, how do I do it? - c++

The vulan enums in the hpp class use a template wrapper to define bitwise operations to guarantee safety.
It looks roughly like this:
template<typename BitType> class Flags
{
public:
using MaskType = typename std::underlying_type<BitType>::type;
// constructors
constexpr Flags()
: m_mask(0)
{
}
constexpr Flags(BitType bit)
: m_mask(static_cast<MaskType>(bit))
{
}
constexpr Flags(Flags<BitType> const& rhs)
: m_mask(rhs.m_mask)
{
}
constexpr Flags(MaskType flags)
: m_mask(flags)
{
}
auto operator<=>(Flags<BitType> const&) const = default;
// logical operator
constexpr bool operator!() const { return !m_mask; }
// bitwise operators
constexpr Flags<BitType> operator&(Flags<BitType> const& rhs) const
{
return Flags<BitType>(m_mask & rhs.m_mask);
}
constexpr Flags<BitType> operator|(Flags<BitType> const& rhs) const
{
return Flags<BitType>(m_mask | rhs.m_mask);
}
constexpr Flags<BitType> operator^(Flags<BitType> const& rhs) const
{
return Flags<BitType>(m_mask ^ rhs.m_mask);
}
// assignment operators
constexpr Flags<BitType>& operator=(Flags<BitType> const& rhs)
{
m_mask = rhs.m_mask;
return *this;
}
constexpr Flags<BitType>& operator|=(Flags<BitType> const& rhs)
{
m_mask |= rhs.m_mask;
return *this;
}
constexpr Flags<BitType>& operator&=(Flags<BitType> const& rhs)
{
m_mask &= rhs.m_mask;
return *this;
}
constexpr Flags<BitType>& operator^=(Flags<BitType> const& rhs)
{
m_mask ^= rhs.m_mask;
return *this;
}
// Arithmetic operators
constexpr Flags<BitType>& operator*=(Flags<BitType> const& rhs)
{
m_mask *= rhs.m_mask;
return *this;
}
constexpr Flags<BitType>& operator+=(Flags<BitType> const& rhs)
{
m_mask += rhs.m_mask;
return *this;
}
constexpr Flags<BitType>& operator*(Flags<BitType> const& rhs)
{
return {m_mask * rhs.m_mask};
}
constexpr Flags<BitType>& operator+(Flags<BitType> const& rhs)
{
return {m_mask + rhs.m_mask};
}
// cast operators
explicit constexpr operator bool() const { return !!m_mask; }
explicit constexpr operator MaskType() const { return m_mask; }
private:
MaskType m_mask;
};
The problem is that if you define a cereal function directly, this seems to lead cereal into a hellspcape that after minutes ends in a segfault.
i.e. this is bad:
// cereal function for serialization.
template <class Archive, typename BitType>
void serialize( Archive& archive, Flags<BitType>& tag )
{
archive(
tag
);
}
One way around is to define the masktype as public, then you can do:
// cereal function for serialization.
template <class Archive, typename BitType>
void serialize( Archive& archive, Flags<BitType>& tag )
{
archive(
tag.m_mask
);
}
This works fine but means you have to modify the vulkan.hpp header, which is a terrible idea.
Alternatively you can try to do this:
// cereal function for serialization.
template <class Archive, typename BitType>
void serialize( Archive& archive, Flags<BitType>& tag )
{
archive(
(BitType)tag
);
}
That doesn't end in a segfault but cereal does not load the correct values when I do this, in fact the serialized data is correct but when it gets loaded all teh vulkan enum fields are set to 0 instead of the stored value.
I am out fo ideas, how can get cereal to properly load this data?

Related

C++ cast template type

Consider the following code snippet :
[[nodiscard]] bool operator==(const BasicIterator<const Type>& rhs) const noexcept {
if( this == &rhs ) {
return true;
}
return node_ == rhs.node_;
}
[[nodiscard]] bool operator==(const BasicIterator<Type>& rhs) const noexcept {
// how to call existing operator== implementation ??
//return operator==( rhs );
}
How should I use the same implementation for both operator==? Is it possible to call operator== <const Type> version from operator== <Type>?
Is there any cast for template types in this case?
Is Type a template? If not, maybe make it a template?
template <typename T>
[[nodiscard]] bool operator==(const BasicIterator<T>& rhs) const noexcept {
if( this == &rhs ) {
return true;
}
return node_ == rhs.node_;
}
If you do not want to expose this template to users, hide it somewhere and use in implementation:
private:
template <typename T>
[[nodiscard]] bool operator==(const BasicIterator<T>& lhs, const BasicIterator<T>& rhs) const noexcept {
if( &lhs== &rhs ) {
return true;
}
return lhs.node_ == rhs.node_;
}
public:
[[nodiscard]] bool operator==(const BasicIterator<const Type>& rhs) const noexcept {
return operator==<const Type>(*this, rhs);
}
[[nodiscard]] bool operator==(const BasicIterator<Type>& rhs) const noexcept {
return operator==<Type>(*this, rhs);
}

How to overload the assignment operator for a class that inherits from a template in c++

I have a given utility template class tagged. I had to declare 2 new structs using these template classes as follows.
tagged.h
#ifndef TAGGED_H
#define TAGGED_H
#include <iostream>
template<typename T, typename TAG>
class tagged
{
private:
T _value;
public:
tagged() : _value() { }
explicit tagged(const T& value) : _value(value) { }
// https://isocpp.org/wiki/faq/templates#template-friends
friend T& value(tagged<T, TAG>& st)
{
return st._value;
}
friend const T& value(const tagged<T, TAG>& st)
{
return st._value;
}
};
template<typename T>
struct equality
{
friend bool operator ==(const T& x, const T& y)
{
return value(x) == value(y);
}
friend bool operator !=(const T& x, const T& y)
{
return value(x) != value(y);
}
};
template<typename T>
struct ordered : equality<T>
{
friend bool operator <(const T& x, const T& y)
{
return value(x) < value(y);
}
friend bool operator <=(const T& x, const T& y)
{
return value(x) <= value(y);
}
friend bool operator >(const T& x, const T& y)
{
return value(x) > value(y);
}
friend bool operator >=(const T& x, const T& y)
{
return value(x) >= value(y);
}
};
These are the two structs i declared following the rules given by the assignment.
primitives.h
//Time
struct __declspec(empty_bases)Time : tagged<uint64_t, Time>, ordered<Time>, show_value<Time, int>{ using tagged::tagged; };
//Duration
struct __declspec(empty_bases)Duration : tagged<uint64_t, Duration>, ordered<Duration>, show_value<Duration, int> { using tagged::tagged; };
I succeeded in writing all other operators like + and - but i cant seem to solve how to overload += and -= I'm not allowed to change the objects in tagged.h I know assignment operators can only be member functions. Because of the way the template works i've tried casting 'const Time&' and const Duration& to non consts but that didnt seem to work. I've tried the examples you can find online about assigment operator overloading but the examples all overload in the template and not in the inherited class where I barely have write access to '_value' which is the value I should overwrite of reassign the pointer of.
Thanks
edit:
struct __declspec(empty_bases)Time : tagged<uint64_t, Time>, ordered<Time>, show_value<Time, int>
{
using tagged::tagged;
Time& operator+(const Duration& right) {
Time t = Time(value(*this) + value(right));
return t;
};
Time& operator+=(const Duration& right) {
(uint64_t&)(*this) = value(*this) + value(right);
return (*this);
};
};
//Duration
struct __declspec(empty_bases)Duration : tagged<uint64_t, Duration>, ordered<Duration>, show_value<Duration, int> {
using tagged::tagged;
Duration& operator+(const Duration& right) {
Duration d = Duration(value(*this) + value(right));
return d;
};
Time& operator+(const Time & right) {
Time t = Time(value(*this) + value(right));
return t;
};
Duration& operator-(const Time & right) {
Duration d = Duration(value(*this) - value(right));
return d;
};
Duration& operator-(const Duration & right) {
Duration d = Duration(value(*this) - value(right));
return d;
};
Duration& operator+=(const Duration& right) {
(uint64_t&)(*this) = (uint64_t&)(*this) + (uint64_t&)(right);
return (*this);
}
Duration& operator-=(const Duration& right) {
(uint64_t&)(*this) = value(*this) - value(right);
return (*this);
};
};
This is what I have now. Still have the same syntax errors that keep popping up. I dont know anymore lmao
From what I see you should be able to implement it using value() function; Since value returns by reference something like this should work:
Duration & operator+=(const Duration & right) {
value(*this) += value( right );
return *this;
}
Just be careful on the other operators (I'm looking at + and -) because you return references to temporal objects.
Duration & operator+(const Duration & right) { // this returns a reference to an object
Duration d = Duration( value( *this ) + value( right ) ); // this creates a temporal variable
return d; // you return a reference to d bu its lifetime is over -> undefined behavior I believe
}

C++ Unordered Pair Type

I'd like to create an unordered pair type in c++, that is, an unordered_set guaranteed to have exactly two elements. Here's what I've come up with, but the problem is if I use this approach, there's a heck of a lot more I have to override - each of the comparison operators, and so on. Is there a simpler way?
class unordered_pair : public std::pair<t, u>
{
public:
unordered_pair(t x, u y) : std::pair<t, u>(x,y) {};
bool operator ==(const unordered_pair<t,u>& rhs)
{
if ((this->first < this->second) ^ (rhs.first < rhs.second))
{
return this->second == rhs.first && this->first == rhs.second;
}
else
{
return this->first == rhs.first && this->second == rhs.second;
}
}
};
I would do something like
struct unordered_pair : std::pair<t, u>
{
bool swapped;
unordered_pair(t x, u y) :
std::pair<t, u>(x,y),
swapped(false);
{
sort();
}
void sort() {
swapped = first > second;
if (swapped)
std::swap(first, second);
}
std::pair<t, u> getOrig() {
if (swapped)
return std::pair<t,u>(second, first);
return std::pair<t, u>(first, second);
}
}
Then you just call sort() every time you change first or second; and all the comparison operators are obtained from the std::pair for free!
The motivation is that if you don't care about the ordering for comparisons, then you won't care about the ordering most of the time; Which will mean most of the time, you won't need to get the original item.
Edit: You state in the comments that we can assume t==u ... in that case I would suggest getting rid of t or u - and make it just std::pair<t, t>
Let's fill in some types into this template (which you omitted the template, there is no declaration of t or u), and see what it tries to instantiate
unordered_pair<int, std::string> pair, pair2;
bool operator ==(const unordered_pair<t,u>& rhs)
{
if ((this->first < this->second) ^ (rhs.first < rhs.second))
This is bool operator <(int, std::string) and bool operator <(std::string, int). These don't exist.
{
return this->second == rhs.first && this->first == rhs.second;
}
This is bool operator ==(int, std::string) and bool operator ==(std::string, int). These also don't exist.
else
{
return this->first == rhs.first && this->second == rhs.second;
}
}
You instead want one template type parameter. Try something like this
class bad_unordered_pair : public std::exception
{
const char * what() const { return "unordered_pair must have exactly two distinct values"; }
}
template <typename T>
std::pair<T, T> make_unordered_pair(T first, T second)
{
std::hash<T> hash;
if (first == second) throw bad_unordered_pair{};
if (hash(first) < hash(second)) return unordered_pair(first, second);
return unordered_pair(second, first);
}
Aside: I'm assuming that you meant
template<typename t>
class unordered_pair : public std::pair<t, t>
since it doesn't make sense for the members to be of different types if they should be interchangeable.
You could write a simple sorted() method, to ease writing these overloads:
private:
std::tuple<t const&, t const&> ordered() const noexcept
{
return (this->first < this->second)
? std::tie(this->first, this->second)
: std::tie(this->second, this->first);
}
};
Then implement == and < using that:
bool operator ==(const unordered_pair<t>& rhs) const noexcept
{
return ordered() == rhs.ordered();
}
bool operator <(const unordered_pair<t>& rhs) const noexcept
{
return ordered() < rhs.ordered();
}
and the other operators in terms of those:
bool operator !=(const unordered_pair<t>& rhs) const noexcept
{
return !(*this == rhs);
}
bool operator >(const unordered_pair<t>& rhs) const noexcept
{
return rhs < *this;
}
bool operator <=(const unordered_pair<t>& rhs) const noexcept
{
return !(rhs < *this);
}
bool operator >=(const unordered_pair<t>& rhs) const noexcept
{
return !(*this < rhs);
}
Alternatively, if you have C++20, then implement <=> instead:
template<typename T>
class unordered_pair : public std::pair<T, T>
{
public:
unordered_pair(T x, T y)
: std::pair<T, T>(x,y)
{}
std::strong_ordering operator<=>(const unordered_pair<T>& other)
{
return ordered() <=> other.ordered();
}
private:
std::tuple<T const&, T const&> ordered() const noexcept
{
return (this->first < this->second)
? std::tie(this->first, this->second)
: std::tie(this->second, this->first);
}
};
Or even absorb the helper into the operator:
std::strong_ordering operator<=>(const unordered_pair<T>& other)
{
auto ordered = [](unordered_pair<T> const& t) {
return (t.first < t.second)
? std::tie(t.first, t.second)
: std::tie(t.second, t.first);
};
return ordered(*this) <=> ordered(other);
}
Whichever approach you take, you'll want to be careful not to compare through base-class pointers, or you'll get inconsistent behaviour.

Transparent comparator code minimization

Let's say we store a struct with a string key and we want to find it by that string in a container like std::set, so common implementation would look like this:
struct Foo {
std::string id;
};
struct FooComp {
using is_transparent = std::true_type;
bool operator()( const Foo &foo, const std::string &str ) const
{
return foo.id < str;
}
bool operator()( const std::string &str, const Foo &foo ) const
{
return str < foo.id;
}
bool operator()( const Foo &foo1, const Foo &foo2 ) const
{
return foo1.id < foo2.id;
}
};
std::set<Foo,FooComp> foo_set;
...
This works fine, but writing three methods for FooComp that do prety match the same (logically) is monotonic and error prone. Is there a way to minimize that code?
You may do as following:
struct Foo {
std::string id;
};
struct FooComp {
using is_transparent = std::true_type;
template <typename LHS, typename RHS>
bool operator()(const LHS& lhs, const RHS& rhs) const
{
return ProjectAsId(lhs) < ProjectAsId(rhs);
}
private:
const std::string& ProjectAsId(const std::string& s) const { return s; }
const std::string& ProjectAsId(const Foo& foo) const { return foo.id; }
};
You write comparison once, but you have to write the projection for each type.
In C++17, it can even be
template <auto f> struct ProjLess
{
using is_transparent = std::true_type;
template <typename LHS, typename RHS>
bool operator()(const LHS& lhs, const RHS& rhs) const
{
return project(lhs) < project(rhs);
}
private:
template <typename T>
using f_t = decltype(std::invoke(f, std::declval<const T&>()));
template <typename T>
using is_f_callable = is_detected<f_t, T>;
template <typename T, std::enable_if_t<is_f_callable<T>::value>* = nullptr>
decltype(auto) project(const T& t) const { return std::invoke(f, t); }
template <typename T, std::enable_if_t<!is_f_callable<T>::value>* = nullptr>
const T& project(const T& t) const { return t; }
};
And usage:
std::set<Foo, ProjLess<&Foo::id>> s;
Demo with C++17
My solution is all in the class:
struct FooComp {
using is_transparent = std::true_type;
struct FooProj {
std::string const& str;
FooProj( std::string const& sin ):str(sin) {}
FooProj( const Foo& foo ):str(foo.id) {}
FooProj( FooProj const& ) = default;
friend bool operator<(FooProj lhs, FooProj rhs) {
return lhs.str < rhs.str;
}
};
bool operator()( FooProj lhs, FooProj rhs ) const
{
return lhs<rhs;
}
};
This doesn't support types that can convert to std::string.
However, when doing a projection-based comparison, I do this:
template<class F, class After=std::less<>>
auto order_by( F&& f, After&& after={} ) {
return
[f=std::forward<F>(f), after=std::forward<After>(after)]
(auto&& rhs, auto&&lhs)->bool {
return after( f(decltype(lhs)(lhs)), f(decltype(rhs)(rhs)) );
};
}
which takes a projection and generates a comparison function for it. We make it transparent with:
template<class F>
struct as_transparent_t {
F f;
using is_transparent=std::true_type;
template<class Lhs, class Rhs>
bool operator(Lhs const& lhs, Rhs const& rhs)const{ return f(lhs, rhs); }
};
template<class F>
as_transparent_f<std::decay_t<F>>
as_transparent( F&& f ) { return {std::forward<F>(f)}; }
so we can project and be transparent via:
as_transparent( order_by( some_projection ) );
which only leaves the projection.
In C++14 we just do a
std::string const& foo_proj_f( std::string const& str ) { return str; }
std::string const& foo_proj_f( Foo const& foo ) { return foo.id; }
auto foo_proj = [](auto const& x)->decltype(auto){ return foo_proj_f(x); };
auto foo_order = as_transparent( order_by( foo_proj ) );
which breaks things down into modular chunks.
In C++17 we can use if constexpr:
auto foo_proj = [](auto const& x)->std::string const& {
if constexpr( std::is_same<decltype(x), std::string const&>{} ) {
return x;
}
if constexpr( std::is_same<decltype(x), Foo const&>{} ) {
return x.id;
}
};
auto foo_order = as_transparent( order_by( foo_proj ) );
or
template<class...Ts>
struct overloaded:Ts...{
using Ts::operator()...;
overloaded(Ts...ts):Ts(std::move(ts)...){}
};
template<class...Ts> overloaded -> overloaded<Ts...>;
which permits
auto foo_proj = overloaded{
[](std::string const& s)->decltype(auto){return s;},
[](Foo const& f)->decltype(auto){return f.id;}
};
which may be easier to read than the if constexpr version. (This version can also be adapted to c++14 or c++11).

why isn't my implicit ctor invoked in following

With the following template to try to make C++ 11/14's new class-enum work as-desired, I find that the following code doesn't even try to invoke the implicit ctor to use a nonmember template that would fit by VS2013 update 1:
BitTest is defined as:
template <typename enumT>
bool BitTest(const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs)
{
return (lhs & rhs);
}
testing code with anomalies:
enum class Flags { None = 0x00, CanChangeDataSources = 0x01, RequiresExclusiveAccess = 0x02 };
EnumeratedFlags<Flags> lhs(Flags::CanChangeDataSources);
// ... the following fails to even attempt to convert the Foo::Flags
if (BitTest(lhs, Flags::CanChangeDataSources)) { DoSomething(); }
// this compiles, but I don't know why it's necessary (and this is annoying and ugly)...
if (BitTest(lhs, EnumeratedFlags<Flags>(Flags::CanChangeDataSources))) { DoSomething(); }
Here is the template definition I am currently attempting to use:
template <typename enumT>
class EnumeratedFlags
{
public:
typedef enumT enum_type;
typedef typename std::underlying_type<enumT>::type store_type;
// constructors
EnumeratedFlags()
: m_bits(0)
{
}
EnumeratedFlags(enum_type flag)
: m_bits(static_cast<store_type>(flag))
{
}
explicit EnumeratedFlags(store_type value)
: m_bits(value)
{
}
EnumeratedFlags(const std::initializer_list<enum_type> & initializers)
: m_bits(0)
{
for (auto flag : initializers)
m_bits |= static_cast<store_type>(flag);
}
// operators
operator std::string () const
{
return to_string();
}
bool operator [] (enum_type flag) const
{
return test(flag);
}
store_type operator * () const
{
return m_bits;
}
operator bool () const
{
return m_bits != store_type(0);
}
// explicit accessors
store_type bits() const
{
return m_bits;
}
std::string to_string() const
{
std::string str(size(), '0');
for (size_t x = 0; x < size(); ++x)
str[size() - x - 1] = (m_bits & (1 << x) ? '1' : '0');
return str;
}
EnumeratedFlags & set(enum_type flag)
{
BitSet(m_bits, static_cast<store_type>(flag));
return *this;
}
EnumeratedFlags & set_if(enum_type flag, bool set_or_clear)
{
BitSetIf(m_bits, static_cast<store_type>(flag), set_or_clear);
return *this;
}
EnumeratedFlags & clear()
{
m_bits = store_type(0);
return *this;
}
EnumeratedFlags & flip()
{
m_bits = ~m_bits;
return *this;
}
EnumeratedFlags & flip(enum_type flag)
{
m_bits ^= static_cast<store_type>(flag);
return *this;
}
size_t count() const
{
// http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
store_type bits = m_bits;
size_t total = 0;
for (; bits != 0; ++total)
{
bits &= bits - 1; // clear the least significant bit set
}
return total;
}
size_t size() const
{
// one character per possible bit
return sizeof(enum_type) * 8;
}
bool test(enum_type flag) const
{
return BitTest(m_bits, static_cast<store_type>(flag));
}
bool any() const
{
return m_bits != 0;
}
bool none() const
{
return m_bits == 0;
}
private:
store_type m_bits;
};
template <class charT, class traits, typename enumT>
std::basic_ostream<charT, traits> & operator << (std::basic_ostream<charT, traits> & os, const EnumeratedFlags<enumT> & flags)
{
return os << flags.to_string();
}
template <typename enumT>
EnumeratedFlags<enumT> operator & (const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs)
{
return EnumeratedFlags<enumT>(lhs.bits() & rhs.bits());
}
template <typename enumT>
EnumeratedFlags<enumT> operator | (const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs)
{
return EnumeratedFlags<enumT>(lhs.bits() | rhs.bits());
}
template <typename enumT>
EnumeratedFlags<enumT> operator ^ (const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs)
{
return EnumeratedFlags<enumT>(lhs.bits() ^ rhs.bits());
}
template <typename enumT>
bool BitTest(const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs)
{
return (lhs & rhs);
}
Basically, I would have thought that any free function in the form of X (const T & lhs, const T & rhs) would use up to one user-defined conversion to find a valid invocation of BitTest<>(), above. Instead, I have to explicitly state the conversion in my above code to get the compiler to use this function, which vastly reduces the expressive power of template class EnumeratedFlags<>.
In general, C++ drives me nuts that there isn't a good way to use bits that combines all of the features and good programming habits of using a scoped enum (enum class Foo) and bit-fields (or a similar named set of bits) and make using them very easy for the client-programmer while retaining basic sanity checking from the compiler (won't auto-convert to a numeric type or vice-verse). Seems like a more comprehensive improvement in the language spec is required to make a bit-field enum that really shines... or am I missing something?
From ยง14.8.1/6 [temp.arg.explicit]
Implicit conversions (Clause 4) will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction.
In your example the second argument to BitTest() does participate in template argument deduction, hence no implicit conversion is attempted and the compiler is unable to convert the type Flags to const EnumeratedFlags<Flag>&.
You could solve it by converting the second parameter type to a non-deduced context, thus preventing it from participating in template argument deduction.
template <typename enumT>
bool BitTest(const EnumeratedFlags<enumT>& lhs,
const EnumeratedFlags<typename EnumeratedFlags<enumT>::enum_type>& rhs)
{
return (lhs & rhs);
}
Live demo
Of course, the other solution is to provide these overloads instead
template <typename enumT>
bool BitTest(const EnumeratedFlags<enumT>& lhs,
enumT rhs)
{
return (lhs & EnumeratedFlags<enumT>(rhs));
}
template <typename enumT>
bool BitTest(enumT lhs,
const EnumeratedFlags<enumT>& rhs)
{
return BitTest(rhs, lhs);
}
My question is already answered above. However, this has been bothering me forever, so I was able to leverage everyone's responses and came up with, what I think is for the first time, a mechanism that I really like!
This gives me a way to use C++ enumerations to store bit flag values, and then to logically bit-manipulate them at will, while never losing type information or the compiler's assistance with making sure that I fall into the pit of success. :)
I hope this helps you too!
(Note: this code compiles and runs properly under VS2013 update 1)
#pragma once
#include <type_traits>
// This is my ongoing attempt to make a really solid enumeration facility in C++11/14
//
// What I hate about C++98 (and older) enum
// - lack of namespace scoping of the non-class enum (name collisions everywhere)
// - auto conversion to numeric types (int i = MyEnumConstant), but no conversion back again (so supports losing info, but restricts regaining it)
//
// What I hate about C++11/14 built-in `enum class X`
// - having to constantly cast in order to treat them (now neither direction works)
// - no built-in mechanism to treat enumerated values as bits or bit-flags
template <typename enum_type>
class bitflag_enum
{
public:
// expose our underlying types
typedef enum_type enum_type;
typedef typename std::underlying_type<enum_type>::type store_type;
// constructors
bitflag_enum()
: m_bits(0)
{
}
bitflag_enum(enum_type flag)
: m_bits(static_cast<store_type>(flag))
{
}
explicit bitflag_enum(store_type value)
: m_bits(value)
{
}
// operators
operator bool() const
{
return m_bits != store_type(0);
}
// explicit accessors
store_type bits() const
{
return m_bits;
}
private:
store_type m_bits;
};
// because implicit conversion isn't considered if a type participates in template type deduction,
// we've defined both homogeneous and heterogeneous operators here for bitflag_enum<enum_type> and enum_type
// hence we define logical operators &, |, ^ and comparisons for TxT, TxU, UxT (where T is bitflag_enum<enum_type>, and U is enum_type)
template <typename enum_type>
bool operator != (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
return bitflag_enum<enum_type>(lhs.bits() != rhs.bits());
}
template <typename enum_type>
bool operator != (bitflag_enum<enum_type> lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(lhs.bits() != static_cast<store_type>(rhs));
}
template <typename enum_type>
bool operator != (enum_type lhs, bitflag_enum<enum_type> rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) != rhs.bits());
}
template <typename enum_type>
bool operator == (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
return bitflag_enum<enum_type>(lhs.bits() == rhs.bits());
}
template <typename enum_type>
bool operator == (bitflag_enum<enum_type> lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(lhs.bits() == static_cast<store_type>(rhs));
}
template <typename enum_type>
bool operator == (enum_type lhs, bitflag_enum<enum_type> rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) == rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator & (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
return bitflag_enum<enum_type>(lhs.bits() & rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator & (bitflag_enum<enum_type> lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(lhs.bits() & static_cast<store_type>(rhs));
}
template <typename enum_type>
bitflag_enum<enum_type> operator & (enum_type lhs, bitflag_enum<enum_type> rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs)& rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator | (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
return bitflag_enum<enum_type>(lhs.bits() | rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator | (bitflag_enum<enum_type> lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(lhs.bits() | static_cast<store_type>(rhs));
}
template <typename enum_type>
bitflag_enum<enum_type> operator | (enum_type lhs, bitflag_enum<enum_type> rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) | rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator ^ (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs)
{
return bitflag_enum<enum_type>(lhs.bits() ^ rhs.bits());
}
template <typename enum_type>
bitflag_enum<enum_type> operator ^ (bitflag_enum<enum_type> lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(lhs.bits() ^ static_cast<store_type>(rhs));
}
template <typename enum_type>
bitflag_enum<enum_type> operator ^ (enum_type lhs, bitflag_enum<enum_type> rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) ^ rhs.bits());
}
// The only missing pieces above are for the UxU cases
// we allow you to have those by defining a specialization of is_bitflag_enum<>, as follows:
//
// template <> struct is_bitflag_enum<YourEnumType> : std::true_type { };
//
// However, by default, no other types will convert to an bitflag_enum<> unless you explicitly say you want it
//
// If you have asked for them, then you can use MyEnum::ValueX | MyEnum::ValueY and that will produce a bitflag_enum<MyEnum>
// so your code can simply use your enumeration values with scope and as-if they were bit flags as you would think you could
// don't mess up existing enumerations or types by defining these global operators on every existing type!
template <typename enum_type> struct is_bitflag_enum : std::false_type { };
template <typename enum_type>
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type
operator & (enum_type lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) & static_cast<store_type>(rhs));
}
template <typename enum_type>
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type
operator | (enum_type lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) | static_cast<store_type>(rhs));
}
template <typename enum_type>
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type
operator ^ (enum_type lhs, enum_type rhs)
{
using store_type = std::underlying_type<enum_type>::type;
return bitflag_enum<enum_type>(static_cast<store_type>(lhs) ^ static_cast<store_type>(rhs));
}