Double fold expression to replace branching statement

77 Views Asked by At

I am trying to figure out how to use fold expressions ( or other metaprogramming techniques) to get rid off "if else" branches in the code snippet below.

template<typename ... Ts>
struct VisitorAny
{

    template<typename Callable>
    auto apply_visitor(Callable&& callable, const std::any& data1, const std::any& data2)
    {
        using first_type = std::tuple_element_t<0, std::tuple<Ts...>>;
        using R = decltype(std::declval<Callable>()(std::declval<first_type>(), std::declval<first_type>()));

        //std::cout << "Return type is " << typeid(R).name() << std::endl;
        R ret{};
        if (data1.type() == typeid(int) && data2.type() == typeid(int))
        {
            ret = std::forward<Callable>(callable)(std::any_cast<int>(data1), std::any_cast<int>(data2));
        }
        else if (data1.type() == typeid(float) && data2.type() == typeid(int))
        {
            ret = std::forward<Callable>(callable)(std::any_cast<float>(data1), std::any_cast<int>(data2));
        }
        else if (data1.type() == typeid(int) && data2.type() == typeid(float))
        {
            ret = std::forward<Callable>(callable)(std::any_cast<int>(data1), std::any_cast<float>(data2));
        }
        else if (data1.type() == typeid(float) && data2.type() == typeid(float))
        {
            ret = std::forward<Callable>(callable)(std::any_cast<float>(data1), std::any_cast<float>(data2));
        }
        //(((data1.type() == typeid(Ts)) ? ((), false) : true) && ...);
        return ret;
    }


};

In the case of a callable which accepts just one instance of std::any, it is straightforward

    template<typename Tany, typename Tr, typename Callable>
    void visit(const Tany& any_inp, Tr& ret, Callable&& callable)
    {
        ret = std::forward<Callable>(callable)(any_inp);
    };

    template<typename Callable>
    auto apply_visitor(Callable&& callable, const std::any& data)
    {
        using first_type = std::tuple_element_t<0, std::tuple<Ts...>>;
        using R = decltype(std::declval<Callable>()(std::declval<first_type>()));
        //std::cout << "Return type is " << typeid(R).name() << std::endl;
        R ret{};
        (((data.type() == typeid(Ts)) ? ((visit(std::any_cast<Ts>(data), ret, callable)), false) : true) && ...);
        return ret;
    };

I wonder how a similar technique can be used for a callable that accepts two instances of std::any. Using std::visit and std::variant is not an option.

2

There are 2 best solutions below

0
Igor Tandetnik On

Something along these lines (not tested):

template<typename Data1, typename Tr, typename Callable>
void visit(const Data1& data1, const std::any& data2, Tr& ret, Callable&& callable)
{
    ((data2.type() == typeid(Ts) ?
      ((ret = std::forward(callable)(data1, std::any_cast<Ts>(data2))), false)
      : true) && ...);
};

template<typename Callable>
auto apply_visitor(Callable&& callable, const std::any& data1, const std::any& data2)
{
    using first_type = std::tuple_element_t<0, std::tuple<Ts...>>;
    using R = decltype(std::declval<Callable>()(std::declval<first_type>()));
    //std::cout << "Return type is " << typeid(R).name() << std::endl;
    R ret{};
    (((data1.type() == typeid(Ts)) ?
      ((visit(std::any_cast<Ts>(data1), data2, ret, std::forward(callable))), false)
      : true) && ...);
    return ret;
};
0
Jarod42 On

Don't know why you don't want std::variant. It would be:

template<typename ... Ts>
struct VisitorAny
{

    std::optional<std::variant<Ts...>> to_variant(std::any a)
    {
        std::optional<std::variant<Ts...>> res;

        ([&](){ if (auto* p = std::any_cast<Ts>(&a)) { res = *p; } }(), ...);

        return res;
    }

    template<typename Callable, typename... Any>
    auto apply_visitor(Callable&& callable, const Any&... anys)
    {
        auto vars = std::tuple(to_variant(anys)...);

        if (!std::apply([](const auto&... vars){ return (vars.has_value() && ...); }, vars))
        {
            throw std::runtime_error("Not supported type");
        }
        std::apply([&callable](const auto&... vars){ return std::visit(callable, *vars...); }, vars);
    }
};

Demo.