I have the following code:
int __dmasprintf (char **s, const char *format, ...) {
char buf[512];
va_list arg;
int ret;
va_start(arg,format);
ret = vsprintf(buf, format, arg);
va_end(arg);
*s = strdup(buf);
if (*s == NULL) return -1;
return 0;
}
I want to add an argument to the va_list arg before calling vsprintf() because my format contains 1 extra argument at the end.
How to add an argument (for example char * myarg) to the the va_list arg?
Or is it possible to pass vsprintf() a customized list?
You can't.
You either need to make two vsprintf (surely vsnprintf?) calls, or replace your function with a variadic macro, like
#define __dmasprintf(S, FMT, ...) ( \
(*S = do_dmasprintf(FMT, __VA_ARGS__, my_arg)) == NULL ? -1 : 0)
char *do__dmasprintf (const char *format, ...) {
char buf[512];
va_list arg;
int ret;
va_start(arg,format);
ret = vsnprintf(buf, sizeof(buf), format, arg);
va_end(arg);
char *s = strdup(buf);
return s;
}
Notes:
I replaced vsprintf with vsnprintf. There's no reason to use the former here (or pretty much anywhere else)
you ignore ret. Should you?
I kept the macro argument layout similar to the original, but since __VA_ARGS__ must be one-or-more arguments (it can't be empty), that means at least one argument is required after FMT. Just remove the FMT argument entirely if you want to allow zero arguments after it.
There is unfortunately no direct way to do that. There is a reason for that : the stdarg macros take the address in the stack of the last known parameter, and then directly iterate the stack.
If you can use macros, #Useless provided a nice solution - beware, macros can have side effects when you pass variables pre- or post-fixed with ++ or --.
If you want to avoid macros, you will have to write your own variant of vsprintf. No problem for it, just find a source for C stdlib (GNU libc could be a nice start point) and be brave ... hope you can use macros !
Related
Using the <stdarg.h> header, one can make a function that has a variable number of arguments, but:
To start using a va_list, you need to use a va_start macro that needs to know how many arguments there, but the printf & ... that are using va_list don't need the argument count. How can I create a function that doesn't need the argument count like printf?
Let's say I want to create a function that takes a va_list and instead of using it, passes it to another function that requires a va_list? (so in pseudocode, it would be like void printfRipOff(const char* format, ...) {printf(format, ...);})
Let's say I want to create a function that takes a va_list and instead of using it, passes it to another function that requires a va_list?
Look at function vprintf( const char * format, va_list arg ); for one example of a function that takes a va_list as an input parameter.
It is basically used this way:
#include <stdio.h>
#include <stdarg.h>
void CountingPrintf(const char* format, ...)
{
static int counter = 1;
printf("Line # %d: ", counter++); // Print a Counter up front
va_list args;
va_start(args, format); // Get the args
vprintf(format, args); // Pass the "args" through to another printf function.
va_end(args);
}
int main(void) {
CountingPrintf("%s %s\n", "Hello", "World");
CountingPrintf("%d + %d == %d\n", 2, 3, 2+3);
return 0;
}
Output:
Line # 1: Hello World
Line # 2: 2 + 3 == 5
Functions like printf() and scanf() have the format string argument that tells them the number and types of the arguments that must have been provided by the function call.
If you're writing your own function, you must have some way of knowing how many arguments were provided and their types. It may be that they are all the same type. It may be that they're all pointers and you can use a null pointer to indicate the end of the arguments. Otherwise, you probably have to include a count.
You can do that as long as provided the called function expects a va_list. There are caveats (see C11 §7.16 Variable arguments) but they're manageable with a little effort.
A very common idiom is that you have a function int sometask(const char *fmt, ...) and a second function int vsometask(const char *fmt, va_list args), and you implement the first as a simple call to the second:
int sometask(const char *fmt, ...)
{
va_list args;
va_start(fmt, args);
int rc = vsometask(fmt, args);
va_end(args);
return rc;
}
The second function does the real work, of course, or calls on other functions to do the real work.
In the question, you say:
… va_start macro that needs to know how many arguments …
No; the va_start macro only needs to know which argument is the one before the ellipsis — the argument name before the , ... in the function definition.
In a comment, you say:
This is what I'm trying to write, but it didn't work.
string format(const char* text, ...)
{
// temporary string used for formatting
string formattedString;
initializeString(&formattedString, text);
// start the va_list
va_list args;
va_start(text, args);
// format
sprintf(formattedString.array, text, args);
// end the va_list
va_end(args);
return formattedString;
}
As noted by abelenky in a comment, you need to use vsprintf():
string format(const char* text, ...)
{
string formattedString;
initializeString(&formattedString, text);
va_list args;
va_start(text, args);
vsprintf(formattedString.array, text, args);
va_end(args);
return formattedString;
}
That assumes that the formattedString has enough space in the array for the formatted result. It isn't immediately obvious how that's organized, but presumably you know how it works and it works safely. Consider using vsnprintf() if you can determine the space available in the formattedString.
The printf and scanf family of functions don't need to be explicitly passed the length of the va_list because they are able to infer the number of arguments by the format string.
For instance, in a call like:
printf("%d %c %s\n", num, c, "Hello");
printf is able to examine the first parameter, the format string, and see that it contains a %d, a %c, and a %s in that order, so it can assume that there are three additional arguments, the first of which is a signed int, the second is a char and the third is a char* that it may assume is a pointer to a null-terminated string.
To write a similar function that does not need to be explicitly told how many arguments are being passed, you must find a way to subtly give it some information allowing it to infer the number and types of the arguments in va_list.
I have the following log function
void log_error(char * file_name, int line_num, int err_code) {
printf("%s:%d:%s\n", file_name, line_num, get_err_str(err_code));
}
I want to change this to have the option to pass a custom format string and args for additional information something like
void log_error(char * file_name, int line_num, int err_code, ...) {
va_list ptr;
char * fmt;
printf("%s:%d:%s", file_name, line_num, get_err_str(err_code));
va_start(ptr, err_code);
fmt = va_arg(ptr, char *);
vprintf(fmt, ptr);
printf("\n");
}
but I don't want to enforce passing this extra info, so I might not get fmt and the required extra arguments which might cause the code to fail, is there a way to check if there are variadic args before trying to access them? Or the only option for my use case is defining 2 different function, like so
void log_error(char * file_name, int line_num, int err_code) {
printf("%s:%d:%s\n", file_name, line_num, get_err_str(err_code));
}
void log_error(char * file_name, int line_num, int err_code, const char * fmt, ...) {
va_list ptr;
printf("%s:%d:%s", file_name, line_num, get_err_str(err_code));
va_start(ptr, err_code);
vprintf(fmt, ptr);
printf("\n");
va_end(ptr);
}
You can't check for variadic arguments in functions without at least one non-variadic argument that tells you what is going on, but you CAN use a variadic macro that expands to the "right" variadic function call. Since you are probably already using a macro to supply __FILE__ and __LINE__, you can probably do something like:
// this is the function that will be called by the macro
void log_error_func(char * file_name, int line_num, int err_code, const char *fmt, ...);
// this is the macro that will be used to log
#define log_error(CODE, ...) log_error_func(__FILE__, __LINE__, CODE, "" __VA_ARGS__)
Now you can use things like
log_error(42, "this error has args: %d, %d, %d", 1, 2, 3);
as well as
log_error(5); /* no args here */
and they'll work as one would expect. The use of "" in a macro like this requires that the format arg (if present) be string literal, so the empty string will be properly prepended.
Your function doesn't have any way of knowing whether or not a format string has been passed. So the best thing to do is to make it an explicit parameter.
void log_error(char * file_name, int line_num, int err_code, char * fmt, ...) {
va_list ptr;
printf("%s:%d:%s", file_name, line_num, get_err_str(err_code));
va_start(ptr, fmt);
vprintf(fmt, ptr);
va_end(ptr);
printf("\n");
}
For a function like this, I would expect that a message string would have to be passed.
is there a way to check if there are variadic args before trying to access them?
No.
the only option for my use case is defining 2 different function, like so
Generally, yes, or similar.
You can make log_error a macro, and overload the macro on the number of arguments. 3 arguments would go to the first version, more would go to the variadic one.
If you want a single funtion, you MUST have a fmt parameter, but you can pass an empty string ("") as that argument when it's not needed. Or you can modify your code to check if fmt is NULL, if that looks neater to you:
void log_error(char *file_name, int line_num, int err_code, char *fmt, ...) {
printf("%s:%d:%s", file_name, line_num, get_err_str(err_code));
if (fmt != NULL) {
va_list ptr;
va_start(ptr, fmt);
vprintf(fmt, ptr);
va_end(ptr);
}
printf("\n");
}
is there a way to check if there are variadic args before trying to access them?
The variadic argument facility does not itself provide a way to determine how many variadic arguments there are, or whether there are any at all.
Or the only option for my use case is defining 2 different function, like so
A variadic function knows from its non-variadic arguments and from its general contract what variadic arguments to expect, and of what types. printf() is the canonical example here: it knows from its format string what to expect of its variadic arguments.
If all the arguments to the current, non-variadic function have significance unsuited to be adapted to the purpose of informing about the presence of variasic arguments then you need at least a different function signature. You don't necessarily need two functions, though.
If you do use two functions, however, then be aware that they will need different names. C does not support function overloading.
If you want to preserve the current signature for existing callers, yet make the custom-format feature available to new ones, then one option would be to replace the current function with a macro that expands to a call to a replacement function with an augmented argument list. Example:
void log_error_custom(char * file_name, int line_num, int err_code,
const char * fmt, ...) {
// ...
}
#define log_error(file, line, err) log_error_custom((file), (line), (err), "")
I have to perform dynamic linking on a variadic function of following format:
int foo(char *args, const char *f, ...)
Here the number of arguments are variable.
What I want to achieve is that I want to pass the obtained arguments to the original function which I am resolving using dlsym.
If I do not pass all the arguments I keep getting segmentation fault.
Thanks in advance.
I think your problem is not related to LD_PRELOAD or dlopen/dlsym: you simply want to call a variadic function from a variadic function, eg:
int printf (const char *fmt, ...) {
return fprintf (stdout, fmt, my_variadic_parameters);
}
As far as I know, it is not possible, but in carefully designed libraries (which is obviously a minority of them), every variadic function has a counterpart that uses va_list parameter, like printf and vprintf, fprintf and vfprintf:
int printf (const char *fmt, ...) {
va_list ap;
int retval;
va_start (ap, fmt);
retval= vfprintf (stdout, fmt, ap);
va_end (ap);
return retval;
}
int vmyprintf (const char *fmt, va_list pap) {
va_list ap;
int retval;
va_copy (ap, pap);
retval= vfprintf (stdout, fmt, ap);
va_end (ap);
return retval;
}
So you should ask the creator of 'foo' to create a 'vfoo':
int vfoo (char *args, const char *f, va_list v)
Have a look at man va_start, there is a nicely concise example in that man page.
You can treat the dlsym symbol address just you would any other function pointer.
The basic concept behind va_args, is using a control variable in a loop, usually a printf style format string that can be parsed to decide when to exit the call frame building loop. In your case ankit, you need to have some form "is there another argument to place in the call frame" as you build up a va_list. Basically va_start va_arg... va_end.
Again, best explained in code, and the man page has a nice example.
Given
int foo(char *args, const char *f, ...)
the information provided by either args or f ought to tell you how many parameters of which type had been passed by the caller as variadic arguments.
How this info is provided depends on foo's implementation, so see foo() documentation.
A nice example to this is printf(const char * format, ...); All info of what is received as variadic arguments shall be parseable from the 1st parameter, the non-variadic format-"string".
If you fail to gain insight in this, you are lost. In C there is no generic, "built-in" way to gather the number and types of variadic arguments passed.
I have a function which implements a custom variant of printf.
Does anyone have a clue how I can print out one more parameter with va_list? My format is like "%d some text %s". I need a, to be printed as first parameter.
void func (int a, char *fmt, ...) {
va_list ap;
va_start (ap, fmt);
// vprintf(stdout, fmt, a, ap) //Can't do like this :(
vfprintf(fmt, a, ap); //Or like this :(
va_end(ap);
}
If a is always printed the same way, it shouldn't be part of the format passed in. You should instead print it separately.
In the example you gave, change your format string to "some text %s". Then your function can do this:
void func (int a, char *fmt, ...) {
va_list ap;
// first print the function specific fields
printf("%d ", a);
// then the user's format
va_start (ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
As noted in comments:
Redesign the interface
You would do best to redesign the interface so that the format string only applies to the ... (va_list) arguments; the fixed argument gets treated separately.
Standard reasons for wanting extra arguments are logging things like __FILE__ and __LINE__ (presumably not __func__ if you don't have C99 __VA_ARGS__ support — see below). You would probably do best to remove the option of caller-controlled formatting of those fixed arguments, leaving the fmt to process just the ... arguments.
If you can't redesign the interface
The fact that the %d is part of the format and the int value is not part of the va_list makes life tricky.
If you cannot, or decline to, change the rules for calling the function, you'll need to parse the format, for example separating it into the part up to (but not including) the second %, and use that format with the standalone argument; the remainder of the format is then passed to vprintf(). If you're on a POSIX system and in a multi-threaded program, you might want to use flockfile() and funlockfile() on stdout to ensure that they're treated as a single unit of output despite the multi-threading.
// Using C99 features
void func(int a, char *fmt, ...)
{
char *p1 = strchr(fmt, '%');
if (p1 == 0)
{
// No conversion specifications. Not puts(); it adds a newline
fputs(fmt, stdout);
return;
}
char *p2 = strchr(p1 + 1, '%');
if (p2 == 0)
{
// The user invoked func(1, "iteration %d:\n");?
printf(fmt, a);
return;
}
int buflen = p2 - fmt + 1;
char buffer[buflen];
memmove(buffer, fmt, buflen);
buffer[buflen-1] = '\0';
// flockfile(stdout); // Multi-threading
printf(buffer, a);
va_list ap;
va_start(ap, fmt);
vprintf(p2, ap);
va_end(ap);
// funlockfile(stdout); // Multi-threading
}
The code cheats; it uses a C99 VLA (variable length array) and also interleaved declarations and statements. In C90, therefore, you'd probably end up using malloc(), or perhaps a fixed size buffer (running risks of overflows), or a hybrid (use a fixed size buffer if the prefix of the format string is small enough to fit, or allocate (and free) if not).
You need to decide on the appropriate error handling strategies. Note that this isn't bullet-proof code. Calling func(a, "%% yield is %d; data is [%s]\n", data); throws another spanner in the works. Also, if you're on POSIX and someone passes "it is %2$s and %1$d things happened", you're in deep trouble.
Yes, I'm on POSIX system. But it is quite old, I am even not able to use approaches with __VA_ARGS__ macros as it appears in C99+ standards.
It's curious that you're on such an old system. However, that simply means you probably won't write code with the n$ notations in it, so it is one less thing to worry about. The multi-threading issue is less likely to be a problem either. And you can't use the other C99 features that were shown in my sample code.
You could look at the FFI (foreign function interface) library; I'm not at all sure it will help, though.
Based on previous answer to parse format, I've found a simple solution (if %d is always first):
void func (int a, char *fmt, ...) {
char *new_format = malloc(10*sizeof(char));
snprintf(new_format, 10, "%d", a);
strcat(new_format, (fmt+2));
va_list ap;
va_start (ap, fmt);
vprintf(stdout, new_format, ap);
va_end(ap);
}
Special Thanks to Jonathan Leffler
I decided to write a variation of the scan C function that instead of returning an int stating success or failure would print an error message and abort the program on error.
void aborting_scanf(const char *format, ???) {
if (scanf(format, &var) != 1)
puts("Invalid input");
exit(1);
}
The problem is what to write instead of the ???, in the man page, the type signature is:
int scanf(const char *format, ...)
But then how can I name the target variable?
In theory I could write a version for each type by hand but it would incredibly repetitive so I am looking for a more general solution to avoid code duplication.
You need to use variable arguments for this along with vscanf:
void aborting_scanf(const char *format, ...) {
va_list ap;
va_start(ap, format);
if (vscanf(format, ap) != 1) {
puts("Invalid input");
exit(1);
}
va_end(ap);
}
The ... argument tells the compiler that the remaining arguments are of varying types and numbers and can be anything. The variable of type va_list allows you to access these values, and the vscanf function can read this directly.
scanf uses a variable argument list as indicated by the ... parameter. You manipulate these with the functions in stdarg.h. This format is not suitable to give a full introduction into variable argument lists, read a tutorial/book of choice about it.