Могу ли я получить тип возврата для нескольких вызовов функций? - PullRequest
0 голосов
/ 07 января 2019

Я хотел бы сохранить функции в упорядоченной коллекции, а затем применить их все к определенной коллекции, что приведет к получению сильно измененных значений, хранящихся в другой коллекции. Моя первоначальная попытка заключалась в создании 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 s, которые будут указывать, является ли следующий элемент map, filter или какой-либо другой поддерживаемой функцией, так что не будет никакой путаницы относительно того, что должна делать функция - сейчас проблема в том, чтобы начать с такой логикой.

Есть ли способ получить возвращаемый тип, соединяющий произвольное число функций, когда тип передается самому первому из известных ему?

Или, может быть, мой дизайн настолько несовершенен, что мне лучше начать заново, следуя совершенно другой логике?

Отказ от ответственности - я хорошо знаю о предстоящем в C++20 (надеюсь) rangesV3. Я пытаюсь имитировать их поведение (с некоторыми незначительными изменениями). Мне также известно о boost::adapters - их использование меня не устраивает, плюс я бы хотел попробовать реализовать нечто подобное.

1 Ответ

0 голосов
/ 07 января 2019

Скажем, у вас есть три вызываемых объекта, и вы хотите получить тип 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;
}

Этот фрагмент кода работает и для произвольного числа связанных по цепочке объектов.

...