Home > Mobile >  Check if there are variadic arguments in C
Check if there are variadic arguments in C

Time:03-17

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");
}
  • Related