我正在制作一个类模板来编码函数指针。该函数可以具有任何结果类型和参数的数量/类型。这就是我所拥有的:
LPVOID EncodePtr(LPVOID ptr) {
// Encode...
return ptr;
}
LPVOID DecodePtr(LPVOID ptr) {
// Decode...
return ptr;
}
template<class T>
class encoded_ptr {
public:
typedef encoded_ptr<T> _Myt;
encoded_ptr() {
ptr_ = (T*)EncodePtr(nullptr);
}
// Irresponsible?
template<class _OtherType>
encoded_ptr(_OtherType ptr) {
ptr_ = (T*)DecodePtr((LPVOID)ptr);
}
~encoded_ptr() {
ptr_ = (T*)EncodePtr(nullptr);
}
// Makes it possible to call the function directly
template<class... _Args>
typename std::result_of<T*(_Args...)>::type operator()(_Args... _Ax) {
T* fn = get();
return fn(_Ax...);
}
T* get() const {
return (T*)DecodePtr((LPVOID)ptr_);
}
bool is_set() {
return (get() != nullptr);
}
private:
T* ptr_;
};
它按预期工作。例如:
encoded_ptr<decltype(MessageBoxA)> MsgBox;
MsgBox = &MessageBoxA; // Could also initialize in the constructor
// (HWND)0 is justified by the actual problem in the question
MsgBox((HWND)0, "Test message!", "Test", 0);
第一个问题是声明括号运算符()
的方式不允许Visual Studio的IntelliSense发挥其魔力并给出关于函数参数的提示:
template<class... _Args>
typename std::result_of<T*(_Args...)>::type operator()(_Args... _Ax) {
T* fn = get();
return fn(_Ax...);
}
我没有使用(_Args... _Ax)
,而是想解压实际的函数参数,以便IntelliSense可以正确地给出提示。
目前的行为是:
预期的行为是:
第二个问题是以这种方式调用函数,编译器不进行基本强制转换,迫使我将NULL
强制转换为(void*)NULL
,0
转换为(HWND)0
等。当使用具有大量参数的函数时,这很烦人。
也许在实施中存在一些错误,但我不是模板专家。另外,我不知道问题的标题是否合适。
我感谢任何帮助。
修改
到目前为止我已尝试过(@ OlegBogdanov的建议):
template<class T, class... Args>
class encoded_ptr;
template<class T, class... Args>
class encoded_ptr<T(Args...)> {
public:
typedef encoded_ptr<T> _Myt;
using Fptr = T(*)(Args...);
encoded_ptr(Fptr ptr) {
ptr_ = (Fptr)EncodePtr((LPVOID)ptr);
}
// Makes it possible to call the function directly
typename T operator()(Args... _Ax) {
Fptr fn = get();
return fn(std::forward<Args>(_Ax)...);
}
Fptr get() const {
return (T*)DecodePtr((LPVOID)ptr_);
}
bool is_set() {
return (get() != nullptr);
}
private:
Fptr ptr_;
};
结果:无法实例化/使用构造函数:Incomplete type is not allowed
。
修改
这是正确的方向,问题是调用惯例。
更改:
class encoded_ptr<T(Args...)>
至class encoded_ptr<T(__stdcall)(Args...)>
和
using Fptr = T(*)(Args...)
至using Fptr = T(__stdcall*)(Args...)
我正在尝试检测调用约定而不是硬编码。
答案 0 :(得分:1)
我认为你的期望
template<class... _Args>
typename std::result_of<T*(_Args...)>::type operator()(_Args... _Ax) {
T* fn = get();
return fn(_Ax...);
}
错了。它完全忽略了你的目标函数参数列表(你使用了糟糕的芒类型擦除)和传递(我想使用单词&#39;转发&#39;但这将是不准确的)无论调用者给出什么。因此0在
MsgBox(0, "Test message!", "Test", 0);
推断为int
,您必须将其强制转换为HWND
,否则编译器无法猜测它。
你真正在做的是重新发明std::function或其上面的包装器。
如果您真的认为std::function不足以满足您的需求,您将不得不复制其部分实施,即您至少需要
template<class R, class... Args>
class encoded_ptr; // leaving this undefined
template<class R, class... Args>
class encoded_ptr<R(Args...)> {
using Fptr = R(*)(Args...);
encoded_ptr(Fptr ptr) {
ptr_ = (T*)DecodePtr((LPVOID)ptr);
}
...
捕获类型
中的参数列表并且调用运算符将重用它而不是随机键入的传递参数:
// this must not be here -> template<class... _Args>
R operator()(Args... _Ax) {
T* fn = get()
return fn(std::forward<Args>(_Ax)...);
}
编辑:
您无法再存储T*
,T
只是一种返回类型,按Fptr
存储