Variadic function: Extract arguments and append string - c

I have a function log_info (copied from printf's implementation) that accepts variable no. of arguments and passes it to vprintf:
int log_info(const char *format, ...) {
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
va_end (arg);
return done;
}
I want to prepend and append strings to these arguments. So if a user calls the above function this way:
log_info("at iteration %d, float value is %f", i, f);
instead of printing
at iteration 4, float value is 102.34
I want to print
[INFO] [at iteration 4, float value is 102.34] [timestamp: xxxx]
I could do it in 3 separate steps
fprintf(stdout, "[INFO] [");
vprintf(stdout, format, arg);
fprintf(stdout, "] [timestamp:%f]", ts);
But the program is multi-threaded, and hence I want all data to be written in a single call to vprintf (vprintf being thread-safe).
The other option is to lock a mutex and then write it in 3 steps as shown above, but if appending strings to the args is not too complex, I would like to try that.
Edit: Performance overhead due to use of mutex is not really an issue, but I dont want to use one unless necessary.
Thanks in advance

You want to add new fields to the output, so just construct a new format string.
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
int loginfo(const char *format, ...) {
int ret;
int len;
va_list ap;
char *new_fmt;
char *timestamp;
const char fmt_template[] = "[INFO] [%s] [timestamp: %s]";
/* Grab the timestamp now, since if we call it twice, it may change in length */
timestamp = get_time_string();
/* Calculate length for the augmented format string and allocate. */
len = snprintf(NULL, 0, fmt_template, format, timestamp);
new_fmt = malloc(len + 1);
/* Construct the new format string */
snprintf(new_fmt, len + 1, fmt_template, format, timestamp);
/* Print as before, using new format string */
va_start (ap, format);
ret = vfprintf (stdout, new_fmt, ap);
va_end (ap);
free(new_fmt);
return ret;
}

One approach is to use vsnprintf to write the original message to a (dynamically allocated) string. Then use fprintf to output the metadata plus original message:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
...
static const char * get_timestamp_str()
{
...
}
static void Log(const char *format, ...)
{
va_list arg;
int len;
char * orig_msg;
/* Compute length of original message */
va_start(arg, format);
len = vsnprintf(NULL, 0, format, arg);
va_end(arg);
/* Allocate space for original message */
orig_msg = (char *)calloc(len+1, sizeof(char));
/* Write original message to string */
va_start(arg, format);
vsnprintf(orig_msg, len+1, format, arg);
va_end(arg);
/* Write metadata plus original message to stderr */
fprintf(stderr, "[INFO] [timestamp %s] %s\n", get_timestamp_str(), orig_msg);
free(orig_msg);
}

Related

Implementing optional argument of an variadic-using function?

I have a function:
log(const char *domain, int log_level, const char *fmt, ...)
I would like first and second arguments optional, so it's like following calls would be possible:
log("SYSTEM-A", 1, "Example %s", "...message");
log(1, "Example %s", "...message");
log("Example %s", "...message");
I've read about neat macro tricks, however they (almost?) all rely on trailing arguments to 'stand out' in a helper macro:
HELPER_SELECT(_1, _2, _3, func, ...) func
I however cannot use this method, because log() can take arbitrary number of variadic arguments. Is this possible to overcome somehow? With use of _Generics, maybe?
(1) log("SYSTEM-A", 1, "Example %s", "...message");
(2) log(1, "Example %s", "...message");
(3) log("Example %s", "...message");
From what I understand:
(1) does not has % in it's first argument.
(2) first argument is int
(3) has % in it's argument.
You can:
overload log macro on number of arguments
if one argument
choose (3)
else
_Generic on first argument
If first argument is an int
choose (2)
Else
call some _log_wrapper(const char *arg, ...)
inspect if strchr(arg, '%')
if it does, call va_list version of (3)
if it does not, call va_list version of (1)
A possible implementation looks like this:
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
void vlog_domain(const char *domain, int log_level, const char *fmt, va_list va) {
printf("domain\n");
}
void vlog_level(int log_level, const char *fmt, va_list va) {
printf("level\n");
}
void vlog_normal(const char *fmt, va_list va) {
printf("normal\n");
}
void _log_wrapper(int type, ...) {
va_list va;
va_start(va, type);
if (type == 1) {
int log_level = va_arg(va, int);
const char *fmt = va_arg(va, const char *);
vlog_level(log_level, fmt, va);
} else {
const char *arg = va_arg(va, const char*);
if (!strchr(arg, '%')) {
const char *domain = arg;
int log_level = va_arg(va, int);
const char *fmt = va_arg(va, const char*);
vlog_domain(domain, log_level, fmt, va);
} else {
const char *fmt = arg;
vlog_normal(fmt, va);
}
}
va_end(va);
}
#define _log_1(_1) vlog_normal(_1) // TODO
#define _log_2(_1, ...) _log_wrapper( \
_Generic((_1), int: 1, char *: 2), _1, ##__VA_ARGS__)
// this implementation supports max ca. 10 arguments
#define _log_N(_9,_8,_7,_6,_5,_4,_3,_2,_1,_0,N,...) _log_##N
#define log(...) _log_N(__VA_ARGS__,2,2,2,2,2,2,2,2,2,2,1)(__VA_ARGS__)
int main() {
log("SYSTEM-A", 1, "Example %s", "...message"); // domain
log(1, "Example %s", "...message"); // level
log("Example %s", "...message"); // normal
}
These are some time spent on writing the interface, that the next developer will most probably anyway not understand and will have to rewrite and refactor the whole code. I suggest instead to be as possible clear and write as possibly easy code to understand and just name your functions:
logd("SYSTEM-A", 1, "Example %s", "...message");
logl(1, "Example %s", "...message");
log("Example %s", "...message");
and be done with it.
Inspect other projects how they solved logging with "domain+loglevel" (which sounds like syslog() severity and facility....) have a look how other projects solved logging interface. From my mind I enjoyed zephyr project solved logging, and it's open source so see inspect it's sources.
This isn't really possible in C - variadic functions solve this problem once, but you're trying to solve it twice.
Consider the use of a simple options structure. Have all options empty value be equivalent to 0 (or have a default initializer), so that the caller doesn't need to remember any sentinel values.
struct loggeropts {
const char *domain;
int level;
};
void logger(struct loggeropts *opts, const char *fmt, ...) {
if (opts) {
if (opts->domain) { /* */ }
if (opts->level) { /* */ }
}
/* impl */
}
int main(void) {
struct loggeropts opts = { .domain = "SYSTEM-A" };
logger(&opts, "#Example %u %s", 42, "information");
logger(NULL, "#Example %u %s", 44, "different information");
}
If optional arguments aren't used that often, you could hide the call behind a macro.
#define logger(string, ...) logger_with_opts(NULL, string, __VA_ARGS__)
void logger_with_opts(struct loggeropts *opts, const char *fmt, ...);
Alternatively, have a special section of your format string that identifies passed options, and make sure they are passed before the usual variadic arguments. Remembering to move your fmt pointer before passing it onward. This does seem fragile, though, and has additional overhead.
logger("{{#d#l}}Example %s", "SYSTEM-A", 1, "...message");
logger("{{#d}}Example %s", "SYSTEM-B", "...message");
I would most likely suggest just simply having domain-specific logging functions, in addition to the general purpose function.

How to update a vargs to send to printf

I have the following code where I format a message that has been passed via a logger:
va_list args;
va_start(args, level);
// pop the msg and do some string replaces
char *msg = va_arg(args, char*);
ssize_t len = str_replace(tmp, "%(msg)s", msg, output, 1024);
// now how to get it 'back in' so I can print it?'
// msg = output
vfprintf(stderr, output, args);
The above code prints nothing, as it seems to have 'popped' the msg without me placing the updated msg back in there. What would be a possible way to do this?
Instead of fiddling with the format string, you could print the variadic arguments passed to the log function to a string first, then print that string with a regular fprintf via the %s format.
You haven't posted your function signature, but it looks as if you passed the format string as first variadic argument. That's unusual. I've changed the signature so that the format string is the last non-variadic arg. That's common and it also opens up the possibility to chech your format strings for correctness. (See below.)
Here's an example implementation:
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
enum {
FATAL, ERROR, WARNING, INFO, NLEVEL
};
void say(int level, const char *fmt, ...)
{
static const char *templates[NLEVEL] = {
"\033[1m!!! FATAL: %s !!!\033[0m\n",
"ERROR: %s\n",
"Warning: %s\n",
"%s Just sayin'.\n"
};
const char *tmp;
char output[1024];
va_list args;
// print arguments to temporary string
va_start(args, fmt);
vsnprintf(output, sizeof(output), fmt, args);
va_end(args);
// find and print template string with message
if (level < 0) level = 0;
if (level >= NLEVEL) level = INFO;
tmp = templates[level];
fprintf(stderr, tmp, output);
va_end(args);
if (level == FATAL) exit(1);
}
Use it like this:
int main(int argc, char *argv[])
{
int a = 6;
int b = 7;
double Tmin = -273.15;
say(INFO, "%d * %d == %d.", a, b, a * b);
say(INFO, "The %s is %s.", "apple", "red and juicy");
say(WARNING, "Temperature falls below Tmin (%gC)", Tmin);
say(ERROR, "That should %s have happened.",
(rand() % 2) ? "never" : "not really");
say(FATAL, "Out of coffee!");
return 0;
}
Finally, you can make your logging functions safer and let the compiler check the validity of format strings. GCC and Clang do this with arguments, the Microsoft compiler does it with SAL, the Source-code Annotation Language. Other compilers may have their own method to do such checks. (More likely, they just emulate one of the GC or MS systems.)
With a little macro dance in the header you can cater for both variants:
#if defined(_MSC_VER)
#define PRINTF_FMT _Printf_format_string_
#endif
#if defined(__GNUC__) || defined(__clang__)
#define PRINTF_ARGS(IFMT, IARGS) __attribute__((format(printf, IFMT, IARGS)))
#endif
#ifndef PRINTF_ARGS
#define PRINTF_ARGS(IFMT, IARGS)
#endif
#ifndef PRINTF_FMT
#define PRINTF_FMT
#endif
void say(int level, PRINTF_FMT const char *fmt, ...) PRINTF_ARGS(2, 3);
Now a call like this:
say(WARNING, "Meta: Wrong formt for %d.", ~0ull);
warns you about the wrong format specifier.

Passing va_list to other functions

I have been trying to pass variable arguments to other function in C but it is producing inconsistent result in different runtime environment as well as in different runs in same environment:
int main()
{
int result = myprintf("Something \n %d", 9);
return result;
}
int myprintf(const char *format, ...){
printf("Something \n %d", 9);
printf("\n");
va_list args;
va_start(args, format);
int result = printf(format,args);
printf("\n");
va_end(args);
return result;
}
And the result produced is:
WWW.FIRMCODES.COM
9
WWW.FIRMCODES.COM
438656664
I could not find the reason for "438656664".
You cannot pass the variadic arguments to a variadic function. Instead, you must call a function that takes a va_list as argument. The standard library provides variants of printf and scanf that take a va_list; their names have the prefix v.
Your example should look like:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
int printfln(const char *format, ...)
{
int result;
va_list args;
va_start(args, format);
result = vprintf(format, args);
printf("\n");
va_end(args);
return result;
}
int main()
{
int result = printfln("Something \n %d", 9);
printf("(%d)\n", result);
return 0;
}
There are some gotchas, for example when you want to call two v... function for printing to the screen and a log file: The v... function may exhaust the va_list, so you must pass in a fresh one to each call if your code should be portable.
For the C++ fellow also reading this. You can actually do it using pack expansion without using vprintf.
This trick is quite handy when you need to wrap a method that takes the ellipsis (...)
and not a va_list.
For instance:
template <class ... Args>
void foo(const char *format, Args ... args)
{
printf(format, args...);
}
Here class ... Args is template parameter pack, Args ... args is function parameter pack, and args... is function parameter pack expansion.
Alternatively, you can simply use a wrapper macro:
#include <stdio.h>
#define myprintf(fmt, ...) ( printf("Something \n %d\n", 9), printf(fmt, __VA_ARGS__) )
int main (void)
{
int result = myprintf("Something \n %d\n", 9);
printf("%d\n", result);
}
Note the use of the comma operator to preserve the returned value of the right-hand printf call to the caller.
This isn't any less type safe than the (equally dangerous) stdarg.h variadic functions.
Just a simple demonstration and worked example with "a fresh one va_list" when you need to print/output-as-string a template string like constexpr const char* example = R"(template "%s")"; .
std::string print_templ(const char* format, ...)
{
va_list args1;
va_start(args1, format);
va_list args2;
va_copy(args2, args1);
std::vector<char> str(std::vsnprintf(nullptr, 0, format, args1) + 1);
va_end(args1);
const int ret = std::vsnprintf(str.data(), str.size(), format, args2);
va_end(args2);
return std::string(str.begin(), str.begin()+ret);
}

C: Passing variable number of arguments from one function to another

So, here's a small problem I'm facing right now -> I'm trying to write a function that will accept a char* message and a variable number of arguments. My function will modify the message a little, and then It'll call printf with the message and given parameters. Essentialy, I'm trying to write something like that:
void modifyAndPrintMessage(char* message,...){
char* newMessage; //copy message.
//Here I'm modifying the newMessage to be printed,and then I'd like to print it.
//passed args won't be changed in any way.
printf(newMessage,...); //Of course, this won't work. Any ideas?
fflush(stdout);
}
So, anybody knows what should I do to make it happen? I'd be most grateful for any help :)
You want to use varargs...
void modifyAndPrintMessage( char* message, ... )
{
// do somehthing custom
va_list args;
va_start( args, message );
vprintf( newMessage, args );
va_end( args );
}
void modifyAndPrintMessage(char* message,...)
{ char newMessage[1024]; // **Make sure the buffer is large enough**
va_list args;
va_start(args, message);
vsnprintf(newMessage, message, args);
printf(newMessage);
fflush(stdout);
}
Use varargs to accept variable number of parameters then use sprintf to create the new message
You can use va_list from stdarg.h,
C example: http://www.tutorialspoint.com/cprogramming/c_variable_arguments.htm
C++ example: http://www.cprogramming.com/tutorial/lesson17.html.
An of course, see the man page: http://linux.die.net/man/3/stdarg
Man page example for reference:
#include <stdio.h>
#include <stdarg.h>
void
foo(char *fmt, ...)
{
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt)
switch (*fmt++) {
case 's': /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* need a cast here since va_arg only
takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
There is a library which includes this functionality. Here is some example code from the reference:
#include <stdarg.h> /* va_list, va_start, va_arg, va_end */
int FindMax (int n, ...)
{
int i,val,largest;
va_list vl;
va_start(vl,n);
largest=va_arg(vl,int);
for (i=1;i<n;i++)
{
val=va_arg(vl,int);
largest=(largest>val)?largest:val;
}
va_end(vl);
return largest;
}
The ellipsis is actually valid code, and you can use the va_list object to parse a variable number of parameters.

Passing variable number of arguments around

Say I have a C function which takes a variable number of arguments: How can I call another function which expects a variable number of arguments from inside of it, passing all the arguments that got into the first function?
Example:
void format_string(char *fmt, ...);
void debug_print(int dbg_lvl, char *fmt, ...) {
format_string(fmt, /* how do I pass all the arguments from '...'? */);
fprintf(stdout, fmt);
}
To pass the ellipses on, you initialize a va_list as usual and simply pass it to your second function. You don't use va_arg(). Specifically;
void format_string(char *fmt,va_list argptr, char *formatted_string);
void debug_print(int dbg_lvl, char *fmt, ...)
{
char formatted_string[MAX_FMT_SIZE];
va_list argptr;
va_start(argptr,fmt);
format_string(fmt, argptr, formatted_string);
va_end(argptr);
fprintf(stdout, "%s",formatted_string);
}
There's no way of calling (eg) printf without knowing how many arguments you're passing to it, unless you want to get into naughty and non-portable tricks.
The generally used solution is to always provide an alternate form of vararg functions, so printf has vprintf which takes a va_list in place of the .... The ... versions are just wrappers around the va_list versions.
Variadic Functions can be dangerous. Here's a safer trick:
void func(type* values) {
while(*values) {
x = *values++;
/* do whatever with x */
}
}
func((type[]){val1,val2,val3,val4,0});
In magnificent C++11 you could use variadic templates:
template <typename... Ts>
void format_string(char *fmt, Ts ... ts) {}
template <typename... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts... ts)
{
format_string(fmt, ts...);
}
Though you can solve passing the formatter by storing it in local buffer first, but that needs stack and can sometime be issue to deal with. I tried following and it seems to work fine.
#include <stdarg.h>
#include <stdio.h>
void print(char const* fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vprintf(fmt, arg);
va_end(arg);
}
void printFormatted(char const* fmt, va_list arg)
{
vprintf(fmt, arg);
}
void showLog(int mdl, char const* type, ...)
{
print("\nMDL: %d, TYPE: %s", mdl, type);
va_list arg;
va_start(arg, type);
char const* fmt = va_arg(arg, char const*);
printFormatted(fmt, arg);
va_end(arg);
}
int main()
{
int x = 3, y = 6;
showLog(1, "INF, ", "Value = %d, %d Looks Good! %s", x, y, "Infact Awesome!!");
showLog(1, "ERR");
}
Hope this helps.
You can try macro also.
#define NONE 0x00
#define DBG 0x1F
#define INFO 0x0F
#define ERR 0x07
#define EMR 0x03
#define CRIT 0x01
#define DEBUG_LEVEL ERR
#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...) if((DEBUG_LEVEL & X) == X) \
DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)
int main()
{
int x=10;
DEBUG_PRINT(DBG, "i am x %d\n", x);
return 0;
}
You can use inline assembly for the function call. (in this code I assume the arguments are characters).
void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
{
va_list argumentsToPass;
va_start(argumentsToPass, fmt);
char *list = new char[numOfArgs];
for(int n = 0; n < numOfArgs; n++)
list[n] = va_arg(argumentsToPass, char);
va_end(argumentsToPass);
for(int n = numOfArgs - 1; n >= 0; n--)
{
char next;
next = list[n];
__asm push next;
}
__asm push fmt;
__asm call format_string;
fprintf(stdout, fmt);
}
Ross' solution cleaned-up a bit. Only works if all args are pointers. Also language implementation must support eliding of previous comma if __VA_ARGS__ is empty (both Visual Studio C++ and GCC do).
// pass number of arguments version
#define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}
// NULL terminated array version
#define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}
Short answer
/// logs all messages below this level, level 0 turns off LOG
#ifndef LOG_LEVEL
#define LOG_LEVEL 5 // 0:off, 1:error, 2:warning, 3: info, 4: debug, 5:verbose
#endif
#define _LOG_FORMAT_SHORT(letter, format) "[" #letter "]: " format "\n"
/// short log
#define log_s(level, format, ...) \
if (level <= LOG_LEVEL) \
printf(_LOG_FORMAT_SHORT(level, format), ##__VA_ARGS__)
usage
log_s(1, "fatal error occurred");
log_s(3, "x=%d and name=%s",2, "ali");
output
[1]: fatal error occurred
[3]: x=2 and name=ali
log with file and line number
const char* _getFileName(const char* path)
{
size_t i = 0;
size_t pos = 0;
char* p = (char*)path;
while (*p) {
i++;
if (*p == '/' || *p == '\\') {
pos = i;
}
p++;
}
return path + pos;
}
#define _LOG_FORMAT(letter, format) \
"[" #letter "][%s:%u] %s(): " format "\n", _getFileName(__FILE__), __LINE__, __FUNCTION__
#ifndef LOG_LEVEL
#define LOG_LEVEL 5 // 0:off, 1:error, 2:warning, 3: info, 4: debug, 5:verbose
#endif
/// long log
#define log_l(level, format, ...) \
if (level <= LOG_LEVEL) \
printf(_LOG_FORMAT(level, format), ##__VA_ARGS__)
usage
log_s(1, "fatal error occurred");
log_s(3, "x=%d and name=%s",2, "ali");
output
[1][test.cpp:97] main(): fatal error occurred
[3][test.cpp:98] main(): x=2 and name=ali
custom print function
you can write custom print function and pass ... args to it and it is also possible to combine this with methods above. source from here
int print_custom(const char* format, ...)
{
static char loc_buf[64];
char* temp = loc_buf;
int len;
va_list arg;
va_list copy;
va_start(arg, format);
va_copy(copy, arg);
len = vsnprintf(NULL, 0, format, arg);
va_end(copy);
if (len >= sizeof(loc_buf)) {
temp = (char*)malloc(len + 1);
if (temp == NULL) {
return 0;
}
}
vsnprintf(temp, len + 1, format, arg);
printf(temp); // replace with any print function you want
va_end(arg);
if (len >= sizeof(loc_buf)) {
free(temp);
}
return len;
}
Let's say you have a typical variadic function you've written. Because at least one argument is required before the variadic one ..., you have to always write an extra argument in usage.
Or do you?
If you wrap your variadic function in a macro, you need no preceding arg. Consider this example:
#define LOGI(...)
((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
This is obviously far more convenient, since you needn't specify the initial argument every time.
I'm unsure if this works for all compilers, but it has worked so far for me.
void inner_func(int &i)
{
va_list vars;
va_start(vars, i);
int j = va_arg(vars);
va_end(vars); // Generally useless, but should be included.
}
void func(int i, ...)
{
inner_func(i);
}
You can add the ... to inner_func() if you want, but you don't need it. It works because va_start uses the address of the given variable as the start point. In this case, we are giving it a reference to a variable in func(). So it uses that address and reads the variables after that on the stack. The inner_func() function is reading from the stack address of func(). So it only works if both functions use the same stack segment.
The va_start and va_arg macros will generally work if you give them any var as a starting point. So if you want you can pass pointers to other functions and use those too. You can make your own macros easily enough. All the macros do is typecast memory addresses. However making them work for all the compilers and calling conventions is annoying. So it's generally easier to use the ones that come with the compiler.

Resources