Example
enum { QL = 42 };
int main() {
std::cout << std::to_underlying(QL); // prints 42
static_assert(typeid(std::to_underlying(QL)) == typeid(unsigned));
}
Puzzle
- Can you implement
sum_enums
which adds only underlying enum values?
template <auto... Ns>
constexpr auto sum_enums = /*TODO*/;
enum { _1 = 1 };
enum class Q2 { _2 = 2 };
enum class Q3 : int { _3 = 3 };
static_assert(0 == sum_enums<42>);
static_assert(1 == sum_enums<_1>);
static_assert(2 == sum_enums<Q2::_2>);
static_assert(3 == sum_enums<Q3::_3>);
static_assert(1 + 2 + 3 == sum_enums<_1, Q2::_2, Q3::_3>);
static_assert(3 + 2 + 1 == sum_enums<Q3::_3, Q2::_2, _1>);
static_assert(3 + 2 + 1 == sum_enums<Q3::_3, Q2::_2, _1, 42>);
static_assert(3 + 2 + 1 == sum_enums<1, Q3::_3, 2, Q2::_2, 3, _1, 4>);
Solutions
template <auto V>
constexpr auto enum_underlying_or_zero = 0;
template <auto V>
requires requires { std::to_underlying(V); }
constexpr auto enum_underlying_or_zero<V> = std::to_underlying(V);
template <auto... Ns>
constexpr auto sum_enums = (enum_underlying_or_zero<Ns> + ...)
template <auto... Ns>
constexpr auto sum_enums = ([](auto val) {
if constexpr (std::is_enum_v<decltype(val)>)
return std::to_underlying(val);
else
return 0;
} (Ns) + ...);
namespace detail {
template <typename T>
concept CEnum = std::is_enum_v<T>;
[[nodiscard]] constexpr auto get_enum_value(auto&&) { return 0; }
[[nodiscard]] constexpr auto get_enum_value(const CEnum auto e) {
return std::to_underlying(e);
}
} // namespace detail
template <auto... Ns>
constexpr auto sum_enums = (... + detail::get_enum_value(Ns));
namespace detail {
template <auto v>
constexpr auto value = []{
if constexpr (std::is_enum_v<decltype(v)>) {
return std::to_underlying(v);
} else {
return 0;
}
};
}
template <auto... Ns>
constexpr auto sum_enums = [] {
return (detail::value<Ns>() + ...);
}();
template <auto N>
constexpr auto underlying_value = [] {
if constexpr (requires {std::to_underlying(N);}) {
return std::to_underlying(N);
} else {
return decltype(N){};
}
};
template <auto... Ns>
constexpr auto sum_enums = (underlying_value<Ns>() + ... + 0);
template<typename ValT>
requires std::integral<ValT>
constexpr auto get_return_val(ValT const& val){
return 0;
}
template<typename ValT>
constexpr auto get_return_val(ValT const& val){
return std::to_underlying(val);
}
template<typename... Ns>
constexpr auto sum(Ns const&... n){
return ( get_return_val(n) + ...);
}
template <auto... Ns>
constexpr auto sum_enums = [](){
return sum(Ns...);
}();
template<typename ValT>
requires std::integral<ValT>
constexpr auto get_return_val(ValT const& val){
return 0;
}
template<typename ValT>
constexpr auto get_return_val(ValT const& val){
return std::to_underlying(val);
}
template<typename... Ns>
constexpr auto sum(Ns const&... n){
return ( get_return_val(n) + ...);
}
template <auto... Ns>
constexpr auto sum_enums = [](){
return sum(Ns...);
}();
template <auto N>
constexpr auto enum_value = [] {
if constexpr (std::is_enum_v<decltype(N)>) {
return std::to_underlying(N);
} else {
return 0;
}
};
template <auto... Ns>
constexpr auto sum_enums = (enum_value<Ns>() + ...);
template <auto... Ns>
constexpr auto sum_enums = (... + [] {
if constexpr (requires { std::to_underlying(Ns); }) {
return std::to_underlying(Ns);
} else {
return 0;
}
}());
template <auto N>
constexpr auto underlying_value = []{
if constexpr(requires{std::to_underlying(N);}){
return std::to_underlying(N);
}
else{
return 0;
}
};
template <auto... Ns>
constexpr auto sum_enums = (... + underlying_value<Ns>());
template <auto... Ns>
constexpr auto sum_enums = (
[](auto n) {
if constexpr (requires { std::to_underlying(n); })
return std::to_underlying(n);
else
return 0;
}(Ns) + ...
);
template<auto T>
[[nodiscard]] consteval auto get_enum_value() -> int {
if constexpr (std::is_enum_v<decltype(T)> ) {
return std::to_underlying(T);
}
else {
return 0;
}
}
template <auto... Ns>
constexpr auto sum_enums = (... + get_enum_value<Ns>());
template <auto n>
constexpr auto to_value = [] {
if constexpr (std::is_enum_v<decltype(n)>) {
return std::to_underlying(n);
} else {
return 0;
}
};
template <auto... Ns>
constexpr auto sum_enums = [] {
return (to_value<Ns>() + ...);
}();
template <typename ...Ns>
constexpr auto sum_enums_impl(Ns... ns) {
auto underlying_or_zero = [](auto ns) {
if constexpr( requires{std::to_underlying(ns); })
return std::to_underlying(ns);
else
return 0;
};
return ( underlying_or_zero(ns) +...);
}
template <auto... Ns>
constexpr auto sum_enums = sum_enums_impl(Ns...);
template <auto... Ns>
constexpr auto sum_enums = []{
if constexpr (sizeof...(Ns) > 0){
auto helper = []<auto H, auto... Hs>() {
if constexpr (std::is_enum_v<decltype(H)>)
return std::to_underlying(H) + sum_enums<Hs...>;
else
return sum_enums<Hs...>;
};
return helper.template operator()<Ns...>();
}
else
return 0;
}();