Home > Software engineering >  How to simplify variable parameter template functions?
How to simplify variable parameter template functions?

Time:10-03

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;
}
  • Related