2016-12-17 1 views
2

Я искал this tuple for_each() implementation несколько месяцев назад и задавался вопросом, возможно ли реализовать версию, которая собирает возвращаемые значения при вызове функций в кортеж в результате?Есть ли кортеж for_each(), который возвращает кортеж всех значений, возвращаемых из вызванных функций?

Причина, по которой я хочу сделать это в моей базе кода У меня есть следующая функция, которая принимает входной список вариаций в форме и возвращает кортеж значений.

template <typename... T, typename... R> 
static constexpr auto map_to_opengl(T &&... shapes) 
{ 
    return std::make_tuple(shape_mapper::map_to_array_floats(shapes)...); 
} 

Ну, я хотел бы изменить свою подпись функции, чтобы принять кортеж формы, и возвращает результат вызова функции в каждой форме (это должно быть семантически эквивалентен коду выше). Если я смогу это сделать, я могу сохранить код более сухим, что важно для меня.

template <typename... T, typename... R> 
static constexpr auto map_to_opengl(std::tuple<T...> &&shapes) 
{ 
    return tuple_foreach(shapes, &shape_mapper::map_to_array_floats); 
} 

Однако реализация tuple_foreach не позволяет все значения должны быть собраны. Можно ли написать такую ​​функцию? Если она существует в Hana, я пропустил это :(

Я думаю, вы не могли бы назвать этот алгоритм for_each, но, возможно, накапливают? Я не уверен, что здесь.

+0

Вы можете комбинировать существующий код с новым ['std :: apply'] (http://en.cppreference.com/w/cpp/utility/apply)? –

+0

Возможно, но я не уверен, как «собрать» значения в кортеж, если бы я должен был называть применительно к каждой функции, поскольку каждая функция вызывается один за другим, я понятия не имею, где бы я помещал промежуточные возвращаемые значения. – Short

+0

Терминологические языки FP назовут это «картой», C++ назвал бы это «преобразованием». – ildjarn

ответ

3

Ничего не было добавлено в стандартную библиотеку в C++ 17, что особенно помогло бы здесь (о чем я могу думать); вот подход обычно C++ 14 (с использованием пакета расширения) в качестве автономного алгоритма:

namespace detail { 
    template<typename T, typename F, std::size_t... Is> 
    constexpr auto map_tuple_elements(T&& tup, F& f, std::index_sequence<Is...>) { 
     return std::make_tuple(f(std::get<Is>(std::forward<T>(tup)))...); 
    } 
} 

template<typename T, typename F, std::size_t TupSize = std::tuple_size_v<std::decay_t<T>>> 
constexpr auto map_tuple_elements(T&& tup, F f) { 
    return detail::map_tuple_elements(
     std::forward<T>(tup), f, 
     std::make_index_sequence<TupSize>{} 
    ); 
} 

Online Demo

+0

Это впечатляюще изящное решение, мой друг. И демонстрация с примерами делает этот ответ легендарным. Спасибо!! Вы упоминаете «обычный подход», я бы очень хотел узнать больше об этом. У вас есть источники, которые я могу узнать больше о «обычном подходе»? – Short

+0

@Short: Я рекомендую прочитать ['std :: integer_sequence' и друзей] (http://en.cppreference.com/w/cpp/utility/integer_sequence) и прочитать некоторые высоко-голосовые ответы, которые используют из них ('std :: index_sequence', вероятно, является символом для поиска). – ildjarn

0

Следующая main() создает кортеж, применяет функция для каждого значения в кортеже и производит другой кортеж. Вы можете тривиально обернуть все это в свою собственную функцию.

Это решение использует несколько шаблонов, которые являются новыми для C++ 17, но все они могут если необходимо, трижды переоценить для C++ 14:

#include <utility> 
#include <tuple> 
#include <iostream> 

// The function 

int square(int n) 
{ 
    return n * 2; 
} 

// The helper class for unpacking a tuple into a parameter pack, 
// invoking square(), then packing the result back into a tuple. 

template<typename tuple, typename index_sequence> class apply_square; 

template<typename tuple, 
    size_t... sequence> 
class apply_square<tuple, std::index_sequence<sequence...>> { 

public: 

    template<typename tuple_arg> 
    static auto do_apply_square(tuple_arg &&tupple) 
    { 
     return std::make_tuple(square(std::get<sequence>(tupple))...); 
    } 
}; 

int main() 
{ 
    // Create a sample tuple 

    auto f=std::make_tuple(1, 2); 

    // Invoke appropriately-specialized do_apply_square() against 
    // the tuple. 

    typedef std::make_index_sequence<std::tuple_size<decltype(f)> 
        ::value> tuple_indexes; 

    auto f_squared=apply_square<decltype(f), tuple_indexes> 
     ::do_apply_square(f); 

    // f_squared should now be another tuple. Let's see: 

    int &first=std::get<0>(f_squared); 
    int &second=std::get<1>(f_squared); 

    std::cout << first << ' ' << second << std::endl; 
} 
4

Делать это с std::apply бы что-то вдоль этих линий:

template<typename T, typename F> 
constexpr auto map_tuple_elements(T&& tup, F f) { 
    return std::apply([&f](auto&&... args){ 
       return std::make_tuple(f(decltype(args)(args))...);  
      }, std::forward<T>(tup)); 
}