I have to change this code fragment from varargs.h to stdarg.h, but I do not know exactly how to:
#ifndef lint
int ll_log (va_alist)
va_dcl
{
int event, result;
LLog *lp;
va_list ap;
va_start (ap);
lp = va_arg (ap, LLog *);
event = va_arg (ap, int);
result = _ll_log (lp, event, ap);
va_end (ap);
return result;
}
When I try build this, compiler says:
error "GCC no longer implements <varargs.h>."
error "Revise your code to use <stdarg.h>."
The program, which I need to compile and run, has a few similar fragments and I need to know how to change them. If you can write some example, I'll be content.
<varargs.h> is a pre-standard C header; use <stdarg.h> instead. The differences:
The function must take at least one named argument.
The function must be prototyped (using the ellipsis terminator).
The va_start macro works differently: it takes two arguments, the first being the va_list to be initialized and the second the name of the last named argument.
Example:
int ll_log (LLog *llog, ...) {
int event, result;
LLog *lp;
va_list ap;
va_start (ap, llog);
lp = llog;
event = va_arg (ap, int);
result = _ll_log (lp, event, ap);
va_end (ap);
return result;
}
Regarding va_start: gcc ignores the second argument, but not giving the correct one is not portable.
You have to include
#include <stdarg.h>
va_ Macro syntax stays the same.
Related
I have a standard logging API built into my C code, a nice simple logF(const char *pFormat, ...) thing which has always, so far, been mapped to vprintf(), i.e.:
void logF(const char *pFormat, ...)
{
va_list args;
va_start(args, pFormat);
vprintf(pFormat, args);
va_end(args);
}
The C code above this API must work on multiple embedded platforms. I've just reached a platform (Nordic NRF52840) where the underlying logging interface I have to work with is presented as a variadic macro of the form NRF_LOG_INFO(...).
QUESTION: how do I correctly pass fn(const char *pFormat, ...) into a BLAH(...) macro? My brain hurts....
This is with GCC 4.9.3, though it would be nice to have a solution that is relatively relaxed about C compiler version.
EDIT 1: noted that I could redefine my logF() function to be a variadic macro and map it there, the issue is that I would then have a platform-specific header file rather than a generic one, I would have to move it down into the platform code and have one for each. Not impossible but more messy.
EDIT 2: I was asked for the trail of how NRF_LOG_INFO() expands. Here's the relevant output of the pre-processor:
#define NRF_LOG_INFO(...) NRF_LOG_INTERNAL_INFO( __VA_ARGS__)
#define NRF_LOG_INTERNAL_INFO(...) NRF_LOG_INTERNAL_MODULE(NRF_LOG_SEVERITY_INFO, NRF_LOG_SEVERITY_INFO, __VA_ARGS__)
#define NRF_LOG_INTERNAL_MODULE(level,level_id,...) if (NRF_LOG_ENABLED && (NRF_LOG_LEVEL >= level) && (level <= NRF_LOG_DEFAULT_LEVEL)) { if (NRF_LOG_FILTER >= level) { LOG_INTERNAL(LOG_SEVERITY_MOD_ID(level_id), __VA_ARGS__); } }
#define LOG_INTERNAL(type,...) LOG_INTERNAL_X(NUM_VA_ARGS_LESS_1( __VA_ARGS__), type, __VA_ARGS__)
#define LOG_INTERNAL_X(N,...) CONCAT_2(LOG_INTERNAL_, N) (__VA_ARGS__)
Then depending on number of args, anything up to:
#define LOG_INTERNAL_6(type,str,arg0,arg1,arg2,arg3,arg4,arg5) nrf_log_frontend_std_6(type, str, (uint32_t)(arg0), (uint32_t)(arg1), (uint32_t)(arg2), (uint32_t)(arg3), (uint32_t)(arg4), (uint32_t)(arg5))
void nrf_log_frontend_std_6(uint32_t severity_mid,
char const * const p_str,
uint32_t val0,
uint32_t val1,
uint32_t val2,
uint32_t val3,
uint32_t val4,
uint32_t val5);
It is not possible to pass the arguments from a variadic function to a variadic macro.
As you want to hide the platform specific macro call from the API header you can process the function arguments with vsnprintf instead of vprintf and call the logging macro with format "%s" and the resulting string buffer.
void logF(const char *pFormat, ...)
{
va_list args;
/* Choose a reasonable size or replace with dynamic allocation based on the return value of vsnprintf */
/* This could also be a static variable or a global variable to avoid allocation of a big buffer on the stack. */
char buffer[1024];
va_start(args, pFormat);
vsnprintf(buffer, sizeof(buffer), pFormat, args);
NRF_LOG_INFO("%s", buffer);
va_end(args);
}
Note that you may have to call NRF_LOG_FLUSH before the buffer goes out of scope or gets overwritten. See https://devzone.nordicsemi.com/f/nordic-q-a/22647/nrf_log_info-how-to-print-log-with-string-parameter
I am trying to write a macro which gets the information and sends that information to another function by splitting the orginal va_list into string and another va_list spawned from the original one.
Below is my code.
Call to macro
/* Usage */
PRINT_LOG("Format log = %d, %f, %s", 1, 2.7, "Test");
My code below
/* my includes here */
#include <stdarg.h>
void printInfo(int level, const char *debugInfo, ...); /* defined in 3rd party API */
void formatLogs(int level, ...);
#define PRINT_LOG(...) formatLogs(0, __VA_ARGS__)
void formatLogs(int level, ...)
{
va_list args;
va_start(args, level);
/* get the first argument from va_list */
const char *debugString = va_arg(args, const char*);
/* here I want to get the rest of the variable args received from PRINT_LOG*/
va_list restOfArgs = ???????; /* restOfArgs should be 1, 2.7, "Test" */
/* Below I want to send the rest of the arguments */
printInfo(level, debugString, args);
va_end(args);
}
Is it possible to send some part of va_list as va_list to another function?? If so, how can i do it?
Thank you very much in advance.
The simplest thing to do, based on the code in your question, is to redefine the macro like so:
#define PRINT_LOG(s, ...) printInfo(0, s, __VA_ARGS__)
and just skip the intermediate function altogether. Because what you're trying to do can't be done like that.
The , ...) variable argument ellipsis is not a va_list. The variable arguments passed to a function aren't realized as a va_list until va_start is invoked. To pass a va_list as a function argument, the function has to have a va_list in its signature, like this:
int vprintf(const char *format, va_list argList);
Following program crashes in 64 bit system. This code is working fine in 32 bit system
I am using centOS 32 bit and centOS 64 bit sytems..
what will be the reason for this ?
#if 1
if (1)
{
memset(message1, '\0', sizeof(message1));
vsprintf(message1, format, vAList);
}
#endif
if (1)
{
//PROGRAM CRASHES in the following line IF AM PUTTING #if 1 in the above code as i did
val = vfprintf(logFile, format,vAList);
}
when program reaches val = vfprintf(logFile,format,vAList);
here it crashes in 64 bit system..
You can't use a variadic argument list twice. vfprintf "uses up" the arguments and the next call probably tries to access memory after the list. In the words of the man page:
These functions [v*printf] do not call the va_end macro. Consequently, the value of ap is undefined after the call. The application should call va_end(ap) itself afterwards.
If your format requires dereferencing, like the %s format, chances are that your program crashes.
You can solve that in two ways: Either wrap all calls to vfprintf in their own va_start and va_end:
int f(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
vprintf(fmt, va);
va_end(va);
va_start(va, fmt);
vprintf(fmt, va);
va_end(va);
return 0;
}
Or make a copy of the variadic list:
int f(const char *fmt, ...)
{
va_list va, vb;
va_start(va, fmt);
va_copy(vb, va);
vprintf(fmt, va);
vprintf(fmt, vb);
va_end(va);
va_end(vb);
return 0;
}
(You must make the copy before the list you copy from has been used.)
Edit: Forgot to clean up the copied va lis, vb in the second example. Fixed now.
i have this chunk of code
static void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
int errno_save;
unsigned long n;
char buf[MAXLINE];
errno_save = errno;
#ifdef HAVE_VSNPRINTF
vsnprintf(buf, sizeof(buf), fmt, ap); /* this is safe */
#else
vsprintf(buf ,fmt, ap); /* this is not safe */
#endif
n = strlen(buf);
if (errnoflag)
snprintf(buf + n, sizeof(buf) - n, ": %s", strerror(errno_save));
strcat(buf, "\n");
if (daemon_proc) {
syslog(level,"%s", buf);
} else {
fflush(stdout);
fputs(buf, stderr);
fflush(stderr);
}
return;
}
when i compile it (Clang 5.0.0 with -Weverything) i obtain those warnings:
Building C object lib/netutils/CMakeFiles/netutils.dir/error.c.o
/Users/User/Desktop/project.cmake/lib/netutils/error.c:98:16: warning: format string is not a string literal [-Wformat-nonliteral]
vsprintf(buf ,fmt, ap); /* this is not safe */
^~~
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/secure/_stdio.h:66:57: note: expanded from
macro 'vsprintf'__builtin___vsprintf_chk (str, 0, __darwin_obsz(str), format, ap)
^
the same thing happens with this other function vsnprintf(buf, sizeof(buf), fmt, ap);
how can i fix this warning?
Thanks
Apparently the solution is to tell Clang that your vsnprintf is called within a function that implements the behaviour of the printf family of functions by, well, calling vsnprintf. You do this with an attribute, as described here:
__attribute__((__format__ (__printf__, 3, 0)))
static void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
...
}
This checks whether the format string is a literal on the calling function. As err_doit takes already a va_list, you should specify the format on the functions that call it, too. The second number, which is 0 here, should then be the argument index of the variadic argument, .... See also this discussion.
Function attributes are a non-standard extension of gcc, which is also implemented for Clang. If you want to keep the code compiler independent, you should wrap the attributes in a macro that hides it for compilers that don't know attributes.
(Disclaimer: I couldn't check this with Clang, but it works for gcc. Edit: fixed wrong argument index in example)
I'm looking to write what I would imagine is a fairly common macro. I want to emulate the repeated "-v" options on many POSIX programs by defining a bunch of macros of the following form:
#define V1(str, ...) if(optv >= 1){printf("%s: "str,prog,__VA_ARGS__);}
int main(int argc, char* argv[])
{
// ... stuff ...
int i = 1;
V1("This contains a variable: %d\n",i);
}
// Output:
// ./program: This contains a variable: 1
where optv counts the number of "-v" options found on the command line and prog contains the program name (neither shown). This works well, but the problem is that I have to use a variable. V1("Output") will generate a compiler error. I could always use V1("Output%s","") but there should be a cleaner solution.
The GNU C preprocessor has a special feature that lets you delete the trailing comma when there are no arguments filling the variadic portion by prepending the token-pasting operator ## to __VA_ARGS__:
#define V1(str, ...) if(optv < 1); else printf("%s: "str,prog, ## __VA_ARGS__)
Alternatively, if you wish to remain fully C99 compliant, you could incorporate the the format string parameter into the ellipsis, but in this instance you'll also need to refactor your code since you want to include the extra prog parameter between the format string and the varargs. Something like this might work:
#define V1(...) if(optv < 1); else myprintf(prog, __VA_ARGS__)
int myprintf(const char *prog, const char *fmt, ...)
{
// Print out the program name, then forward the rest onto printf
printf("%s: ", prog);
va_list ap;
va_start(ap, fmt);
int ret = vprintf(fmt, ap);
va_end(ap);
return ret;
}
Then, V1("Output") expands to myprintf(prog, "Output") without using any non-C99 compiler extensions.
EDIT
Also note that I inverted the if condition in the macro, due to some weird issues that can arise if you invoke the macro inside an if statement without braces—see this FAQ for a detailed explanation.
Why don't you use 2 different macros for each verbosity level; one which prints a message and variable, and one which just prints a message?
You should probably write yourself a small support function so that you can do the job cleanly:
extern void vb_print(const char *format, ...);
#define V1(...) do { if (optv >= 1) vb_print(__VA_ARGS__); } while (0)
I assume that both optv and prog are global variables. These would go into a header (you wouldn't write them out in the programs themselves, would you?).
The function can be:
#include <stdio.h>
#include <stdarg.h>
extern const char *prog;
void vb_print(const char *format, ...)
{
va_list args;
va_start(args, format);
printf("%s:", prog);
vprintf(format, args);
va_end(args);
}
There's no rocket science in there. You can tweak the system to your heart's content, allowing a choice of where the information is written, flushing the output, ensuring there's a newline at the end, etc.
Try this:
#define V1X(str, ...) if(optv >= 1) {printf("%s: "str,prog,__VA_ARGS__);} else
#define V1(...) V1X(__VA_ARGS__,0)
I believe that fixes the problem you described, and the else at the end fixed another problem.