C pass variadic arguments to other function [duplicate] - c

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).

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...
}

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++.

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 );

I also wanted to wrap printf and found a helpful answer here:
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.

A possible way is to use #define:
#define exampleB(int b, ...) example(0, b, __VA_ARGS__)

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.

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.

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.

Adding ... to a parameter makes it include all the following parameters as well.
int FIRST_FUNC(int first_param...){
return SECOND_FUNC(first_param);
}
Or using GNU C extensions:
int FIRST_FUNC(...){
__builtin_return(
__builtin_apply(
(void(*)())SECOND_FUNC, __builtin_apply_args(), 100));
}

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.

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

Related

using variable number of arguments in a function call and delegating them to another function

I have got a global function which has the following signature:
void Systemfehlerprotokollieren(BYTE quelle,WORD fehlercode,WORD subfehlercode,
BYTE klassifizierung, BYTE status,BYTE kanalnummer,DWORD detailfehler,
WORD modulnummer,WORD location,WORD wLenZusatzText,char *pcZusatztext);
This function I want to simplify in two ways.
Use variable arguments like in printf instead of pcZusatztext
And thus get rid of parameter wLenZusatzText
Use the original signature in a local context
So my external function (the last parameters) would look like:
ext_Systemfehlerprotokollieren(WORD location, char *form, ...);
this function then should call void Systemfehlerprotokollieren(. . . ) with its above mentioned parameters as before.
Right now I have the following code parts:
void vSystemfehlerprotokollierenText(BYTE quelle,WORD fehlercode,WORD subfehlercode,
BYTE klassifizierung,BYTE status,BYTE kanalnummer,DWORD detailfehler,
WORD modulnummer,WORD location,va_list args)
{
int ret;
char zepuf_printf_mode_lokal[ZELE];
memset(&zepuf_printf_mode_lokal[0],0x00,ZELE);
ret = vsnprintf_s(zepuf_printf_mode_lokal, ZELE-1,_TRUNCATE, "%s",args );
if (ret != -1)
{
if (ret < 0)
{
Systemfehlerprotokollieren(quelle,fehlercode,subfehlercode,
klassifizierung,status,kanalnummer,detailfehler,
modulnummer,location,0,NULL);
return;
}
}
Systemfehlerprotokollieren(quelle,fehlercode,subfehlercode,
klassifizierung,status,kanalnummer,detailfehler,
modulnummer,location,strlen(zepuf_printf_mode_lokal),zepuf_printf_mode_lokal);
}
in above function Systemfehlerprotokollieren will be called as usual
and
void SystemFehlerKG(WORD wFehlerCode, WORD wSubFehlerCode,BYTE KanalNummer,
DWORD detailinfo2, DWORD detailinfo3, WORD programmstelle, char *form, ... )
{
va_list args = NULL ;
if (form != NULL)
{
va_start(args, form);
vSystemfehlerprotokollierenText(SYS_FEHL_QUELLE_FLEXOS,wFehlerCode,wSubFehlerCode,
SYS_FEHL_KLASS_FEHLER, SYS_FEHL_STATUS_FEHLER_KOMMT_GEHT, KanalNummer,
(WORD)detailinfo2, (WORD)detailinfo3, programmstelle,args);
va_end(args);
}
else
vSystemfehlerprotokollierenText(SYS_FEHL_QUELLE_FLEXOS,wFehlerCode,wSubFehlerCode,
SYS_FEHL_KLASS_FEHLER, SYS_FEHL_STATUS_FEHLER_KOMMT_GEHT, KanalNummer,
(WORD)detailinfo2, (WORD)detailinfo3, programmstelle,"");
}
This works fine, when i call the second function like this:
SystemFehlerKG(5000+TCPIP_SYS_ERROR_FKT_SEND,0,SYS_FEHL_KANAL_ALLG,
iRet,0,SFPROG_00000,"%s","");
but I don't know what to change to get the same result with
SystemFehlerKG(5000+TCPIP_SYS_ERROR_FKT_SEND,0,SYS_FEHL_KANAL_ALLG,
iRet,0,SFPROG_00000);
... where the last two parameters are omitted...
[Edit #1]:
I see, that printf(); doesn't work either, it must be at least printf("");
so the closest possible approach would be
SystemFehlerKG(5000+TCPIP_SYS_ERROR_FKT_SEND,0,SYS_FEHL_KANAL_ALLG,
iRet,0,SFPROG_00000,"");
[Edit #2 as suggested by #Jan Krüger]:
void SystemFehlerKG(WORD wFehlerCode, WORD wSubFehlerCode,
BYTE KanalNummer, DWORD detailinfo2, DWORD detailinfo3,
WORD programmstelle, char *form, ... )
{
va_list args = NULL ;
va_start(args, form);
vSystemfehlerprotokollierenText(SYS_FEHL_QUELLE_FLEXOS,wFehlerCode,
wSubFehlerCode, SYS_FEHL_KLASS_FEHLER,
SYS_FEHL_STATUS_FEHLER_KOMMT_GEHT, KanalNummer,
(WORD)detailinfo2, (WORD)detailinfo3, programmstelle,args);
va_end(args);
}
I dont have the code at hand right now, but i think, that this threw an exception when form was not a valid (format) string (being NULL or "").
I will tell tomorrow.
[Edit #3:]
I made the changes to my code as in edit#2. Did not work.
But I found the error:
void vSystemfehlerprotokollierenText(BYTE quelle,WORD fehlercode,WORD subfehlercode,
BYTE klassifizierung,BYTE status,BYTE kanalnummer,DWORD detailfehler,
WORD modulnummer,WORD location,va_list args)
needs to be
void vSystemfehlerprotokollierenText(BYTE quelle,WORD fehlercode,WORD subfehlercode,
BYTE klassifizierung,BYTE status,BYTE kanalnummer,DWORD detailfehler,
WORD modulnummer,WORD location,char * form, va_list args)
the function SystemfehlerKGhas to be changed also to reflect the parameter form.
One thing still isnt very clear to me:
If I am using the ... in my top most function and want to call another function that uses the ... signature, what must i be aware of?
Your edit is well-spotted. In printf and friends, the format string is not part of the variadic part of the function's signature. You have to pass it separately as a normal argument.
The other important bit: you always have to pass on the va_list structure to vsnprintf_s (via your vSystemfehlerprotokollieren), even if you're sure there are no extra arguments. So, don't make the va_start and va_end conditional.

c variadic functions, same arguments, different formats

I'm having a problem with va_ methods, and I couldn't find an example (or didn't figure out what the keywords are). The problem is, I need to use the same args for different formats, but the compiler gives me:
incorrect usage of va_start
error. The code I'm trying is something like this:
void vSaveNecessaryFields(EnumA eFormat, ...)
{
va_list xArgs, xArgs2;
const char *fmt1 = NULL, *fmt2 = NULL;
char caString[100] = {0};
fmt1 = cpGetDbFormat(eFormat);
fmt2 = cpGetPrinterFormat(eFormat);
va_start(xArgs1, fmt1);
va_copy(xArgs2, xArgs1);
vsnprintf(caString, sizeof(caString), fmt1, xArgs1);
vSaveToDb(caString);
va_start(xArgs2, fmt2);
vsnprintf(caString, sizeof(caString), fmt2, xArgs2);
vPrintFormatted(caString);
va_end(xArgs2);
va_end(xArgs1);
}
How can I solve this problem?
The argument to va_start should be the eFormat argument. Furthermore, the va_list is declared as xArgs but you use xArgs1, which causes a syntax error.
void vSaveNecessaryFields(EnumA eFormat, ...) {
va_list xArgs, xArgs2;
const char *fmt1 = NULL, *fmt2 = NULL;
char caString[100] = {0};
fmt1 = cpGetDbFormat(eFormat);
fmt2 = cpGetPrinterFormat(eFormat);
va_start(xArgs, eFormat);
vsnprintf(caString, sizeof(caString), fmt1, xArgs);
va_end(xArgs);
vSaveToDb(caString);
va_start(xArgs2, eFormat);
vsnprintf(caString, sizeof(caString), fmt2, xArgs2);
vPrintFormatted(caString);
va_end(xArgs2);
}
You need to call va_end, then call va_start a second time after you have closed the parameter block.

Hooking Printf-like function and modifying output

I'm hooking printf-like function that is defined like this:
int Con_Printf(const char *format, ...)
I want to check if a given string is within the arguments or in the final formatted string and if is there, then replace it with another string, but I have the following code to pass the arguments to the original function:
va_list args;
char *parg;
va_start(args, format);
vasprintf(&parg, format, args);
va_end(args);
(*conprint)("%s", parg);
free(parg);
being (*conprint) the original Con_Printf function.
How can I view the string with format applied (i.e arguments replaced into placeholders) to replace what I need and not just pass through all the arguments?
Example:
The program calls Con_Printf("%s %s", "foo", "bar"), I want to know in my Con_Printf hook if "bar" is within the arguments, but I don't know how many arguments are there because of the ", ...)" definition of Con_Printf.
If "bar" is found within the arguments, then replace it for "baz" and forward a modified call (*conprint)("%s %s", "foo", "baz") (replaced bar with baz). (*conprint)("foo baz") would do the trick too.
Thanks!
I think, what you want is va_arg(). Check the man page here. Instead of directly using vasprintf(), you may want to
take out one argument at a time
perform required checks and operations
then put them together for the final conprint.
from the manual page, the historic setup is:
#include <varargs.h>
void foo(va_alist)
va_dcl
{
va_list ap;
va_start(ap);
while (...) {
...
x = va_arg(ap, type);
...
}
va_end(ap);
}
You need to choose the type based on the supplied format specifier
Well, I think I missed the fact that the string part should contain the already formatted text, so I could directly do string manipulation on there to replace the needle.
So, my code finally ended like this:
va_list args;
char *parg;
va_start(args, format);
vasprintf(&parg, format, args);
va_end(args);
char* replacedstr;
replacedstr = str_replace(parg, "bar", "baz");
(*conprint)("%s", replacedstr);
free(parg);
Being str_replace a nice function that I've found somewhere (I've never coded in C, just doing experiments).
static (*__cdecl)F_HOOK *edit_f(&printf, sizeof(printf))
{
__cdecl _:printf
{
/* .. code before executing printf stuff.. */
}
}

creating va_list dynamically in GCC - can it be done?

my problem with vsprintf is that I can not obtain input arguments directly, I have to first get inputs one by one and save them in void**, then pass this void** to vsprintf(), it is all fine for windows, but when I come to 64bit linux, gcc cannot compile because it is not allowed to convert from void** to va_list, Is there anyone that can give me some help how I should do this under linux?
Can I create va_list dynamically in GCC?
void getInputArgs(char* str, char* format, ...)
{
va_list args;
va_start(args, format);
vsprintf(str, format, args);
va_end(args);
}
void process(void)
{
char s[256];
double tempValue;
char * tempString = NULL;
void ** args_ptr = NULL;
ArgFormatType format; //defined in the lib I used in the code
int numOfArgs = GetNumInputArgs(); // library func used in my code
if(numOfArgs>1)
{
args_ptr = (void**) malloc(sizeof(char)*(numOfArgs-1));
for(i=2; i<numOfArgs; i++)
{
format = GetArgType(); //library funcs
switch(format)
{
case ArgType_double:
CopyInDoubleArg(i, TRUE, &tempValue); //lib func
args_ptr[i-2] = (void*) (int)tempValue;
break;
case ArgType_char:
args_ptr[i-2]=NULL;
AllocInCharArg(i, TRUE, &tempString); //lib func
args_ptr[i-2]= tempString;
break;
}
}
}
getInputArgs(s, formatString, (va_list) args_ptr); //Here
// is the location where gcc cannot compile,
// Can I and how if I can create a va_list myself?
}
There is a way you can do this, but it is specific to gcc on Linux. It does work on Linux (tested) for both 32 and 64 bit builds.
DISCLAIMER: I am not endorsing using this code. It is not portable, it is hackish, and is quite frankly a precariously balanced elephant on a proverbial tightrope. I am merely demonstrating that it is possible to dynamically create a va_list using gcc, which is what the original question was asking.
With that said, the following article details how va_list works with the amd64 ABI: Amd64 and Va_arg.
With knowledge of the internal structure of the va_list struct, we can trick the va_arg macro into reading from a va_list that we construct ourselves:
#if (defined( __linux__) && defined(__x86_64__))
// AMD64 byte-aligns elements to 8 bytes
#define VLIST_CHUNK_SIZE 8
#else
#define VLIST_CHUNK_SIZE 4
#define _va_list_ptr _va_list
#endif
typedef struct {
va_list _va_list;
#if (defined( __linux__) && defined(__x86_64__))
void* _va_list_ptr;
#endif
} my_va_list;
void my_va_start(my_va_list* args, void* arg_list)
{
#if (defined(__linux__) && defined(__x86_64__))
/* va_args will read from the overflow area if the gp_offset
is greater than or equal to 48 (6 gp registers * 8 bytes/register)
and the fp_offset is greater than or equal to 304 (gp_offset +
16 fp registers * 16 bytes/register) */
args->_va_list[0].gp_offset = 48;
args->_va_list[0].fp_offset = 304;
args->_va_list[0].reg_save_area = NULL;
args->_va_list[0].overflow_arg_area = arg_list;
#endif
args->_va_list_ptr = arg_list;
}
void my_va_end(my_va_list* args)
{
free(args->_va_list_ptr);
}
typedef struct {
ArgFormatType type; // OP defined this enum for format
union {
int i;
// OTHER TYPES HERE
void* p;
} data;
} va_data;
Now, we can generate the va_list pointer (which is the same for both 64 bit and 32 bit builds) using something like your process() method or the following:
void* create_arg_pointer(va_data* arguments, unsigned int num_args) {
int i, arg_list_size = 0;
void* arg_list = NULL;
for (i=0; i < num_args; ++i)
{
unsigned int native_data_size, padded_size;
void *native_data, *vdata;
switch(arguments[i].type)
{
case ArgType_int:
native_data = &(arguments[i].data.i);
native_data_size = sizeof(arguments[i]->data.i);
break;
// OTHER TYPES HERE
case ArgType_string:
native_data = &(arguments[i].data.p);
native_data_size = sizeof(arguments[i]->data.p);
break;
default:
// error handling
continue;
}
// if needed, pad the size we will use for the argument in the va_list
for (padded_size = native_data_size; 0 != padded_size % VLIST_CHUNK_SIZE; padded_size++);
// reallocate more memory for the additional argument
arg_list = (char*)realloc(arg_list, arg_list_size + padded_size);
// save a pointer to the beginning of the free space for this argument
vdata = &(((char *)(arg_list))[arg_list_size]);
// increment the amount of allocated space (to provide the correct offset and size for next time)
arg_list_size += padded_size;
// set full padded length to 0 and copy the actual data into the location
memset(vdata, 0, padded_size);
memcpy(vdata, native_data, native_data_size);
}
return arg_list;
}
And finally, we can use it:
va_data data_args[2];
data_args[0].type = ArgType_int;
data_args[0].data.i = 42;
data_args[1].type = ArgType_string;
data_args[1].data.p = "hello world";
my_va_list args;
my_va_start(&args, create_arg_pointer(data_args, 2));
vprintf("format string %d %s", args._va_list);
my_va_end(&args);
And there you have it. It works mostly the same as the normal va_start and va_end macros, but lets you pass your own dynamically generated, byte-aligned pointer to be used instead of relying on the calling convention to set up your stack frame.
I have tried using libffi as mentioned somewhere else and it works.
Here below is the link , hope it can help others with similar issues.
Thanks again for all help I got here!
Link:
http://www.atmark-techno.com/~yashi/libffi.html -- simple example given
http://www.swig.org/Doc1.3/Varargs.html -- printf() and other examples given
The type of va_list is not void ** or anything similar with 64-bit gcc (on Intel x86/64 machines). On both Mac OS X 10.7.4 and on RHEL 5, there is no header stdarg.h in /usr/include. Consider the following code:
#include <stdarg.h>
#include <stdio.h>
int main(void)
{
printf("sizeof(va_list) = %zu\n", sizeof(va_list));
return 0;
}
The output on both RHEL 5 and Mac OS X 10.7 with a 64-bit compilation is:
sizeof(va_list) = 24
With a 32-bit compilation, the output on each platform is:
sizeof(va_list) = 4
(You may take it that I was surprised to find this much discrepancy between the 32-bit and 64-bit versions. I was expecting a value between 12 and 24 for the 32-bit version.)
So, the type is opaque; you can't even find a header that tells you anything about; and it is much bigger than a single pointer on 64-bit machines.
Even if your code works on some machines, it is very, very far from guaranteed to work everywhere.
The GCC 4.7.1 manual does not mention any functions that allow you to build a va_list at runtime.
Following class works for me:
class VaList
{
va_list _arguments;
public:
explicit inline VaList(const void * pDummy, ...)
{
va_start(_arguments, pDummy);
}
inline operator va_list &()
{
return _arguments;
}
inline operator const va_list &() const
{
return _arguments;
}
inline ~VaList()
{
va_end(_arguments);
}
};
and it can be used like this:
void v(const char * format, const va_list & arguments)
{
vprintf(format, const_cast<va_list &>(arguments));
}
...
v("%d\n", VaList("", 1)); // Uses VaList::operator va_list &()
v("%d %d\n", VaList(nullptr, 2, 3)); // Uses VaList::operator va_list &()
vprintf("%s %s %s\n", VaList("", "Works", "here", "too!"));
const VaList args(NULL, 4, 5, "howdy", "there");
v("%d %d %s %s\n", args); // Uses VaList::operator const va_list &() const
The first dummy parameter can be any kind of pointer, it is only used to compute the address of the following arguments.
The same can of course be done in C too but not so niftily (use pointer instead of reference)!
Simple example of using VaList to construct a dynamic va_list:
static void VectorToVaList(const std::vector<int> & v, va_list & t)
{
switch (v.size())
{
case 1: va_copy(t, VaList("", v[0])); return;
case 2: va_copy(t, VaList("", v[0], v[1])); return;
case 3: va_copy(t, VaList("", v[0], v[1], v[2])); return;
case 4: va_copy(t, VaList("", v[0], v[1], v[2], v[3])); return;
// etc
}
throw std::out_of_range("Out of range vector size!");
}
and usage:
va_list t;
VectorToVaList(std::vector<int>{ 1, 2, 3, 4 }, t);
vprintf("%d %d %d %d\n", t);
If the problem you're trying to solve is inserting passing arbitrary types to a function in va_list style, then, consider using union:
#include <iostream>
#include <cstdarg>
union ARG
{
int d;
char* s;
double f;
};
int main()
{
printf("%d %s %f \n", 1, "two", 3.1415 );
// Output: 1 two 3.141500
char format[ 1024 ] = "%d %s %f\n";
ARG args[ 5 ] = { };
args[ 0 ].d = 1;
args[ 1 ].s = "two";
args[ 2 ].f = 3.1415;
printf( format, args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], args[ 4 ] );
// Output: 1 two 3.141500
return 0;
}
Some things you'll note about my solution:
No attempt is made to produce the correct number of arguments. i.e. I oversupply the arguments, but, most functions will look at the first parameter to determine how to handle the rest (i.e. format)
I didn't bother dynamically create the format, but, it is a trivial exercise to build a routine that dynamically populates format and args.
Tested this on:
- Ubuntu, g++
- Android NDK
I did some more testing, and, confirmed #PeterCoordes comments about this answer not working for double precision.

vsprintf, how to convert void** to va_list on linux

my problem with vsprintf is that I can not obtain input arguments directly, I have to first get inputs one by one and save them in void**, then pass this void** to vsprintf(), it is all fine for windows, but when I come to 64bit linux, gcc cannot compile because it is not allowed to convert from void** to va_list, Is there anyone can give me some help how I should do under linux?
part of my code is:
void getInputArgs(char* str, char* format, ...)
{
va_list args;
va_start(args, format);
vsprintf(str, format, args);
va_end(args);
}
void process(void)
{
char s[256];
double tempValue;
char * tempString = NULL;
void ** args_ptr =NULL;
ArgFormatType format; //defined in the lib I used in the code
int numOfArgs = GetNumInputArgs(); // library func used in my code
if(numOfArgs>1)
{
args_ptr = (void**) malloc(sizeof(char)*(numOfArgs-1));
for(i=2; i<numOfArgs; i++)
{
format = GetArgType(); //library funcs
switch(format)
{
case ArgType_double:
CopyInDoubleArg(i, TRUE, &tempValue); //lib func
args_ptr[i-2] = (void*) (int)tempValue;
break;
case ArgType_char:
args_ptr[i-2]=NULL;
AllocInCharArg(i, TRUE, &tempString); //lib func
args_ptr[i-2]= tempString;
break;
}
}
}
getInputArgs(s, formatString, (va_list) args_ptr); /////Here is the location where gcc cannot compile
}
Many Many thanks!!
The problem is, your function gets ..., but you are passing it a va_list. ... is used for a usage like this:
getInputArgs(s, formatString, arg1, arg2, arg3, arg4 /* etc */);
and it won't work with va_list. Unfortunately, there is not an easy way to create a va_list from different parameters instead of getting it from .... See this question for example.
What you should do is to change the way you want to print to the string.
You can have:
char s[256];
int so_far = 0;
And in your for loop instead of something like this:
CopyInDoubleArg(i, TRUE, &tempValue); //lib func
args_ptr[i-2] = (void*) (int)tempValue;
You write:
CopyInDoubleArg(i, TRUE, &tempValue); //lib func
if (so_far < 256) /* 256 is the maximum length of s */
so_far += snprintf(s + so_far, 256 - so_far, "%lf", tempValue);
Something along these lines. This way, you create the string one by one, appending each element to the previous, instead of trying to make it all at once.

Resources