Passing variable arguments to another function that accepts a variable argument list

C++Variadic Functions

C++ Problem Overview


So I have 2 functions that both have similar arguments

void example(int a, int b, ...);
void exampleB(int b, ...);

Now example calls exampleB, but how can I pass along the variables in the variable argument list without modifying exampleB (as this is already used elsewhere too).

C++ Solutions


Solution 1 - C++

You can't do it directly; you have to create a function that takes a va_list:

#include <stdarg.h>

static void exampleV(int b, va_list args);

void exampleA(int a, int b, ...)    // Renamed for consistency
{
    va_list args;
    do_something(a);                // Use argument a somehow
    va_start(args, b);
    exampleV(b, args);
    va_end(args);
}

void exampleB(int b, ...)
{
    va_list args;
    va_start(args, b);
    exampleV(b, args);
    va_end(args);
}

static void exampleV(int b, va_list args)
{
    ...whatever you planned to have exampleB do...
    ...except it calls neither va_start nor va_end...
}

Solution 2 - C++

Maybe throwin a rock in a pond here, but it seems to work pretty OK with C++11 variadic templates:

#include <stdio.h>

template<typename... Args> void test(const char * f, Args... args) {
  printf(f, args...);
}

int main()
{
  int a = 2;
  test("%s\n", "test");
  test("%s %d %d %p\n", "second test", 2, a, &a);
}

At the very least, it works with g++.

Solution 3 - C++

you should create versions of these functions which take a va_list, and pass those. Look at vprintf as an example:

int vprintf ( const char * format, va_list arg );

Solution 4 - C++

I also wanted to wrap printf and found a helpful answer here:

https://stackoverflow.com/questions/1056411/how-to-pass-variable-number-of-arguments-to-printf-sprintf

I was not at all interested in performance (I'm sure this piece of code can be improved in a number of ways, feel free to do so :) ), this is for general debugprinting only so I did this:

//Helper function
std::string osprintf(const char *fmt, ...)
{
    va_list args;
    char buf[1000];
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args );
    va_end(args);
    return buf;
}

which I then can use like this

Point2d p;

cout << osprintf("Point2d: (%3i, %3i)", p.x, p.y);
instead of for example:
cout << "Point2d: ( " << setw(3) << p.x << ", " << p.y << " )";

The c++ ostreams are beautiful in some aspects, but practically the become horrific if you want to print something like this with some small strings such as parenthesis, colons and commas inserted between the numbers.

Solution 5 - C++

A possible way is to use #define:

#define exampleB(int b, ...)  example(0, b, __VA_ARGS__)

Solution 6 - C++

Incidentally, many C implementations have an internal v?printf variation which IMHO should have been part of the C standard. The exact details vary, but a typical implementation will accept a struct containing a character-output function pointer and information saying what's supposed to happen. This allows printf, sprintf, and fprintf to all use the same 'core' mechanism. For example, vsprintf might be something like:

void s_out(PRINTF_INFO *p_inf, char ch)
{
  (*(p_inf->destptr)++) = ch;
  p_inf->result++;
}

int vsprintf(char *dest, const char *fmt, va_list args)
{
  PRINTF_INFO p_inf;
  p_inf.destptr = dest;
  p_inf.result = 0;
  p_inf.func = s_out;
  core_printf(&p_inf,fmt,args);
}

The core_printf function then calls p_inf->func for each character to be output; the output function can then send the characters to the console, a file, a string, or something else. If one's implementation exposes the core_printf function (and whatever setup mechanism it uses) one can extend it with all sorts of variations.

Solution 7 - C++

It might not be exactly the same situation as described here, but if you were to define a wrapper for a string format function (e.g. logger):

void logger(const char *name, const char *format, ...);
void wrapper(const char *format, ...);

when you implement a wrapper that calls logger, we can just create a string first with vasprintf and then pass it to logger.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

static void wrapper(const char *format, ...)
{
    char *string;
    va_list args;
    va_start(args, format);

    // variadic printf with allocated string. must free()
    vasprintf(&string, format, args);
    logger("wrapper", "%s", string);

    free(string);
    va_end(args);
}

Not the cleanest, but works. Try this when you must avoid using macro functions.

Solution 8 - C++

Based on the comment that you're wrapping vsprintf, and that this is tagged as C++ I'd suggest not trying to do this, but change up your interface to use C++ iostreams instead. They have advantages over the print line of functions, such as type safety and being able to print items that printf wouldn't be able to handle. Some rework now could save a significant amount of pain in the future.

Solution 9 - C++

This is the only way to do it.. and the best way to do it too..

static BOOL(__cdecl *OriginalVarArgsFunction)(BYTE variable1, char* format, ...)(0x12345678); //TODO: change address lolz

BOOL __cdecl HookedVarArgsFunction(BYTE variable1, char* format, ...)
{
	BOOL res;

	va_list vl;
	va_start(vl, format);

	// Get variable arguments count from disasm. -2 because of existing 'format', 'variable1'
	uint32_t argCount = *((uint8_t*)_ReturnAddress() + 2) / sizeof(void*) - 2;
	printf("arg count = %d\n", argCount);

	// ((int( __cdecl* )(const char*, ...))&oldCode)(fmt, ...);
	__asm
	{
		mov eax, argCount
		test eax, eax
		je noLoop
		mov edx, vl
		loop1 :
		push dword ptr[edx + eax * 4 - 4]
		sub eax, 1
		jnz loop1
		noLoop :
		push format
		push variable1
        //lea eax, [oldCode] // oldCode - original function pointer
		mov eax, OriginalVarArgsFunction
		call eax
		mov res, eax
		mov eax, argCount
		lea eax, [eax * 4 + 8] //+8 because 2 parameters (format and variable1)
		add esp, eax
	}
    return res;
}

Solution 10 - C++

Using the new C++0x standard, you may be able to get this done using variadic templates or even convert that old code to the new template syntax without breaking anything.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNot AvailableView Question on Stackoverflow
Solution 1 - C++Jonathan LefflerView Answer on Stackoverflow
Solution 2 - C++Vincent FourmondView Answer on Stackoverflow
Solution 3 - C++tenfourView Answer on Stackoverflow
Solution 4 - C++AxelView Answer on Stackoverflow
Solution 5 - C++ShaiView Answer on Stackoverflow
Solution 6 - C++supercatView Answer on Stackoverflow
Solution 7 - C++Jake Fetiu KimView Answer on Stackoverflow
Solution 8 - C++Mark BView Answer on Stackoverflow
Solution 9 - C++SSpokeView Answer on Stackoverflow
Solution 10 - C++DavidView Answer on Stackoverflow