Create type std :: function with limited arguments

Given the type of C function being called, I want to get a std::function ; whose type:

  • has the same function return type C
  • Argument types are the first type N arguments of function C

This means that for the given type void(int, char, double) and the given N type of function:

  • N = 1 => type of result: std::function<void(int)>
  • N = 2 => type of result: std::function<void(int, char)>
  • N = 3 => type of result: std::function<void(int, char, double)>
  • N > 3 => compile time error

Example:

 template<std::size_t N, typename R, typename... A> constexpr auto get() { return /*(magically somehow)*/ std::function<R(FirstNFromA...)> } template<std::size_t N, typename R, typename... A> struct S { using func = decltype(get<N, R, A...>()); }; 
+6
source share
4 answers

This follows a possible solution:

 #include <tuple> #include <utility> #include <functional> #include <type_traits> template< typename R, typename... A, std::size_t... I, std::enable_if_t<(sizeof...(I)<=sizeof...(A))>* = nullptr > constexpr auto get(std::integer_sequence<std::size_t, I...>) { return std::function<R(std::tuple_element_t<I, std::tuple<A...>>...)>{}; } template<std::size_t, typename> struct FuncType; template<std::size_t N, typename R, typename... A> struct FuncType<N, R(A...)> { using Type = decltype(get<R, A...>(std::make_index_sequence<N>{})); }; int main() { static_assert( std::is_same< FuncType<2, void(int, char, double, int)>::Type, std::function<void(int, char)> >::value, "!" ); } 

The main idea is to use tuple and those utilities that are part of the standard template library (as an example, std::tuple_element ), mix them with the package extension located in the right place, and that’s all. constexpr For this, I used the constexpr function, which returns an empty std::function object of this type. The function type is then taken using decltype and exported as the type of the FuncType object using an alias.
Everything happens at compile time.
To deduce the correct type for this function, I used a well-known template that includes std::integer_sequence and actually decompresses the tuple types, extending the indexes.

+6
source

Another solution might be:

 #include <tuple> #include <utility> #include <functional> #include <type_traits> template <size_t N, class R, class Pack, class ResultPack, class Voider> struct FuncTypeImpl; template <size_t N, class R, template <class...> class Pack, class First, class... Args, class... ResultArgs> struct FuncTypeImpl<N, R, Pack<First, Args...>, Pack<ResultArgs...>, std::enable_if_t<(N > 0)>>: FuncTypeImpl<N-1, R, Pack<Args...>, Pack<ResultArgs..., First>, void> { using typename FuncTypeImpl<N-1, R, Pack<Args...>, Pack<ResultArgs..., First>, void>::Type; }; template <size_t N, class R, template <class...> class Pack, class... Args, class... ResultArgs> struct FuncTypeImpl<N, R, Pack<Args...>, Pack<ResultArgs...>, std::enable_if_t<(N == 0)>> { using Type = std::function<R(ResultArgs...)>; }; template<std::size_t, typename> struct FuncType; template<std::size_t N, typename R, typename... A> struct FuncType<N, R(A...)> { using Type = typename FuncTypeImpl<N, R, std::tuple<A...>, std::tuple<>, void>::Type; }; int main() { static_assert( std::is_same< FuncType<3, void(int, char, double, int)>::Type, std::function<void(int, char, double)> >::value, "!" ); } 

Edit: Another one, maybe a little simpler (which does not require a solution to std :: tuple):

 #include <utility> #include <functional> #include <type_traits> template <class T> struct ResultOf; template <class R, class... Args> struct ResultOf<R(Args...)> { using Type = R; }; template<std::size_t N, class Foo, class ResultFoo = typename ResultOf<Foo>::Type() , class Voider = void> struct FuncType; template<std::size_t N, class R, class First, class... Args, class... ResultArgs > struct FuncType<N, R(First, Args...), R(ResultArgs...), std::enable_if_t<(N > 0)>>: FuncType<N-1, R(Args...), R(ResultArgs..., First), void> { }; template<std::size_t N, class R, class First, class... Args, class... ResultArgs > struct FuncType<N, R(First, Args...), R(ResultArgs...), std::enable_if_t<(N == 0)>> { using Type = std::function<R(ResultArgs...)>; }; int main() { static_assert( std::is_same< FuncType<3, void(int, char, double*, int)>::Type, std::function<void(int, char, double*)> >::value, "!" ); } 
+1
source

Another tuple-based solution.

Should work with C ++ 11 as well.

I suppose this might be simplified, but I don't know how to avoid using the bool template parameter (I'm just learning C ++ 11).

 #include <tuple> #include <utility> #include <functional> #include <type_traits> template<std::size_t N, bool Z, typename R, typename...> struct FTH1; template <typename R, typename... A, typename... B> struct FTH1<0U, true, R, std::tuple<A...>, B...> { using type = decltype(std::function<R(A...)>{}); }; template <std::size_t N, typename R, typename... A, typename B0, typename... B> struct FTH1<N, false, R, std::tuple<A...>, B0, B...> { using type = typename FTH1<N-1U, (N-1U == 0U), R, std::tuple<A..., B0>, B...>::type; }; template <std::size_t N, typename> struct FuncType; template<std::size_t N, typename R, typename... A> struct FuncType<N, R(A...)> { using Type = typename FTH1<N, (N == 0), R, std::tuple<>, A...>::type; }; int main() { static_assert( std::is_same< FuncType<2, void(int, char, double, int)>::Type, std::function<void(int, char)> >::value, "!" ); } 

ps: Sorry for my bad english.

+1
source

As I mentioned in the comments, if such a problem arises, then my first approach would be to change the problem itself! Sometimes, instead of finding a "hard solution" for a "hard problem", it is better to make the problem the most "simpler"!
It should not be necessary when you need to write FuncType<2, R(X,Y,Z)>::type instead of the simple std::function<R(X,Y)> .

Above is my real answer. To solve your problem as a pleasure from coding, I put a simple macro answer. Instead of compiling time, it will give you the type you need during preprocessing.

 #define F(R, ...) std::function<R(__VA_ARGS__)> // shorthand for std::function... #define FuncType(N, R, ...) FUNC_##N(R, __VA_ARGS__) // Main type #define FUNC_0(R, ...) F(R) // overload for 0 arg #define FUNC_1(R, _1, ...) F(R, _1) // overload for 1 arg #define FUNC_2(R, _1, _2, ...) F(R, _1, _2) // overload for 2 args #define FUNC_3(R, _1, _2, _3, ...) F(R, _1, _2, _3) // overload for 3 args 

Using:

 int main() { static_assert(std::is_same< FuncType(2, void, int, char, double, int), // <--- see usage std::function<void(int, char)> >::value, "!"); } 

As you can see, there is a slight change in use. Instead of FuncType<2, void(int, char, double, int)>::type , I use FuncType(2, void, int, char, double, int) . Here is a demon.


Currently, FUNC_N macros FUNC_N overloaded with up to 3 arguments. For additional arguments, if we want to avoid copying pastes, we can generate the header file with a simple program:

  std::ofstream funcN("FUNC_N.h"); // TODO: error check for argv & full path for file for(size_t i = 0, N = stoi(argv[1]); i < N; ++i) { funcN << "#define FUNC_" << i << "(R"; // FUNC_N for(size_t j = 1; j <= i; ++j) funcN << ", _" << j; // picking up required args funcN << ", ...) "; // remaining args funcN << "F(R"; // std::function for(size_t j = 1; j <= i; ++j) funcN << ", _" << j; // passing only relevant args funcN <<")\n"; } 

And just #include this:

 #define F(R, ...) std::function<R(__VA_ARGS__)> #define FuncType(N, R, ...) FUNC_##N(R, __VA_ARGS__) #include"FUNC_N.h" 

Below is a demo to create a header file.

+1
source

All Articles