Объявление функции n переменных

205
17 декабря 2017, 10:28

Есть число n. Как объявить функцию n переменных одного типа? Ситуация примерно такая:

using my_type = int;
constexpr size_t n = 4;
std::function<void(give_me_n_types<my_type>(n))> my_func;
Answer 1

Первое, что пришло в голову:

#include <iostream>
#include <utility>
#include <functional>

namespace details
{
    template<typename T, ::std::size_t = 0>
    using get_type_t = T;  
    template<typename RetType, typename ArgsType, ::std::size_t ... I>
    auto make_function_type_impl(::std::index_sequence<I...>) -> RetType (&)(get_type_t<ArgsType, I>...);
    template<typename RetType, typename ArgsType, ::std::size_t N>
    using make_function_type = std::remove_reference_t<decltype(make_function_type_impl<RetType, ArgsType>(::std::make_index_sequence<N>()))>;
}//namespace details

template<typename RetType, typename ArgsType, ::std::size_t N>
using make_function_type_n = ::details::make_function_type<RetType, ArgsType, N>;

double foo(int x, int y)
{
    return (double)x/y;
}

int main()
{
    using my_type = int;
    constexpr size_t n = 2;
    std::function<make_function_type_n<double, my_type, n>> my_func = foo;
    std::cout << my_func(10, 4);
}

http://rextester.com/CPMS17471

Работает это следующим образом.

make_function_type_n<double, int, 2>

Шаблонный using разворачивается в:

::details::make_function_type<double, int, 2>

В свою очередь этот шаблонный using разворачивается в

std::remove_reference_t<decltype(make_function_type_impl<double, int>(::std::make_index_sequence<2>()))>; 

::std::make_index_sequence<2>() создаст объект типа

std::index_sequence<0, 1>// где 0 и 1 - это индексы до 2.

т.е. у нас в decltype попадает выражение

make_function_type_impl<double, int>(::std::index_sequence<0, 1>)

Следовательно, получаем следующее:

make_function_type_impl(::std::index_sequence<0, 1>) -> double (&)(get_type_t<int, 0>, get_type_t<int, 1>)

Таким образом decltype даст ссылку на функцию типа double(int, int), а ссылка удаляется с помощью std::remove_reference_t.

Пока писал, возникла идея повторить не один параметр, а несколько.

Для начала нужна будет структура, которая будет хранить нужный тип функции и наращивать его параметры.

namespace details
{
    template<typename RetType, typename ... Params>
    struct func_n_t {
        using type = RetType(Params...);//Тип функции
        //Этот шаблонный using будет хранить тип func_n_t с параметрами, добавленными к текущим
        template<typename ... Args>
        using add = func_n_t<RetType, Params..., Args...>;
    };

Всё что остается сделать - создать func_n_t с повторенными N раз параметрами.

    template<::std::size_t N, typename Func, typename ... Args>
    struct function_n_t_impl
    {
        //Применяем рекурсию шаблонов.
        //На каждом шаге к инстансу шаблона func_n_t прибавляются аргументы Args
        //При этом N уменьшается на 1
        using type = typename function_n_t_impl<N - 1, typename Func::template add<Args...>, Args...>::type;        
    };
    //Частичная специализация, которая остановит рекурсию
    template<typename Func, typename ... Args>
    struct function_n_t_impl<0, Func, Args...>
    {
        //Просто берем от типа Func тип нужной нам функции
        using type = typename Func::type;   
    };
}//namespace details
//Псевдоним, который запускает формирование нужного типа, начиная с func_n_t<RetType> (т.е. с type = RetType())
template<::std::size_t N, typename RetType, typename ... ArgsType>
using make_function_t = typename ::details::function_n_t_impl<N, details::func_n_t<RetType>, ArgsType...>::type;

Использование:

//Указатель на функцию, возвращающую double и принимающую два параметра типа int
make_function_t<2, double, int>* my_foo = foo;

Полный код:

#include <iostream>
#include <functional>

namespace details
{
    template<typename RetType, typename ... Params>
    struct func_n_t {
        using type = RetType(Params...);//Тип функции
        //Этот шаблонный using будет хранить тип func_n_t с параметрами, добавленными к текущим
        template<typename ... Args>
        using add = func_n_t<RetType, Params..., Args...>;
    };  

    template<::std::size_t N, typename Func, typename ... Args>
    struct function_n_t_impl
    {
        //Применяем рекурсию шаблонов.
        //На каждом шаге к инстансу шаблона func_n_t прибавляются аргументы Args
        //При этом N уменьшается на 1
        using type = typename function_n_t_impl<N - 1, typename Func::template add<Args...>, Args...>::type;        
    };
    //Частичная специализация, которая остановит рекурсию
    template<typename Func, typename ... Args>
    struct function_n_t_impl<0, Func, Args...>
    {
        //Просто берем от типа Func тип нужной нам функции
        using type = typename Func::type;   
    };
}//namespace details

//Псевдоним, который запускает формирование нужного типа, начиная с func_n_t<RetType> (т.е. с type = RetType())
template<::std::size_t N, typename RetType, typename ... ArgsType>
using make_function_t = typename ::details::function_n_t_impl<N, details::func_n_t<RetType>, ArgsType...>::type;

//Функции для теста
double foo(int x, int y)
{
    std::cout << "foo(" << x << ", " << y << ")\n";
    return (double)x/y;
}

double bar(int x1, double y1, int x2, double y2)
{
    std::cout << "bar(" << x1 << ", " << y1 << ", " << x2 << ", " << y2 << ")\n";
    return x1/y1 + x2/y2;
}

void zoo()
{
    std::cout << "zoo\n";
}

int main()
{
    //Функция, возвращающая double и принимающая два параметра типа int
    make_function_t<2, double, int>* my_foo = foo;
    //Функция, возвращающая double и принимающая четыре параметра: int double, int double
    //Набор параметров int, double повторяется два раза.
    std::function<make_function_t<2, double, int, double>> my_bar = bar;
    //Функции, возвращающие void и не принимающие параметров
    std::function<make_function_t<10, void>> my_zoo1 = zoo;//10 пустых наборов
    std::function<make_function_t<0, void, int, size_t, void>> my_zoo2 = zoo;//0 наборов int, size_t, void
    my_foo(10, 4);
    my_bar(10, 4.6, 15, 5.3);
    my_zoo1();
    my_zoo2();
}

http://rextester.com/HWBJA2947

Answer 2

Можно записать несколько короче, не используя стандартную библиотеку и оставаясь в рамках C++11:

template<typename TResult, typename TArg, int remaining_args_count, typename... TArgs> class
t_n_args_fun_impl final
{
    public: using t_fun = typename t_n_args_fun_impl<TResult, TArg, remaining_args_count - 1, TArgs..., TArg>::t_fun;
};
template<typename TResult, typename TArg, typename... TArgs> class
t_n_args_fun_impl<TResult, TArg, 0, TArgs...> final
{
    public: using t_fun = TResult (TArgs...);
};
template<typename TResult, typename TArg, int args_count> using
t_n_args_fun = typename t_n_args_fun_impl<TResult, TArg, args_count>::t_fun;
#include <functional>
#include <type_traits>
int main()
{
    static_assert
    (
        ::std::is_same
        <
            ::std::function<t_n_args_fun<void, double, 5>>
        ,   ::std::function<void (double, double, double, double, double)>
        >::value
    ,   ""
    );
    static_assert
    (
        ::std::is_same
        <
            ::std::function<t_n_args_fun<void, void, 0>>
        ,   ::std::function<void (void)>
        >::value
    ,   ""
    );
    return(0);
}

онлайн компилятор

READ ALSO
Реализация алгоритма COS

Реализация алгоритма COS

Ребят, помогите найти код алгоритма COS(дискретное логарифмирование), очень надо

181
Реализация алгоритма Адлемана [требует правки]

Реализация алгоритма Адлемана [требует правки]

Ребят, помогите, пожалуйста найти код алгоритм Адлемана, очень надо

209
Java и mysql udf [требует правки]

Java и mysql udf [требует правки]

Как написать свою UDF для mysql на java? (для желающих больших вопросов добавлено) - нужны ссылки как надо написать на java, а не на С, что вызывать, как...

204
Android CertPathValidatorException как решить?

Android CertPathValidatorException как решить?

Я создаю SSLSocket для шифрованного подключения к серверамНа серверах настроено шифрование TLSv1

294