c++ – Declaring a function of n variables

Question:

There is a number n . How to declare a function of n variables of the same type? The situation is something like this:

using my_type = int;
constexpr size_t n = 4;

std::function<void(give_me_n_types<my_type>(n))> my_func;

Answer:

The first thing that came to mind:

#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

It works as follows.

make_function_type_n<double, int, 2>

The templated using expands to:

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

In turn, this boilerplate using expands to

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

::std::make_index_sequence<2>() will create an object of type

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

those. we have an expression in decltype

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

Therefore, we get the following:

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

So decltype will give a reference to a function of type double(int, int) and the reference is removed using std :: remove_reference_t.

While I was writing, the idea arose to repeat not one parameter, but several.

First, you need a structure that will store the desired type of function and increase its parameters.

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...>;
    };

All that remains to be done is to create func_n_t with parameters repeated N times.

    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;

Usage:

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

Complete code:

#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

Scroll to Top