创建具有有限参数的 std::function 类型
Create a std::function type with limited arguments
给定可调用函数的类型C
,我想在编译时得到一个std::function
;其中的类型:
- 具有相同的 return 类型的函数
C
- 参数类型是函数的前
N
个参数类型C
这意味着,对于给定的类型void(int, char, double)
和给定的N
,函数的类型是:
N = 1
=> 结果类型:std::function<void(int)>
N = 2
=> 结果类型:std::function<void(int, char)>
N = 3
=> 结果类型:std::function<void(int, char, double)>
N > 3
=> 编译时错误
示例:
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...>());
};
它遵循一个可能的解决方案:
#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,
"!"
);
}
基本思路是使用 tuple
和标准模板库中的那些实用程序(例如,std::tuple_element
),将它们与放在右侧的包扩展混合地点,仅此而已。
为此,我使用了一个 constexpr
函数,该函数 returns 一个给定类型的空 std::function
对象。然后函数的类型通过 decltype
获取并使用别名导出为 FuncType
对象的类型。
一切都发生在编译时。
为了推导出该函数的正确类型,我使用了一个众所周知的模式,该模式涉及 std::integer_sequence
并通过扩展索引实际解包元组的类型。
另一种解决方案可能是:
#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,
"!"
);
}
编辑:
还有一个可能更简单一点(不需要 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,
"!"
);
}
另一个基于元组的解决方案。
应该也适用于 C++11。
我想它可以简化,但我不知道如何避免使用 bool
模板参数(我刚刚学习 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,
"!"
);
}
p.s.: 抱歉我的英语不好
正如我在评论中提到的,如果出现这样的问题,那么我的第一个方法是改变问题本身!有时候,与其找一个"tough solution"换一个"tough problem",还不如把问题本身弄成"simpler"!
永远不需要写 FuncType<2, R(X,Y,Z)>::type
而不是简单的 std::function<R(X,Y)>
.
以上是我的真实回答。为了解决您的编码问题,我给出了一个简单的基于宏的答案。它会在预处理时为您提供所需的类型,而不是编译时。
#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
用法:
int main() {
static_assert(std::is_same<
FuncType(2, void, int, char, double, int), // <--- see usage
std::function<void(int, char)>
>::value, "!");
}
如您所见,用法略有变化。我使用的不是 FuncType<2, void(int, char, double, int)>::type
,而是 FuncType(2, void, int, char, double, int)
。这里是 demo.
截至目前,FUNC_N
宏最多可重载 3 个参数。对于更多的参数,如果我们想避免复制粘贴,那么我们可以用一个简单的程序生成一个头文件:
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";
}
简单地#include
它:
#define F(R, ...) std::function<R(__VA_ARGS__)>
#define FuncType(N, R, ...) FUNC_##N(R, __VA_ARGS__)
#include"FUNC_N.h"
这里是demo如何生成头文件
给定可调用函数的类型C
,我想在编译时得到一个std::function
;其中的类型:
- 具有相同的 return 类型的函数
C
- 参数类型是函数的前
N
个参数类型C
这意味着,对于给定的类型void(int, char, double)
和给定的N
,函数的类型是:
N = 1
=> 结果类型:std::function<void(int)>
N = 2
=> 结果类型:std::function<void(int, char)>
N = 3
=> 结果类型:std::function<void(int, char, double)>
N > 3
=> 编译时错误
示例:
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...>());
};
它遵循一个可能的解决方案:
#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,
"!"
);
}
基本思路是使用 tuple
和标准模板库中的那些实用程序(例如,std::tuple_element
),将它们与放在右侧的包扩展混合地点,仅此而已。
为此,我使用了一个 constexpr
函数,该函数 returns 一个给定类型的空 std::function
对象。然后函数的类型通过 decltype
获取并使用别名导出为 FuncType
对象的类型。
一切都发生在编译时。
为了推导出该函数的正确类型,我使用了一个众所周知的模式,该模式涉及 std::integer_sequence
并通过扩展索引实际解包元组的类型。
另一种解决方案可能是:
#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,
"!"
);
}
编辑: 还有一个可能更简单一点(不需要 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,
"!"
);
}
另一个基于元组的解决方案。
应该也适用于 C++11。
我想它可以简化,但我不知道如何避免使用 bool
模板参数(我刚刚学习 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,
"!"
);
}
p.s.: 抱歉我的英语不好
正如我在评论中提到的,如果出现这样的问题,那么我的第一个方法是改变问题本身!有时候,与其找一个"tough solution"换一个"tough problem",还不如把问题本身弄成"simpler"!
永远不需要写 FuncType<2, R(X,Y,Z)>::type
而不是简单的 std::function<R(X,Y)>
.
以上是我的真实回答。为了解决您的编码问题,我给出了一个简单的基于宏的答案。它会在预处理时为您提供所需的类型,而不是编译时。
#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
用法:
int main() {
static_assert(std::is_same<
FuncType(2, void, int, char, double, int), // <--- see usage
std::function<void(int, char)>
>::value, "!");
}
如您所见,用法略有变化。我使用的不是 FuncType<2, void(int, char, double, int)>::type
,而是 FuncType(2, void, int, char, double, int)
。这里是 demo.
截至目前,FUNC_N
宏最多可重载 3 个参数。对于更多的参数,如果我们想避免复制粘贴,那么我们可以用一个简单的程序生成一个头文件:
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";
}
简单地#include
它:
#define F(R, ...) std::function<R(__VA_ARGS__)>
#define FuncType(N, R, ...) FUNC_##N(R, __VA_ARGS__)
#include"FUNC_N.h"
这里是demo如何生成头文件