Recently, I came up with an idea when learning to call function pointers. I used template variable parameters to construct template functions so that I can call function pointers
#include<functional>
#include<Windows.h>
template<class T, class ...Args>
decltype(auto) ExecuteFunc(LPVOID f, Args&& ...args) {
if (f != nullptr) {
return std::function<T>((T*)f)(std::forward<Args>(args)...);
}
}
int main(){
ExecuteFunc<int(HWND, char*, char*, int)>(&MessageBoxA, (HWND)NULL, (char*)"Text", (char*)"caption", MB_OK);
return 0;
}
Although this code runs well, it seems a bit too long picture Is there a way to shorten the code?
I hope the code can be simplified to call.
ExecuteFunc<int>(&MessageBoxA, (HWND)NULL, (char*)"Text", (char*)"caption", MB_OK);
If you have some interesting ideas, please express them freely.
Supplementary description. Some of the answers misunderstood my question and changed the type of the argument, which apparently std::invoke doesn't do yet. Argument one is passed in as a function pointer address, not the name of an already exported function Example 2.
LPVOID FunAddr=&MessageBoxA;
int ret=ExecuteFunc<int>(FunAddr, (HWND)NULL, (char*) "Text", (char*) "caption", MB_OK);
int ret=ExecuteFunc<int> means that the return value of the function is of type int FunAddr is the address of a function where you can't directly give the symbolic name of the function the rest of the function's parameters
Also I know that FunAddr doesn't have any available type information, but the parameters given can extract this type information and splice it to T(int,int)
template<class T,class . .Args>
T ExecuteFunc(LPVOID f, Args&&... .args) {
}
CodePudding user response:
There's an issue with the implementation of ExecuteFunc
: It doesn't return a value on every path which is undefined behaviour.
To me this looks like you're trying to reinvent std::invoke
though. std::invoke
can do anything other than execute the null check for you.
std::invoke(&MessageBoxA, nullptr, "Text", "caption", MB_OK);
If you really want to implement this yourself, you could be using something like this:
template<class F, class ...Args>
decltype(auto) ExecuteFunc(F&& f, Args&&...args)
{
if constexpr (std::is_pointer_v<F>)
{
using ResultType = decltype(std::declval<F>()(std::forward<Args>(args)...));
if (f == nullptr)
{
// return the default constructed return value, if a null function pointer is passed
return ResultType{};
}
}
return std::forward<F>(f)(std::forward<Args>(args)...);
}
int AddOne(int i)
{
return i 1;
}
int main()
{
static_assert(std::is_same_v<decltype(ExecuteFunc(&MessageBoxA, nullptr, "Text", "caption", MB_OK)), int>); // just to show the return type is as expected for MessageBoxA
std::cout << ExecuteFunc(&AddOne, 5) << '\n';
std::cout << ExecuteFunc(static_cast<decltype(&AddOne)>(nullptr), 5) << '\n';
std::cout << ExecuteFunc([](int i) {return i 1; }, 5) << '\n';
}
CodePudding user response:
[enter image description here][1]
#include<functional>
#include<Windows.h>
template<class T, class ...Args>
decltype(auto) ExecuteFunc(T f, Args&& ...args) {
f(args...);
}
int main() {
ExecuteFunc(std::function{ MessageBoxA }, (HWND)NULL, (char*)"Text", (char*)"caption", MB_OK);
return 0;
}