Let's say I have the following code:
// exposition-only, I can't use this macro in my solution
#if defined(HAS_MY_ENUM)
enum my_enum {
zero,
one,
};
#endif
enum empty {
placeholder, // an enum must always have a value
};
I'd love to have a conditional type alias my_enum_or_empty
that is set to my_enum
if it is defined, otherwise it's set to empty
, e.g.:
using my_enum_or_empty = std::conditional<my_enum_exists, my_enum, empty>;
I want a SFINAE-based solution that gives me my_enum_exists
.
I was thinking of this approach, but it requires a forward-declaration of my_enum
, and it cannot be forward declared, because it is sizeless. And I cannot add a size, because this enum
comes from an external library. Do you have any ideas?
My use case is that this my_enum
is defined in an external C library that I don't have control over. I need to compile my code against two versions of this library (with my_enum
and without). The header sources, where my_enum
is defined cannot be changed i.e. it must be an old C-style enum
.
The library version is not known at compile time. Sadly, there is no LIBRARY_VERSION
macro. I need to rely on this enum
alone.
I cannot rely on the build system. In fact, I am doing a C++ header-only wrapper around this C code, hence there is no build system. Neither can I move responsibility to my customer. There are more than one type missing and there are many versions of this library, i.e. it'd be too complex for my wrapper user.
@Artyer suggests a more practical approach in his answer. I'm leaving this here mostly as a technical curiosity.
What you can do, if you know the name of the one of the constants in the enum, is to define it as a macro (assuming the target header doesn't use that name anywhere else), which would expand to itself plus some magic to detect whether it was used or not (stateful metaprogramming).
The following works with the assumption that the constant doesn't have a = value
specified. If it does and you know the exact value beforehand, supporting that case should be trivial. If it does but you don't know the value, it might still be possible, but will be somewhat more ugly.
#include <type_traits>
namespace EnumDetector
{
namespace detail
{
constexpr void _adl_EnumMarker() {} // Dummy ADL target.
template <typename T>
struct Reader
{
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-template-friend"
#endif
friend constexpr auto _adl_EnumMarker(Reader<T>);
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
};
template <typename T>
struct Writer
{
friend constexpr auto _adl_EnumMarker(Reader<T>) {return true;}
};
template <typename T, typename = void> struct Value : std::false_type {};
template <typename T> struct Value<T, std::enable_if_t<_adl_EnumMarker(Reader<T>{})>> : std::true_type {};
}
template <typename T>
inline constexpr bool HaveEnum = detail::Value<T>::value;
}
#define ENUM_DETECTOR_REGISTER_WITHOUT_VALUE(tag_, enumerator_) \
DETAIL_ENUM_DETECTOR_REGISTER_WITHOUT_VALUE(__COUNTER__, tag_, enumerator_)
#define DETAIL_ENUM_DETECTOR_REGISTER_WITHOUT_VALUE(counter_, tag_, enumerator_) \
DETAIL_ENUM_DETECTOR_CAT(_enum_detector_helper_,counter_), \
DETAIL_ENUM_DETECTOR_CAT(_enum_detector_helper2_,counter_) = (void(::EnumDetector::detail::Writer<tag_>{}), 0), \
enumerator_ = DETAIL_ENUM_DETECTOR_CAT(_enum_detector_helper_,counter_)
#define DETAIL_ENUM_DETECTOR_CAT(x, y) DETAIL_ENUM_DETECTOR_CAT_(x, y)
#define DETAIL_ENUM_DETECTOR_CAT_(x, y) x##y
// ---
// Repeat following for all enums:
struct MyEnumTag {};
#define my_enum_constant ENUM_DETECTOR_REGISTER_WITHOUT_VALUE(MyEnumTag, my_enum_constant)
// ---
#ifdef YOUR_LIB_INCLUDE_GUARD // <-- Customize this for the target library, or remove altogether if it uses `#pragma once`.
#error "Must not include this stuff elsewhere.."
#endif
// #include <stuff.h>
enum SomeEnum { x, y, my_enum_constant, z, w }; // Try commenting me out to trigger the assertion.
// ---
// Repeat following for all enums:
#undef my_enum_constant
// ---
// Lastly, the usage:
static_assert(EnumDetector::HaveEnum<MyEnumTag>);
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