I am writing a wrapper to call API functions in a vtable. This is done through a variadic temple, to wrap API functions with an arbitrary number of parameters. I have found that some calls work only if the numbers passed as arguments are forced to be long long:
vtablecall(pIntf, methodnumber, TRUE, 0, 0); //CRASHES
vtablecall(pIntf, methodnumber, (long long)TRUE, (long long)0, (long long)0); //WORKS
The problem is that the compiler does not enforce (long long) by itself. Debugging shows that the TRUE or 0 arguments are considered int. Then maybe they are somehow optimized by the compiler (my suspicion) before being sent to the pointed function.
My goal: what syntax use in the template so that all integer arguments are promoted to long long (and if possible all real numbers promoted to double)? Putting (long long) every time isn't a good solution because of the risk of mistake, and initializer_list isn't an option because some parameters may be real numbers.
Thanks!
template <typename... pack> HRESULT WINAPI vtablecall(IUnknown* pIntf, const int methodnumber, pack... args){
HRESULT WINAPI (*psub)(IUnknown*, pack...)=(HRESULT WINAPI (*)(IUnknown*, pack...))((LPVOID**)pIntf)[0][methodnumber];
//// breakpoint here
return psub(pIntf, std::forward<pack>(args)...);
}
int main() {
IUnknown* pIntf=...
int mymethodnumber=43;
vtablecall(pIntf, mymethodnumber, pIntf, methodnumber, TRUE, 0, 0);
}
---> CRASHES: Process exited with status = 0x80131506
Frame variables in debugger before calling 'psub':
(IUnknown *) pIntf = 0x00000272be28ffa0
(const int) methodnumber = 43
(int) args = 1
(int) args = 0
(int) args = 0
(HRESULT (*)(IUnknown *, int, int, int)) psub = 0x000002aee04146ba
Now, with setting explicitly arguments to long long:
int main() {
IUnknown* pIntf=...
int mymethodnumber=43;
vtablecall(pIntf, mymethodnumber, pIntf, methodnumber, (long long)TRUE, (long long)0, (long long)0);
}
---> works OK
Frame variables in debugger before calling 'psub':
(IUnknown *) pIntf = 0x0000024bd525ffa0
(const int) methodnumber = 43
(long long) args = 1
(long long) args = 0
(long long) args = 0
(HRESULT (*)(IUnknown *, long long, long long, long long)) psub = 0x0000024bd57e46ba
CodePudding user response:
You can use std::conditional
to define your own type
template<typename T>
using my_type_t = std::conditional_t<std::is_integral_v<T>, long long, T>;
If T
is an integer of any type, then my_type_t<T>
will be long long
, otherwise it will be T
.
So you can use it as such
...
HRESULT WINAPI (*psub)(IUnknown*, my_type_t<pack>...) = ...