I'm trying to wrap the Windows API functions to check errors when I so choose. As I found out in a previous SO question, I could use a template function to call the API function, and then call GetLastError()
to retrieve any error it might have set. I could then pass this error to my Error
class to let me know about it.
Here's the code for the template function:
template<typename TRet, typename... TArgs>
TRet Wrap(TRet(WINAPI *api)(TArgs...), TArgs... args)
{
TRet ret = api(args...);
//check for errors
return ret;
}
Using this I can have code as follows
int WINAPI someFunc (int param1, BOOL param2); //body not accessible
int main()
{
int ret = someFunc (5, true); //works normally
int ret2 = Wrap (someFunc, 5, true); //same as above, but I'll get a message if there's an error
}
This works wonderfully. However, there is one possible problem. Take the function
void WINAPI someFunc();
When subbing this into the template function, it looks as follows:
void Wrap(void(WINAPI *api)())
{
void ret = api(); //<-- ahem! Can't declare a variable of type void...
//check for errors
return ret; //<-- Can't return a value for void either
}
To get around this, I tried creating a version of the template where I replaced TRet
with void
. Unfortunately, this actually just causes an ambiguity of which one to use.
That aside, I tried using
if (strcmp (typeid (TRet).name(), "v") != 0) //typeid(void).name() == "v"
{
//do stuff with variable to return
}
else
{
//do stuff without returning anything
}
However, typeid
is a runtime comparison, so the code still doesn't compile due to trying to declare a void variable, even if it never will.
Next, I tried using std::is_same <TRet, void>::value
instead of typeid
, but found out that it was a runtime comparison as well.
At this point, I don't know what to try next. Is there any possibility of getting the compiler to believe that I know what I'm doing will run fine? I don't mind attaching an extra argument to Wrap
, but I couldn't really get anything out of that either.
I use Code::Blocks with GNU G++ 4.6.1, and Windows XP, as well as Windows 7. Thanks for any help, even if it is telling me that I'll have to end up just not using Wrap
for functions that return void.
All the template parameters are fixed+known at compile-time. If there are compiler errors due to template instantiation, they must be caught at compile-time!
Templates are instantiated in the process of converting each translated translation unit into an instantiation unit. A translation unit is essentially a source file. A translated translation unit (try to say that three times fast) is the output from compilation without templates instantiated.
7.2.The C++ compiler uses compile-time instantiation, which forces instantiations to occur when the reference to the template is being compiled.
You can use a helper class to fine tune specializations:
template <typename F>
struct wrapper
{};
template <typename Res, typename... Args>
struct wrapper<Res(Args...)>
{
static Res wrap(Res (WINAPI *f)(Args...), Args&& args...)
{
Res r = f(std::forward<Args>(args)...);
// Blah blah
return r;
}
};
template <typename... Args>
struct wrapper<void(Args...)>
{
static void wrap(void (WINAPI *f)(Args...), Args&& args...)
{
f(std::forward<Args>(args)...);
// Blah blah
}
};
Now, you can write the wrapper:
template <typename Res, typename... Args>
Res Wrap(Res (WINAPI *f)(Args...), Args&& args...)
{
return wrapper<Res(Args...)>::wrap(f, std::forward<Args>(args)...);
}
Note that it works even when Res
is void
. You're allowed to return
an expression returning void in a function returning void.
The correct type is deduced, as in Wrap(someFunc, 5, true)
, even for functions returning void.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With