有没有办法分解指向函数的模板化指针?
Is there a way to break down a templated pointer to a function?
目前我有一个这样的模板:
template<typename func, typename ret, typename... args> class Entry{
public:
PVOID Address;
ret operator()(args...){
return ((func) this->Address)(args...);
}
};
我是这样使用它的:
Entry<int(*)(int), int, int> func;
// ^func ^ret ^args
func.Address = (PVOID) 0xDEADC0DE;
func(123); // calls 0xDEADC0DE with '123' as argument
但是,我想知道是否有可能只有这个:
Entry<int(*)(int)> func;
// ^only specifying the function's prototype once instead of breaking it down
func(123);
如果这样,我将无法重载 operator()
,因为我无法将函数指针类型拆分为参数和 return 类型(这样我就可以编写 return_type operator()(args...)
).
有什么办法可以实现吗?
我正在使用 VS2013 2013 年 11 月 CTP
你可以通过这样的专业化来做到这一点:
// Entry has one template argument
template<typename func> class Entry;
// and if it's a function type, this specialization is used as best fit.
template<typename ret, typename... args> class Entry<ret(args...)>{
public:
PVOID Address;
ret operator()(args... a){
return ((ret(*)(args...)) this->Address)(a...);
}
};
int main() {
Entry<int(int)> foo;
foo.Address = (PVOID) 0xdeadc0de;
func(123);
}
要将它与您示例中的函数指针类型一起使用(尽管我更喜欢函数类型语法),写
// here ------v
template<typename ret, typename... args> class Entry<ret(*)(args...)>{
附录:我出去吃晚饭时又想到一件事:operator()
有一个(轻微的)问题,您可能会或可能不会担心:您不 运行 转入按值或按左值引用传递的参数的转发问题,因为它们只是在传递时传递(因为函数指针和 operator()
的参数列表完全相同),但是如果您打算使用右值引用参数,则这对它们隐式不起作用。为此,
Entry<int(int&&)> foo;
foo(123);
不编译。如果你打算将它与采用右值引用的函数一起使用,operator()
可以像这样固定:
ret operator()(args... a){
// explicit forwarding ----v
return ((ret(*)(args...)) this->Address)(std::forward<args>(a)...);
}
@Wintermutes post 中所示的部分专业化是可能的。
但是,如果没有它,你正在尝试的应该是可能的:
template <typename func>
class Entry{
public:
PVOID Address;
template <typename... Args>
auto operator()(Args&&... args)
-> decltype( ((func*) Address)(std::forward<Args>(args)...) ) {
return ((func*) Address)(std::forward<Args>(args)...);
}
};
模板参数必须是函数类型。但是,您可以通过在 return 表达式中稍作修改使其同时使用函数和指向函数类型的指针:使用 typename std::remove_pointer<func>::type*
而不是使用 func*
作为转换的目标类型, 即
template <typename... Args>
auto operator()(Args&&... args)
-> decltype( ((typename std::remove_pointer<func>::type*) Address)(std::forward<Args>(args)...) ) {
return ((typename std::remove_pointer<func>::type*) Address)(std::forward<Args>(args)...);
}
Demo.
一种元编程方法。首先,一些试图保留调用约定信息的指针特征:
template<class...>types {using type=types;};
enum class calling_convention {
cdecl,
clrcall,
stdcall,
fastcall,
thiscall,
vectorcall,
};
template<class Sig>
struct signature_properties;
template<class R, class...Args>
struct signature_properties {
using return_type = R;
using argument_types = types<Args...>;
};
template<class FuncPtr>
struct function_properties;
#define CAT_(A,B) A##B
#define CAT(A,B) CAT_(A,B)
#define CALLING_CONVENTION_SUPPORT( CONVENTION ) \
template<class R, class... Args> \
struct function_properties< R(CAT(__, CONVENTION) *)(Args...) >: \
signature_properties<R(Args...)> \
{ \
using type = R(CAT(__, CONVENTION) *)(Args...) \
static const calling_convention convention = calling_convention::CONVENTION; \
static type from_pvoid(void const* pvoid) { \
return static_cast<type>(pvoid); \
} \
}
CALLING_CONVENTION_SUPPORT(cdecl);
CALLING_CONVENTION_SUPPORT(clrcall);
CALLING_CONVENTION_SUPPORT(stdcall);
CALLING_CONVENTION_SUPPORT(fastcall);
CALLING_CONVENTION_SUPPORT(thiscall);
CALLING_CONVENTION_SUPPORT(vectorcall);
#undef CAT
#undef CAT_
#undef CALLING_CONVENTION_SUPPORT
恶心的宏。并且严重矫枉过正。并且未经测试。但是你明白了。
接下来,帮手来做这项工作:
template<class FuncPtrType, class R, class Args>
struct helper;
template<class FuncPtrType, class R, class... Args>
struct helper<FuncPtrType, R, types<Args...>> {
FuncPtrType ptr;
R operator()(Args...args)const {
return ptr(std::forward<Args>(args)...);
}
helper(FuncPtrType p):ptr(p) {};
helper( helper const& )=default;
helper& operator=( helper const& )=default;
};
helper中的完美转发也很诱人
最后,我们使用上面的特征 class 将工作从 Entry
反弹到 helper
:
template<class FuncPtrType>
struct Entry:helper<
FuncPtrType,
typename signature_properties<FuncPtrType>::return_type,
typename signature_properties<FuncPtrTpye>::arguments
> {
using parent = helper<
FuncPtrType,
typename signature_properties<FuncPtrType>::return_type,
typename signature_properties<FuncPtrTpye>::arguments
>;
Entry(void const* pvoid):parent( static_cast<FuncPtrType>(pvoid) ) {}
};
除了我们在 Entry
中包含一个构造函数以获取 void const*
并将类型化指针转发给 helper
。
一个变化是我们从 void*
转换为我们最早知道它是函数类型的函数类型,而不是在我们调用的时候。
目前我有一个这样的模板:
template<typename func, typename ret, typename... args> class Entry{
public:
PVOID Address;
ret operator()(args...){
return ((func) this->Address)(args...);
}
};
我是这样使用它的:
Entry<int(*)(int), int, int> func;
// ^func ^ret ^args
func.Address = (PVOID) 0xDEADC0DE;
func(123); // calls 0xDEADC0DE with '123' as argument
但是,我想知道是否有可能只有这个:
Entry<int(*)(int)> func;
// ^only specifying the function's prototype once instead of breaking it down
func(123);
如果这样,我将无法重载 operator()
,因为我无法将函数指针类型拆分为参数和 return 类型(这样我就可以编写 return_type operator()(args...)
).
有什么办法可以实现吗?
我正在使用 VS2013 2013 年 11 月 CTP
你可以通过这样的专业化来做到这一点:
// Entry has one template argument
template<typename func> class Entry;
// and if it's a function type, this specialization is used as best fit.
template<typename ret, typename... args> class Entry<ret(args...)>{
public:
PVOID Address;
ret operator()(args... a){
return ((ret(*)(args...)) this->Address)(a...);
}
};
int main() {
Entry<int(int)> foo;
foo.Address = (PVOID) 0xdeadc0de;
func(123);
}
要将它与您示例中的函数指针类型一起使用(尽管我更喜欢函数类型语法),写
// here ------v
template<typename ret, typename... args> class Entry<ret(*)(args...)>{
附录:我出去吃晚饭时又想到一件事:operator()
有一个(轻微的)问题,您可能会或可能不会担心:您不 运行 转入按值或按左值引用传递的参数的转发问题,因为它们只是在传递时传递(因为函数指针和 operator()
的参数列表完全相同),但是如果您打算使用右值引用参数,则这对它们隐式不起作用。为此,
Entry<int(int&&)> foo;
foo(123);
不编译。如果你打算将它与采用右值引用的函数一起使用,operator()
可以像这样固定:
ret operator()(args... a){
// explicit forwarding ----v
return ((ret(*)(args...)) this->Address)(std::forward<args>(a)...);
}
@Wintermutes post 中所示的部分专业化是可能的。
但是,如果没有它,你正在尝试的应该是可能的:
template <typename func>
class Entry{
public:
PVOID Address;
template <typename... Args>
auto operator()(Args&&... args)
-> decltype( ((func*) Address)(std::forward<Args>(args)...) ) {
return ((func*) Address)(std::forward<Args>(args)...);
}
};
模板参数必须是函数类型。但是,您可以通过在 return 表达式中稍作修改使其同时使用函数和指向函数类型的指针:使用 typename std::remove_pointer<func>::type*
而不是使用 func*
作为转换的目标类型, 即
template <typename... Args>
auto operator()(Args&&... args)
-> decltype( ((typename std::remove_pointer<func>::type*) Address)(std::forward<Args>(args)...) ) {
return ((typename std::remove_pointer<func>::type*) Address)(std::forward<Args>(args)...);
}
Demo.
一种元编程方法。首先,一些试图保留调用约定信息的指针特征:
template<class...>types {using type=types;};
enum class calling_convention {
cdecl,
clrcall,
stdcall,
fastcall,
thiscall,
vectorcall,
};
template<class Sig>
struct signature_properties;
template<class R, class...Args>
struct signature_properties {
using return_type = R;
using argument_types = types<Args...>;
};
template<class FuncPtr>
struct function_properties;
#define CAT_(A,B) A##B
#define CAT(A,B) CAT_(A,B)
#define CALLING_CONVENTION_SUPPORT( CONVENTION ) \
template<class R, class... Args> \
struct function_properties< R(CAT(__, CONVENTION) *)(Args...) >: \
signature_properties<R(Args...)> \
{ \
using type = R(CAT(__, CONVENTION) *)(Args...) \
static const calling_convention convention = calling_convention::CONVENTION; \
static type from_pvoid(void const* pvoid) { \
return static_cast<type>(pvoid); \
} \
}
CALLING_CONVENTION_SUPPORT(cdecl);
CALLING_CONVENTION_SUPPORT(clrcall);
CALLING_CONVENTION_SUPPORT(stdcall);
CALLING_CONVENTION_SUPPORT(fastcall);
CALLING_CONVENTION_SUPPORT(thiscall);
CALLING_CONVENTION_SUPPORT(vectorcall);
#undef CAT
#undef CAT_
#undef CALLING_CONVENTION_SUPPORT
恶心的宏。并且严重矫枉过正。并且未经测试。但是你明白了。
接下来,帮手来做这项工作:
template<class FuncPtrType, class R, class Args>
struct helper;
template<class FuncPtrType, class R, class... Args>
struct helper<FuncPtrType, R, types<Args...>> {
FuncPtrType ptr;
R operator()(Args...args)const {
return ptr(std::forward<Args>(args)...);
}
helper(FuncPtrType p):ptr(p) {};
helper( helper const& )=default;
helper& operator=( helper const& )=default;
};
helper中的完美转发也很诱人
最后,我们使用上面的特征 class 将工作从 Entry
反弹到 helper
:
template<class FuncPtrType>
struct Entry:helper<
FuncPtrType,
typename signature_properties<FuncPtrType>::return_type,
typename signature_properties<FuncPtrTpye>::arguments
> {
using parent = helper<
FuncPtrType,
typename signature_properties<FuncPtrType>::return_type,
typename signature_properties<FuncPtrTpye>::arguments
>;
Entry(void const* pvoid):parent( static_cast<FuncPtrType>(pvoid) ) {}
};
除了我们在 Entry
中包含一个构造函数以获取 void const*
并将类型化指针转发给 helper
。
一个变化是我们从 void*
转换为我们最早知道它是函数类型的函数类型,而不是在我们调用的时候。