Variadic Macro for custom print function with argument expansion in C - c

I am trying replicate the printf for my application. Instead of using stdout, I have a different set of user specific pointers that basically point to the location one wants to print something. So instead of having:
#define print(fmt, ...) printf(stdout, fmt, __VA_ARGS__)
I want something like this:
#define print(x, fmt, ...) dev_printf(x->pointer_to_a_screen, fmt, __VA_ARGS__)
Here the expectation is that the x is a struct that stores a users current context and information with it. I want to automatically expand x to use the pointer_to_a_screen which basically tells dev_printf where to print the given input. Expectation is the dev_printf needs to behave as any other standard printf function and the user can specify variable arguments.
Is this even possible? I keep getting a compile error for above #define print repeatedly and I cant understand why? I can't expand the first argument?

The trailing comma has to be removed in case no arguments are passed. Ie.
#define print(x, fmt, ...) dev_printf(x->pointer_to_a_screen, fmt, __VA_ARGS__)
print(something, "arg: %d", i);
// expands to dev_printf(something->pointer_to_a_screen, "arg: %d", i);
// all fine
// but:
print(something, "no arg");
// expands to dev_printf(something->pointer_to_a_screen, "not arg", );
// ^^
In your case you can just:
#define print(x, ...) dev_printf(x->pointer_to_a_screen, __VA_ARGS__)
Newer code should use __VA_OPT__:
#define print(x, fmt, ...) dev_printf(x->pointer_to_a_screen, fmt __VA_OPT__(,) __VA_ARGS__)
In days before __VA_OPT__ it was typical to use GNU extension ##__VA_ARGS__to:
#define print(x, fmt, ...) dev_printf(x->pointer_to_a_screen, fmt, ##__VA_ARGS__)
You can also use POSIX fopencookie to create a custom stream that you could manipulate with normal fprintf functions.
Each of standard *printf function have equivalent v*printf function. The best is to provide your own dev_vprintf function that would take a va_list and then provide a simple wrapper:
#ifdef __GNUC__
// ex. on gcc compiler this causes printf-like warnings to happen
__attribute__((__format__(__printf__, 2, 3)))
#endif
return_type print(some_type *x, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
return_type e = dev_vprintf(x->pointer_to_a_screen, fmt, va);
va_end(va);
return e;
}
Such wrapper will check for type mismatches, is easy to maintain and it's easy to check for errors.

I would not use macro for that
int mystrangeprintf(MYTYPE *x, const char *fmt, ...)
{
int length;
char buff[256];
va_list va;
va_start(va, fmt);
length = vsnprintf(buff, sizeof(buff), fmt, va);
va_end(va);
dev_printString(x->pointer_to_a_screen, buff);
return length;
}

Related

How to use parameters const char *format in custom printf

I'm looking for a function that print log information, in order to debug my program.
This function must look at a boolean and decide if write the log in a file or to the console.
int useFile;
int log_informations( ? )
{
if(useFile)
{
// open/close FILE *pf omitted
fprintf(pf, ? );
}
else
{
printf( ? );
}
return 0;
}
int main()
{
int answer = 42;
log_informations("The answer is %d.", answer);
return 0;
}
Can you help me with the parameter? I did not found any reference.
NB: In this question I made things clear and simpler as they were in my context, so I do not need workaround but, possibly, a simple answer.
Thank you in advance ;)
As other mentioned, you don't need to actually write a function. A macro could help here:
#define LOG(fmt, ...) fprintf(logToFile ? fileLogger : stdout, fmt, ##__VA_ARGS__)
To answer your question you can look at how printf and vprintf work:
int printf(const char *format, ...);
int vprintf(const char *format, va_list arg);
printf is a variadic function and you want to provide your own version that wraps vprintf and vfprintf.
#include <stdarg.h> // needed for va_list, va_start, and va_end.
int my_printf(const char *format, ...)
{
va_list ap;
int ret;
va_start(ap, format);
if (useFile) ret = vfprintf(pf, format, ap);
else ret = vprintf(format, ap);
va_end(ap);
return ret;
}
EDIT: as #phuclv mentioned, if you want to include file, line, and/or function information in your log a macro is the only way. For example, for appending file and line information you could do something like this:
#define LOG(fmt, ...) fprintf(logToFile ? fileLogger : stdout, __FILE__ ":" STRINGIFY(__LINE__) " " fmt, ##__VA_ARGS__)
#define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x)
You need STRINGIGY for __LINE__ because it is an int.

Separate different types of arguments from va_list

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);

Variadic Macro calling fprintf: how to add arguments to __VA_ARGS__?

I have two macros:
#define LogFunction(str) fprintf(stdout, "%s: %s\n",__FUNCTION__,(str))
#define LogPrintf(f_, ...) fprintf(stdout, (f_), ##__VA_ARGS__)
So i can use them this way:
void MyFunction()
{
int N=4;
LogFunction("START"); // Outputs "MyFunction: START"
LogPrintf("N=%d\n", N); // Outputs "N=4"
}
What I would like to change is to
add FUNCTION at the start of the LogPrintf as it is in LogFunction
add "\n" at the end of the LogPrintf without having to remember to put it in myself
so in the end i could have just one macro for my outputs.
I've tried to understand if Appending to __VA_ARGS__ could've been useful, but i admit that i've not understood if it is related to my case :(
Thanks.
why not doing it in 3 steps?
#define LogPrintf(f_, ...) do { fprintf(stdout, "%s: ",__FUNCTION__); \
fprintf(stdout, (f_), ##__VA_ARGS__); \
fprintf(stdout,"\n"); } while(0)
this does 3 prints, but at least it's simple and does what you want. the do while(0) trick makes sure this is one only block (when using if without braces) and requires semicolon.
If you're willing to rely on the first argument to LogPrintf being a string literal, then you should be able to use string concatenation to achieve your objective:
// Assumes f_ always corresponds to a string literal:
#define LogPrintf(f_, ...) fprintf(stdout, "%s: " f_ "\n", __FUNCTION__, ##__VA_ARGS__)
Note, however, that in standard C, the LogPrintf macro requires at least two arguments, and the ## has no place. I keep it here only because you use it in your original code.
If you must accept format string expressions other than string literals, however, then your simplest alternative is to perform multiple I/O calls, as another answer also suggests:
#define LogPrintf(f_, ...) do { \
fprintf(stdout, "%s: ", __FUNCTION__); \
fprintf(stdout, (f_), ##__VA_ARGS__); \
fputc('\n', stdout); \
} while (0)
Note that in this case, the macro expands to a statement (sans trailing semicolon), whereas in the other, the macro expands to an expression. If you want the return value(s) of any of the I/O functions, then you'll have to make special provisions for that in this case.
If that doesn't work for you either, then the ultimate alternative is to write and use a helper function, as was suggested in comments:
#define LogPrintf(f_, ...) log_printf_impl(stdout, __FUNCTION__, (f_), ##__VA_ARGS__)
int log_printf_impl(FILE *f, const char *func, const char *fmt, ...) {
static const char prefix[] = "%s: ";
size_t flen = strlen(fmt);
va_list args;
int result = -1;
char *aug_fmt = malloc(sizeof(prefix) + strlen(fmt) + 1);
if (aug_fmt) {
va_start(args, fmt);
sprintf(aug_fmt, "%s%s\n", prefix, fmt);
result = vfprintf(f, aug_fmt, func, args);
va_end(args);
free(aug_fmt);
}
return result;
}

printf macro for compiler with and without support for VARIADIC

Please help me writing a printf macro for one compiler that supports VARIADIC and another that does not.
For instance:
#ifdef HAVE__VA_ARGS
printf macro
#else
printf macro
An solution with VARIADIC can be lock like this.
#define my_printf(_format, ...) { \
printf(_format, __VA_ARGS__); \
}
And if you really have an compiler without VARIADIC, then you have to implement an function with an variable argument list.
#include <stdarg.h>
#include <std.h>
int my_printf(const char *format, ...)
{
va_list ap;
va_start(ap, dst);
return vprintf(format, ap);
}

C puzzle...How Can I pass variadic arguments into a macro?

I got stuck here...
#include <stdio.h>
#define DBG_LVL(lvl, stmt) \
do{ \
if(lvl>1) printf stmt; \
}while(0)
#define DBG_INFO(stmt) DBG_LVL(1, stmt)
#define DBG_ERROR(stmt) DBG_LVL(2, stmt)
int main()
{
DBG_INFO(("hello, %s!\n", "world"));
DBG_ERROR(("crazy, %s!\n", "world"));
return 0;
}
As you can see, the code above uses macros like "DBG_INFO" or "DBG_ERROR" to control debug information level.
Now for some reason, I have to replace DBG_LVL() with a new function.
void myprint(int lvl, const char * format, ...);
The only difference is the debug level is taken as its fisrt parameter.
I was thinking:
#define DBG_LVL(lvl, stmt) myprint(lvl, stmt)
Of course it failed, because the "stmt" expression includes parentheses around.
Then I googled around trying to find a way to strip the parentheses, seems there's nothing could help.
I also tried some tricks to pass parameters into "stmt", still failed... :(
Can you help me?
# define EXPAND_ARGS(...) __VA_ARGS__
# define DBG_LVL(lvl, stmt) myprint(lvl, EXPAND_ARGS stmt);
Don't write this as a macro.
Write instead an ordinary varargs function:
void DBG_LVL(int level, char *fmt, ...)
{
if (level < 1) return;
va_list args;
va_start(args, fmt);
vaprintf(fmt, args);
va_end(args);
}
For myprint(), define a similar vamyprint(int lvl, const char *format, va_list ap) as well, and forward the same way.

Resources