суббота, 13 октября 2018 г.

[prog.c++] В склерозник: самодельный аналог std::apply для C++11

Когда-то давно по материалам из Интернета сделал собственный аналог функции std::apply из C++17, но для C++11. Для тех счастливчиков, которые никогда не сталкивались с std::apply или его аналогами скажу, что std::apply позволяет вызвать нужную вам функцию, передав в нее содержимое имеющегося у вас std::tuple. Т.е. фокус в том, чтобы распаковать содержимое std::tuple в набор аргументов вызываемой функции.

В C++11 для такого трюка не было вообще никаких готовых инструментов. В C++14 уже появился std::index_sequence, на базе которого основная магия и происходит. Ну а в C++17 уже есть std::apply. Но мне в свое время нужно было именно для C++11, поэтому и появилась реализация call_by_tuple. Появилась и так и валяется у меня на винте. Время от времени ее приходится искать. И, для того, чтобы искать было проще, решил ее код опубликовать в склерозникблоге.

Поиграться в он-лайн компиляторе с моим вариантом call_by_tuple можно на wandbox-е. Исходный текст этого же примера с реализацией call_by_tuple под катом.

#include <iostream>
#include <tuple>
#include <type_traits>

templatesize_t... >
struct sequence {};

templatesize_t I, size_t... S >
struct generate_sequence : generate_sequence< I - 1, I - 1, S... > {};

templatesize_t... S >
struct generate_sequence< 0, S... >
{
   using type = sequence< S... >;
};

templatesize_t... S, typename C, typename... A >
auto
call_by_tuple_impl( sequence< S... >, C && c, const std::tuple< A... > & args ) ->
   typename std::result_of< C(A...) >::type
{
   return c( std::get<S>( args )... );
}

templatesize_t... S, typename C, typename... A >
auto
call_by_tuple_impl( sequence< S... >, C && c, std::tuple< A... > & args ) ->
   typename std::result_of< C(A...) >::type
{
   return c( std::get<S>( args )... );
}

templatetypename C, typename... A >
auto
call_by_tuple( C && c, const std::tuple<A...> & args ) ->
   typename std::result_of< C(A...) >::type

{
   return call_by_tuple_impl(
         typename generate_sequence< sizeof...(A) >::type(),
         std::forward< C >(c),
         args );
}

templatetypename C, typename... A >
auto
call_by_tuple( C && c, std::tuple<A...> & args ) ->
   typename std::result_of< C(A...) >::type

{
   return call_by_tuple_impl(
         typename generate_sequence< sizeof...(A) >::type(),
         std::forward< C >(c),
         args );
}

int one( int a )
{
   std::cout << "one: " << a << std::endl;
   return a;
}

int two( int a, int b )
{
   std::cout << "two: " << a << ", " << b << std::endl;
   return a+b;
}

void three( std::string a, std::string b )
{
   std::cout << "three: " << a << ", " << b << std::endl;
   a = "-" + a + "-";
   b = "(" + b + ")";
}

int main()
{
   auto t1 = std::make_tuple( 1 );
   auto t2 = std::make_tuple( 12 );

   call_by_tuple( one, std::make_tuple( 1 ) );
   call_by_tuple( one, t1 );
   call_by_tuple( two, std::make_tuple( 12 ) );
   call_by_tuple( two, t2 );

   auto t3 = std::make_tuple< std::string, std::string >( "a""b" );

   call_by_tuple( three, t3 );
}

Комментариев нет: