Search code examples
c++templatesboostc++17boost-hana

Massive cryptic error in custom switch function


I am trying to write an any-value switch based on the any type switch example in the boost hana manual.

I have done this before with success, but just can not seem to get it done now.

I really want to just figure out what the hell is wrong with my solution, so without further ado:

struct default_val{
    constexpr bool operator ==(const auto&) const noexcept{ return false; }
    constexpr bool operator !=(const auto&) const noexcept{ return true; }

    constexpr bool operator ==(default_val) const noexcept{ return true; }
    constexpr bool operator !=(default_val) const noexcept{ return false; }
};

template<typename Fn>
auto default_(Fn fn){
    return hana::make_pair(default_val{}, fn);
}

template<typename Val, typename Fn>
auto case_(Val val, Fn fn){
    return hana::make_pair(val, fn);
}

template<typename Val, typename Default>
decltype(auto) process(Val&&, Default &&def){
    return hana::second(std::forward<Default>(def))();
}

template<typename Val, typename Default, typename Case, typename ... Rest>
decltype(auto) process(Val &&val, Default &&def, Case &&cas, Rest &&... rest){
    if(std::forward<Val>(val) == hana::first(std::forward<Case>(cas)))
        return hana::second(std::forward<Case>(cas))();
    else
        return process(std::forward<Val>(val), std::forward<Default>(def), std::forward<Rest>(rest)...);
}

template<typename Val>
auto switch_(Val val){
    return [val](auto ... cases){
        auto cases_ = hana::make_tuple(cases...);

        auto default_ = hana::find_if(cases_, [](const auto &c){
            return
                hana::type_c<default_val> ==
                hana::type_c<std::decay_t<decltype(hana::first(c))>>;
        });

        static_assert(default_ != hana::nothing);

        auto rest_ = hana::filter(cases_, [](const auto &c){
            return
                hana::type_c<default_val> !=
                hana::type_c<std::decay_t<decltype(hana::first(c))>>;
        });

        return hana::unpack(rest_, [&](const auto &... rest){
            return process(val, default_, rest...);
        });
    }
}

This is supposed to be used like the any example, but with values:

#include "myswitch.hpp"

int main(){
    std::string input;

    std::cin >> input;

    switch_(input)(
        case_("yowza", []{ std::cout << "where's the enthusiasm?\n"; }),
        case_("Yowza", []{ std::cout << "wat2hek\n"; }),
        case_("YOWZA!", []{ std::cout << "HooRah!\n"; }),

        default_([]{ std::cout << "Hello, World!\n"; })
    );
}

But I get a veeeery long and veeeery cryptic error about the use of hana::find_if_t::operator()(Xs&&, Pred&&) before deduction of auto when assigning default_ in switch_.

Can somebody point me in the right direction here?


Solution

  • According to Boost.Hana's manual for predicate (emphasis mine):

    A function called as predicate(k), where k is a key of the structure, and returning whether k is the key of the element being searched for. In the current version of the library, the predicate has to return an IntegralConstant holding a value that can be converted to bool.

    This is required for predicates used by both hana::find_if and hana::filter.

    For your default_val type the implementation could be changed to this:

    struct default_val {
        template <typename T>
        constexpr auto operator ==(const T&) const noexcept{ return hana::false_c; }
        template <typename T>
        constexpr auto operator !=(const T&) const noexcept{ return hana::true_c; }
    
        constexpr auto operator ==(default_val) const noexcept{ return hana::true_c; }
        constexpr auto operator !=(default_val) const noexcept{ return hana::false_c; }
    };
    

    I ran your code with Clang 3.9 and it worked fine with the following additional tweak to unwrap the value in your default_ optional value.

            return hana::unpack(rest_, [&](const auto &... rest){
            return process(val, *default_, rest...);
                             // ^ Note the added dereference operator here