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:
Foo::x
and are of type Foo
(symmetry!)Foo
backFor fun, I'm trying to avoid:
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?
Well this is probably not what you want, but you said "hide this proxy type". So you could hide it in the following even more monstrosity. Now the resulting type is a lambda hiding your proxy :)
#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) { /* ... */ }
//
auto lam = [](auto e) {
struct Key {};
//template <typename E, typename = std::enable_if_t<std::is_enum_v<E>, void>>
struct EnumToBoolProxy {
using E = decltype(e);
operator E() const {
return _val;
}
explicit operator bool() {
using UT = std::underlying_type_t<E>;
return static_cast<UT>(_val) != 0;
}
EnumToBoolProxy(const E val, Key) : _val(val) {}
private:
const E _val;
};
return EnumToBoolProxy(e, Key{});
};
enum class Foo {
Bar = 1, Baz = 2, Boi = 4
};
auto operator&(const Foo lhs, const Foo rhs) {
using UT = std::underlying_type_t<Foo>;
return lam(static_cast<Foo>(static_cast<UT>(lhs) & static_cast<UT>(rhs)));
}
template<typename T, std::enable_if_t<std::is_same_v<T, decltype(lam)>>>
auto operator&(T lhs, const Foo rhs) {
using UT = std::underlying_type_t<Foo>;
return lam(static_cast<Foo>(static_cast<UT>(lhs) & static_cast<UT>(rhs)));
}
auto operator|(const Foo lhs, const Foo rhs) {
using UT = std::underlying_type_t<Foo>;
return lam(static_cast<Foo>(static_cast<UT>(lhs) | static_cast<UT>(rhs)));
}
int main() {
lam(Foo::Bar);
// 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';
// OK, still a proxy thing
auto proxyThing = (Foo::Bar | Foo::Baz) & Foo::Baz;
using Proxy = decltype(proxyThing);
//Proxy proxy2(Foo::Bar); // Does not work anymore.
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With