Let's say that I'm crazy and decided to create the following monstrosity:
#include <type_traits>
#include <iostream>
// Utility proxy type - convertible back to E but also permits bool conversion
// for use in conditions.
//
// e.g.
// Foo f = Foo::Bar & Foo::Baz;
// if (f & Foo::Baz) { /* ... */ }
//
template <typename E, typename = std::enable_if_t<std::is_enum_v<E>, void>>
struct EnumToBoolProxy
{
operator E() const
{
return _val;
}
explicit operator bool()
{
using UT = std::underlying_type_t<E>;
return static_cast<UT>(_val) != 0;
}
private:
const E _val;
EnumToBoolProxy(const E val) : _val(val) {}
friend EnumToBoolProxy operator&(const E, const E);
friend EnumToBoolProxy operator|(const E, const E);
};
enum class Foo
{
Bar = 1, Baz = 2, Boi = 4
};
EnumToBoolProxy<Foo> operator&(const Foo lhs, const Foo rhs)
{
using UT = std::underlying_type_t<Foo>;
return static_cast<Foo>(static_cast<UT>(lhs) & static_cast<UT>(rhs));
}
EnumToBoolProxy<Foo> operator|(const Foo lhs, const Foo rhs)
{
using UT = std::underlying_type_t<Foo>;
return static_cast<Foo>(static_cast<UT>(lhs) | static_cast<UT>(rhs));
}
int main()
{
// Good
if ((Foo::Bar | Foo::Baz) & Foo::Baz)
std::cout << "Yay\n";
// Fine
const bool isFlagSet((Foo::Bar | Foo::Baz) & Foo::Baz);
std::cout << isFlagSet << '\n';
// Meh
auto proxyThing = (Foo::Bar | Foo::Baz) & Foo::Baz;
}
The goal is to:
- Have a scoped enum such that values are
Foo::x
and are of typeFoo
(symmetry!) - Be able to do some "bitwise" arithmetic on them and get a
Foo
back - Be able to check the result for zero-ness
- But not let people generally use the enum as an arithmetic type
For fun, I'm trying to avoid:
- Using a bog-standard enum
- Doing this instead with free functions
IsFlagSet
Ignoring for a second the incongruity of not being able to do the zero-ness check without a prior &
- or |
-operation…
It seems like a shame that my users can still "get" a EnumToBoolProxy
(i.e. proxyThing
). But, since it is not possible to add any members to Foo
, and since operator bool
must be a member, I can't seem to find any other way to go about this.
Granted that's not a real problem as they can't do much with the EnumToBoolProxy
. But it still feels like an abstraction leak, so I'm curious: am I right in saying that this is just inherently impossible? That there's no way to "pick and choose" a scoped-enum's opacity like this? Or is there some way to hide this proxy type while still using as a conversion-to-bool facility to check the "result" of the &
/|
operations? How would you do it?