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);
}
CodePudding user response:
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.
CodePudding user response:
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.
CodePudding user response:
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), "")
CodePudding user response:
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.
CodePudding user response:
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);
}
printf("\n");
}