I am trying to forward non-template variadic arguments between two functions. None of those functions is mine so I can't change their signatures. I used va_args
for that purpose but (unluckily) my solution does not work properly under MSVC. Probably due to a stack overlapping I end up with a random value picked from the stack.
Here is the minimal example to reproduce my problem. Functions funcA
and funcB
have the same signature as the functions from my original problem.
#include <stdarg.h>
int funcA(int a, void* b, ...)
{
va_list args;
va_start(args, b);
auto value = a + va_arg(args, int);
va_end(args);
return value;
}
int funcB(int a, void* b, ...)
{
va_list args;
va_start(args, b);
auto value = funcA(a, args);
va_end(args);
return value;
}
int main() {
return funcB(2, nullptr, 163);
}
Possible output GCC:
Program returned: 165
Possible output MSVC:
Program returned: 7731509
You can run this code from here
Above solution works correctly and as expected under GCC, but under MSVC this code returns rubbish number.
I've tried to adjust this minimal example by explicitly defining calling convention to __cdecl
but this does not help. I have also tried to compile it using different optimization flags but it didn't change anything;
Please note that I've seen many similar questions here, but none of them answered my question related to MSVC.
I've checked how printf
function forwards those arguments, but unluckily it does pass it to _vfprintf_s_l
using va_list
and that's what I can't do in my case.
_Check_return_opt_
_CRT_STDIO_INLINE int __CRTDECL _printf_s_l(
_In_z_ _Printf_format_string_params_(0) char const* const _Format,
_In_opt_ _locale_t const _Locale,
...)
#if defined _NO_CRT_STDIO_INLINE
;
#else
{
int _Result;
va_list _ArgList;
__crt_va_start(_ArgList, _Locale);
_Result = _vfprintf_s_l(stdout, _Format, _Locale, _ArgList);
__crt_va_end(_ArgList);
return _Result;
}
#endif
EDIT
The b
parameter here auto value = funcA(a, args);
was removed on purpose just to test it out under GCC. Even if I bring it back, it won't work under either GCC and MSVC.
My real/original problem is related to function hooking. I have to place a hook into a function exported by the .dll with this kind of variadic interface. So, when I do that, I end up with code which looks more or less like this:
// orig::exported_func
// this is the original function exported by the .dll
// I don't have access to its implementation
//
// hook::exported_func
// this is my stub hook function. The hook is placed in runtime,
// by overriding the original function asm code. After hooking,
// original function address is stored inside orig::exported_func.
int hook::exported_func(int a, void* b, ...) {
// here is the place I need to forward this variadic arguments
// I dont even need to read them, the thing it to pass proper stack size
// Line below is just and example, it wont compile
auto retVal = orig::exported_func(a, b, ...);
.
.
.
// do some not related staff
.
.
.
return retVal;
}
I am trying to forward non-template variadic arguments between two functions. None of those functions is mine so I can't change their signatures. I used va_args
for that purpose but (unluckily) my solution does not work properly under MSVC. Probably due to a stack overlapping I end up with a random value picked from the stack.
Here is the minimal example to reproduce my problem. Functions funcA
and funcB
have the same signature as the functions from my original problem.
#include <stdarg.h>
int funcA(int a, void* b, ...)
{
va_list args;
va_start(args, b);
auto value = a + va_arg(args, int);
va_end(args);
return value;
}
int funcB(int a, void* b, ...)
{
va_list args;
va_start(args, b);
auto value = funcA(a, args);
va_end(args);
return value;
}
int main() {
return funcB(2, nullptr, 163);
}
Possible output GCC:
Program returned: 165
Possible output MSVC:
Program returned: 7731509
You can run this code from here https://godbolt./z/Mjc8E3onW
Above solution works correctly and as expected under GCC, but under MSVC this code returns rubbish number.
I've tried to adjust this minimal example by explicitly defining calling convention to __cdecl
but this does not help. I have also tried to compile it using different optimization flags but it didn't change anything;
Please note that I've seen many similar questions here, but none of them answered my question related to MSVC.
I've checked how printf
function forwards those arguments, but unluckily it does pass it to _vfprintf_s_l
using va_list
and that's what I can't do in my case.
_Check_return_opt_
_CRT_STDIO_INLINE int __CRTDECL _printf_s_l(
_In_z_ _Printf_format_string_params_(0) char const* const _Format,
_In_opt_ _locale_t const _Locale,
...)
#if defined _NO_CRT_STDIO_INLINE
;
#else
{
int _Result;
va_list _ArgList;
__crt_va_start(_ArgList, _Locale);
_Result = _vfprintf_s_l(stdout, _Format, _Locale, _ArgList);
__crt_va_end(_ArgList);
return _Result;
}
#endif
EDIT
The b
parameter here auto value = funcA(a, args);
was removed on purpose just to test it out under GCC. Even if I bring it back, it won't work under either GCC and MSVC.
My real/original problem is related to function hooking. I have to place a hook into a function exported by the .dll with this kind of variadic interface. So, when I do that, I end up with code which looks more or less like this:
// orig::exported_func
// this is the original function exported by the .dll
// I don't have access to its implementation
//
// hook::exported_func
// this is my stub hook function. The hook is placed in runtime,
// by overriding the original function asm code. After hooking,
// original function address is stored inside orig::exported_func.
int hook::exported_func(int a, void* b, ...) {
// here is the place I need to forward this variadic arguments
// I dont even need to read them, the thing it to pass proper stack size
// Line below is just and example, it wont compile
auto retVal = orig::exported_func(a, b, ...);
.
.
.
// do some not related staff
.
.
.
return retVal;
}
Share
Improve this question
edited Nov 19, 2024 at 23:20
Jonathan Leffler
755k145 gold badges949 silver badges1.3k bronze badges
asked Nov 19, 2024 at 22:14
Johny Siemano KolanoJohny Siemano Kolano
1477 bronze badges
1
- Comments have been moved to chat; please do not continue the discussion here. Before posting a comment below this one, please review the purposes of comments. Comments that do not request clarification or suggest improvements usually belong as an answer, on Meta Stack Overflow, or in Stack Overflow Chat. Comments continuing discussion may be removed. – blackgreen ♦ Commented Nov 20, 2024 at 17:36
3 Answers
Reset to default 2How to properly forward non-template, variadic arguments in C/C++ under MSVC
There is no generic way in C or C++ to forward the variable arguments of one C-style variadic function to another.
In particular, va_list
is a type like any other, not a magic language feature. You can pass one to a function (by value), or a pointer to one, or in C++ you can pass a reference to one, but none of those are in any way equivalent to forwarding the original variable arguments themselves, not even when you pass it to a variadic function. If doing so appears to work as you expect under some particular circumstances, then you can count yourself unlucky in that regard, as such a result is misleading at best.
You can unpack some number of variable arguments from the va_list
and pass their specific values to the other function as ordinary arguments, but this is rarely satisfactory, because you need a separate case for each combination of argument count and types.
Since you say that neither of the functions involved is yours, I guess you have inherited someone else's mess. If possible, then, push the issue back on the responsible miscreant. But since you ask the question, I suppose you are in a position to fix the issue for yourself. In that case, one reasonable way to proceed would be to split funcA()
into a variadic front end, and a va_list-based back end. Then funcB()
would pass a va_list
to the new funcA()
back end instead of to the variadic front end. Like so:
int funcAva(int a, void* b, va_list args)
{
return a + va_arg(args, int);
}
int funcA(int a, void* b, ...)
{
va_list args;
va_start(args, b);
auto value = funcAva(a, b, args);
va_end(args);
return value;
}
int funcB(int a, void* b, ...)
{
va_list args;
va_start(args, b);
auto value = funcAva(a, b, args);
va_end(args);
return value;
}
That preserves the signature of funcA()
while providing a way for funcB()
to elicit the needed behavior.
Addendum
The updated question suggests that rather than inheriting someone else's mess, you are trying to create one of your own. In that case, the original question presented a big red herring in the form of your ideas about modifying the original functions. There is no reason to think that would help you.
Under some circumstances, you can wrap a variadic function with a variadic macro. Perhaps that would serve your purpose. Perhaps not.
If you can't change the function signatures, but can change the contents, then you can stub out funcA
to call another function that takes a va_list
, then have funcB
call that instead.
int funcA_impl(int a, void* b, va_list args)
{
int value = a + va_arg(args, int);
return value;
}
int funcA(int a, void* b, ...)
{
va_list args;
va_start(args, b);
int value = funcA_impl(a, b, args);
va_end(args);
return value;
}
int funcB(int a, void* b, ...)
{
va_list args;
va_start(args, b);
int value = funcA_impl(a, b, args);
va_end(args);
return value;
}
Your code is a big undefined behaviour - and that's why it behavies differently. If you cant change the functions, then you are out of luck.
Here you have the corrected version:
#include <stdarg.h>
int funcA(int a, void* b, va_list args)
{
auto value = a + va_arg(args, int);
return value;
}
int funcB(int a, void* b, ...)
{
va_list args;
va_start(args, b);
auto value = funcA(a, b, args);
va_end(args);
return value;
}
int main() {
return funcB(2, nullptr, 163);
}
In C++ you have more options:
- Templates:
#include <iostream>
// Variadic template function
template <typename... Args>
void processArgs(const Args&... args)
{
((std::cout << args << std::endl), ...); //
}
// Variadic function
template <typename... Args>
void passArgs(const Args&... args)
{
processArgs(args...);
}
int main()
{
passArgs(42, 'A', 3.14);
return 0;
}
- std::initializer_list
#include <iostream>
#include <initializer_list>
void processArgs(const std::initializer_list<int>& args)
{
for (int arg : args) {
std::cout << "Integer: " << arg << std::endl;
}
}
void passArgs(std::initializer_list<int> args)
{
processArgs(args);
}
int main()
{
passArgs({42, 7, 13}); // Pass a list of integers
return 0;
}
#include <iostream>
#include <vector>
#include <any>
void processArgs(const std::vector<std::any>& args)
{
for (const auto& arg : args) {
if (arg.type() == typeid(int)) {
std::cout << "Integer: " << std::any_cast<int>(arg) << std::endl;
} else if (arg.type() == typeid(char)) {
std::cout << "Character: " << std::any_cast<char>(arg) << std::endl;
} else if (arg.type() == typeid(double)) {
std::cout << "Double: " << std::any_cast<double>(arg) << std::endl;
} else {
std::cout << "Unknown type!" << std::endl;
}
}
}
void passArgs(const std::vector<std::any>& args)
{
processArgs(args);
}
int main()
{
passArgs({42, 'A', 3.14}); // Pass a heterogeneous list of arguments
return 0;
}