我可以获取多个链接函数调用的返回类型吗?

时间:2019-01-07 14:59:47

标签: c++ function stdtuple

我想将函数存储在有序集合中,然后将所有函数应用于某个集合,这将导致获得经过大量修改的值,并将其存储在另一个集合中。我的最初尝试包括创建一个上述函数的std::tuple并尝试获取将所有这些函数应用于特定类型的结果类型(std::invoke_result):

int main() {
    auto multiply   = [](const auto arg){ return arg * arg; };
    auto change     = [](const auto arg){ return std::vector{arg}; };
    auto to_string  = [](const auto arg){ return arg.size() + " size"; };

    auto functions = std::make_tuple(multiply, change, to_string);

    std::vector<int> source{1, 2, 3, 4};

    using f_type = decltype(functions);
    using last_type =
            std::tuple_element_t<std::tuple_size_v<f_type> - 1, f_type>;
    using result_type =
            std::invoke_result_t<last_type, /* size - 2 ret type and so on */>;

    /* 
     * result_type is the type of applying *multiply* to int (type of *source*),
     * then applying *change* to the result of *multiply* and then applying
     * *to_string* to the result of *change*. Should be std::string.
     */
    std::vector<result_type> results{};
}

问题是template的第二个std::invoke_result_t参数需要一种类型,该类型将传递给last_type类型的对象的调用运算符。这需要在最后一个元素的返回类型之前减去一个,以此类推(可能有很多功能)。

我最终要实现的目标是实现Java的流库(此示例等效于链接3个map函数)。我还将持有其他enum来指示下一个元素是mapfilter还是任何其他受支持的函数,因此不会混淆该函数应该做什么-现在的问题是开始使用这种逻辑。

是否有一种方法可以获取链接任意数量的函数的返回类型,而该类型将传递给已知的第一个函数?

或者我的设计有太多缺陷,我宁愿按照完全不同的逻辑重新开始?

免责声明-我很清楚C++20即将面世rangesV3。我试图模仿他们的行为(有一些细微的变化)。我也知道boost::adapters-它们的用法令我不满意,而且我想尝试简单地实现类似的东西。

1 个答案:

答案 0 :(得分:3)

假设您有三个可调用对象f g h,并且想要获得h(g(f(args...)))的类型,可以这样:

template <size_t first, class ChainedFns, class... Args>
decltype(auto) Call(ChainedFns &&fns, Args&&... args) {
    if constexpr (std::tuple_size_v<std::decay_t<ChainedFns>> == 0)
        return;
    else if constexpr (first < std::tuple_size_v<std::decay_t<ChainedFns>>-1)
        return Call<first + 1>(fns, std::invoke(std::get<first>(std::forward<ChainedFns>(fns)), std::forward<Args>(args)...));
    else if constexpr (first == std::tuple_size_v<std::decay_t<ChainedFns>>-1)
        return std::invoke(std::get<first>(std::forward<ChainedFns>(fns)), std::forward<Args>(args)...);
}

template <size_t first, class ChainedFns, class... Args>
struct invoke_result_of_chained_callables {
    using type = decltype(Call<first>(std::declval<ChainedFns>(), std::declval<Args>()...));
};

template <size_t first, class ChainedFns, class... Args>
using invoke_result_of_chained_callables_t = typename invoke_result_of_chained_callables<first, ChainedFns, Args...>::type;

int main() {
    auto fns = std::make_tuple(
        [](auto) { return 0; }, // f
        [](auto x) { return std::vector{ x }; }, // g
        [](auto x) { return x.size(); } // h
    );

    using type = decltype(Call<0>(fns, nullptr));
    static_assert(std::is_same_v<type, size_t>);

    using type1 = invoke_result_of_chained_callables_t<0, decltype(fns), std::nullptr_t>;
    static_assert(std::is_same_v<type, type1>);
    return 0;
}

此代码段也适用于任意数量的链式可调用对象。