Example
#include <utility>
int main() {
std::unreachable();
return 42; // invokes undefined behavior
}
Puzzle
- Can you implement
switch_id
which marks call with unknown id (id != Ids...) as unreachable?
template<auto Unreachable, auto... Ids>
constexpr auto switch_id(auto id); // TODO
int main(){
using namespace boost::ut;
expect(1_i == switch_id<[] { std::unreachable(); }, 1, 2, 4>(1));
expect(2_i == switch_id<[] { std::unreachable(); }, 1, 2, 4>(2));
expect(throws<std::runtime_error>([] { switch_id<[] { throw std::runtime_error{""}; }, 1, 2, 4>(3); }));
expect(4_i == switch_id<[] { std::unreachable(); }, 1, 2, 4>(4));
}
Solutions
template<auto Unreachable, auto... Ids>
constexpr auto switch_id(auto id) {
if (((id != Ids) and ...)) {
Unreachable();
}
return id;
}
constexpr auto compare(const auto elem, const auto id, auto& found_it)
{
if(elem == id){ found_it = true; };
}
template<auto Unreachable, auto...List>
constexpr auto switch_id(auto id)
{
auto found_it = false;
auto list_tuple = std::make_tuple(List...);
std::apply([&found_it,id](auto&&... elem) {((compare(elem,id,found_it)), ...);}, list_tuple);
if( !found_it ){ Unreachable(); }
return id;
};
template<auto... Ids>
constexpr auto id_does_not_exist = [](const auto id){
return ((id != Ids) and ...);
};
template<auto Unreachable, auto... Ids>
constexpr auto switch_id(auto id){
if(id_does_not_exist<Ids...>(id)){
Unreachable();
}
return id;
}
template<auto Unreachable, auto...Ids>
constexpr auto switch_id(auto id) {
std::vector ids{Ids...};
if (std::find(std::begin(ids), std::end(ids), id) == std::end(ids)) {
Unreachable();
}
return id;
}
template<auto Unreachable, auto...IDs>
constexpr auto switch_id(auto id) {
std::array<int, sizeof...(IDs)> a{IDs...};
if (std::find(std::cbegin(a), std::cend(a), id) == std::cend(a)) {
Unreachable();
}
return id;
};
template<auto Unreachable>
auto switch_id(auto id)
{
Unreachable();
return std::declval<decltype(id)>();
}
template<auto Unreachable, auto Car, auto... Cdr>
auto switch_id(auto id)
{
if (id == Car)
return id;
else
return switch_id<Unreachable, Cdr...>(id);
}
template<auto Unreachable, auto...Ids>
constexpr auto switch_id(auto id) {
std::common_type_t<decltype(Ids)...> result;
if ( ((id==Ids ? (result=id, true) : false) || ...) )
Unreachable();
return result;
}
template<auto Unreachable, auto... Ids>
constexpr auto switch_id(auto id){
constexpr std::array<decltype(id),sizeof...(Ids)> ids = {{ Ids ... }};
if (std::binary_search(ids.begin(), ids.end(), id)) return id;
Unreachable();
return id;
};
namespace detail {
[[nodiscard]] constexpr auto is_valid_id(const auto id, const auto... ids) {
return (... and (id == ids));
}
}
template <auto Unreachable, auto... Ids>
constexpr auto switch_id(const auto id) {
if (not detail::is_valid_id(id, Ids...)) {
Unreachable();
}
return id;
}