I built a custom logging function that takes in a "log level" and string. A user would specify the log level associated with the message (i.e. error, warning, trace, etc.). The logging function would only print the message to the console depending on the current log level configured.
int global_log_level = INFO;
void logger(int msg_log_level, char * msg)
{
if(msg_log_level >= global_log_level )
{
printf("Log Level = %u: %s", msg_log_level, msg);
}
}
Ideally, I want to feed formatted strings to this function.
logger(LOG_LEVEL_ERROR, "Error of %u occurred\n", error_code);
However, by adding this "wrapping" logic, I'm unable to input formatted message. Instead, I have to write the message to a temporary string and then feed that into the function.
char temp[512];
sprintf("Error of %u occurred\n", error_code);
logger(LOG_LEVEL_ERROR, temp);
Is there a way to implement the logger function such that I don't need to have the user create a temporary string themselves?
This is question 15.5 on the C FAQ list.
You want vprintf, which lets you write your own printf-like function logger, but where you can pass the actual format string and arguments off to vprintf to do the work. The trick is that you need to construct a special va_arg type to "point to" the variable-length argument list. It looks like this:
#include <stdarg.h>
int global_log_level = INFO;
void logger(int msg_log_level, char * msg, ...)
{
va_list argp;
va_start(argp, msg);
if(msg_log_level >= global_log_level )
{
printf("Log Level = %u: ", msg_log_level);
vprintf(msg, argp);
}
va_end(argp);
}
As an addition to the #SteveSummit answer many compilers have special pragmas or attributes to indicate that the function is "printf-like". It allows the compiler to check parameters compile-time same as it is done when using printf
Example gcc :
format (archetype, string-index, first-to-check)
The format attribute specifies that a function takes printf, scanf,
strftime or strfmon style arguments which should be type-checked
against a format string. For example, the declaration:
extern int
my_printf (void *my_object, const char *my_format, ...)
__attribute__ ((format (printf, 2, 3)));
causes the compiler to check the arguments in calls to my_printf for consistency with the printf style format string argument
my_format.
The parameter archetype determines how the format string is interpreted, and should be printf, scanf, strftime or strfmon. (You
can also use __printf__, __scanf__, __strftime__ or __strfmon__.) The
parameter string-index specifies which argument is the format string
argument (starting from 1), while first-to-check is the number of the
first argument to check against the format string. For functions where
the arguments are not available to be checked (such as vprintf),
specify the third parameter as zero. In this case the compiler only
checks the format string for consistency. For strftime formats, the
third parameter is required to be zero.
In the example above, the format string (my_format) is the second argument of the function my_print, and the arguments to check start
with the third argument, so the correct parameters for the format
attribute are 2 and 3.
The format attribute allows you to identify your own functions which take format strings as arguments, so that GCC can check the
calls to these functions for errors. The compiler always (unless
-ffreestanding is used) checks formats for the standard library functions printf, fprintf, sprintf, scanf, fscanf, sscanf, strftime,
vprintf, vfprintf and vsprintf whenever such warnings are requested
(using -Wformat), so there is no need to modify the header file
stdio.h. In C99 mode, the functions snprintf, vsnprintf, vscanf,
vfscanf and vsscanf are also checked. Except in strictly conforming C
standard modes, the X/Open function strfmon is also checked as are
printf_unlocked and fprintf_unlocked. See Options Controlling C
Dialect.
Source: https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Function-Attributes.html
I am trying to use a single macro argument (provided by third party) to display a string with placeholders and values of those place holder.
#define DBG1(AR1) do{ \
printf(AR1); \
}while(0);
int main()
{
int varDummy = 123;
/* Expecting Test Message 123*/
DBG1(("Test Message %d\r\n", varDummy));
return 0;
}
Notice the argument of DBG1 ("Test Message %d\r\n", varDummy) is in parentheses and is being treated whole as a string i.e. const char* and error shows up that varDummy not allowed to be converted due to it's int type.
In the actual code the argument of DBG1 has different number of place holders (%d, %x...) and respective values which have to be printed. Apparently I am unable to display the passed string along with its place holder values.
In the real code I have the following macro from third party
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
/* discarding the first argument */
#define LWIP_DEBUGF(debug, message) do{ \
DBG1(message) \
}while(0);
Please note this is a resource constrained embedded system.
Notice the argument of DBG1 ("Test Message %d\r\n", varDummy) is in parentheses …
This problem is at the same time the solution. Since there are already parentheses, you need not supply them in your DBG1 macro and can change printf(AR1); \ to printf AR1; \. Besides that, you can simplify the macro to
#define DBG1(AR1) printf AR1;
(since you can't change the LWIP_DEBUGF macro from third party, we unfortunately have to keep the semicolon).
You can use a temporary buffer to hold the adjointed string, something like this:
#define DBG1(AR1) do{\
printf(AR1);\
} while(0)
#define BUFFER_SIZE 100
int main()
{
int varDummy = 123;
char buffer[BUFFER_SIZE];
sprintf(buffer, "Test Message %d\r\n", varDummy);
/* Expecting Test Message 123*/
DBG1(buffer);
return 0;
}
Notice the argument of DBG1 is in parentheses
Yeah that's the problem, you are calling printf(("Test Message %d\r\n", varDummy)); where the inner parentheses has turned the , from a function argument separator into the comma operator. The comma operator discards the left operand and returns the right. Which is int, a type incompatible with the format string of printf, which explains the compiler error.
Instead you should be using a variadic macro:
#define DBG1(AR1,...) printf(AR1, __VA_ARGS__)
...
DBG1("Test Message %d\r\n", varDummy);
The do-while(0) trick is pointless to use in this case.
Please note this is a resource constrained embedded system.
Then you shouldn't be using stdio.h or variadic functions in the first place. Instead, design your own product-specific logging module. Writing a little snippet that spits out strings on UART isn't rocket science.
Similarly, parsing raw binary data from UART isn't rocket science either, so you don't actually need the string conversion, just a decent terminal program on the PC.
I want to write a function to print characters on an LCD in a similar way that printf/sprintf does using formatting strings.
You may use sprintf function to format the strings and print to LCD.
char buffer[50];
int a = 10, b = 20, c;
c = a + b;
sprintf(buffer, "Sum of %d and %d is %d", a, b, c);
Now the buffer will have the formatted strings
You could write a variadic function and pass the parameters on to vsnprintf():
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
void display(int foo, int bar, char const *format, ...)
{
va_list arglist;
va_start(arglist, format);
int length = vsnprintf(NULL, 0, format, arglist);
char *buffer = malloc(length * sizeof *buffer);
vsnprintf(buffer, length, format, arglist);
va_end(arglist);
puts(buffer);
free(buffer);
}
int main(void)
{
display(42, 13, "%s %d %f", "Hello", 99, 100.13);
}
This answer takes the best parts of all of the other answers and puts them into one. I consider it to be the best way to do this given all factors, and will explain in more detail after presenting the example.
Summary:
Here's a full example, including with basic error checking in the function. Here I create a printf-like function called lcd_printf(), which works exactly like printf(). It uses vsnprintf() to store a formatted string into a statically-allocated buffer. You can then send this buffer to the LCD display at the location indicated by my comment.
Example Code:
lcd_print.h:
// For info on the gcc "format" attribute, read here under the section titled
// "format (archetype, string-index, first-to-check)":
// https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/Common-Function-Attributes.html#Common-Function-Attributes.
int lcd_printf(const char * format, ...) __attribute__((format(printf, 1, 2)));
lcd_print.c:
#include "lcd_print.h"
#include <stdarg.h> // for variable args: va_list
#include <stdio.h> // for vsnprintf()
#include <limits.h> // for INT_MIN
// `printf`-like function to print to the LCD display.
// Returns the number of chars printed, or a negative number in the event of an error.
// Error Return codes:
// 1. INT_MIN if vsnprintf encoding error, OR
// 2. negative of the number of chars it *would have printed* had the buffer been large enough (ie: buffer would
// have needed to be the absolute value of this size + 1 for null terminator)
int lcd_printf(const char * format, ...)
{
int return_code;
// Formatted string buffer: make as long as you need it to be to hold the longest string you'd ever want
// to print + null terminator
char formatted_str[128];
va_list arglist;
va_start(arglist, format);
// Produce the formatted string; see vsnprintf documentation: http://www.cplusplus.com/reference/cstdio/vsnprintf/
int num_chars_to_print = vsnprintf(formatted_str, sizeof(formatted_str), format, arglist);
va_end(arglist);
if (num_chars_to_print < 0)
{
// Encoding error
return_code = INT_MIN;
return return_code; // exit early
}
else if (num_chars_to_print >= sizeof(formatted_str))
{
// formatted_str buffer not long enough
return_code = -num_chars_to_print;
// Do NOT return here; rather, continue and print what we can
}
else
{
// No error
return_code = num_chars_to_print;
}
// Now do whatever is required to send the formatted_str buffer to the LCD display here.
return return_code;
}
main.c:
#include "lcd_print.h"
int main(void)
{
int num1 = 7;
int num2 = -1000;
unsigned int num3 = 0x812A;
lcd_printf("my 3 numbers are %i, %i, 0x%4X\n", num1, num2, num3);
return 0;
}
Explanation & comparison to alternate approaches:
#Harikrishnan points out you should use sprintf(). This is on the right track, and is a valid, but less versatile and complete approach. Creating a new variadic function which uses vsnprintf(), as #Swordfish and I have done, is better.
#Swordfish does a fantastic demonstration of the proper usage of vsnprintf() in order to create your own printf()-like variadic function. His example (aside from lacking error handling) is the perfect template for a custom printf()-like implementation which relies on dynamic memory allocation. His first call to vsnprintf(), with a NULL destination buffer, does nothing more than determine how many bytes he needs to allocate for the formatted string (this is an ingenious and commonly-used trick for this application), and his second call to vsnprintf() actually creates the formatted string. For non-real-time applications which also have large amounts of RAM (ex: PC applications) this is the perfect approach. However, for microcontrollers I strongly recommend against it because:
It is non-deterministic. Calling free() can take different (and indeterminable before-hand) amounts of time to complete each time you call it. This is because the heap memory becomes fragmented over time. This means this approach is not good for real-time systems.
For more information about various heap implementations for malloc() and free(), review the 5 heap implementations, for example, described by FreeRTOS here: https://www.freertos.org/a00111.html. Search this page for "deterministic".
It is unbounded. It will attempt to malloc() ANY amount of memory required for the formatted string. This is bad, as it is more prone to stack-overflow. On safety-critical microcontroller-based systems, stack overflow needs to be strongly guarded against. A preferred approach is to use statically-allocated memory like I have done, with a fixed maximum size.
Additionally, it is lacking the GCC "format" attribute, which is a nice touch (more on this below).
#P__J__ mentions the GCC "format" attribute. My example uses this as well.
If using the GCC compiler, or any other compiler which has something similar, this is highly recommended to add to any custom printf()-like function you make.
The GCC documentation, under the section called format (archetype, string-index, first-to-check), states:
The format attribute specifies that a function takes printf, scanf, strftime or strfmon style arguments that should be type-checked against a format string.
In other words, it provides extra protections and checks to your custom printf()-like function at compile time. This is good.
For our case, simply use printf as the archetype, and a number for the string-index and first-to-check parameters.
The parameter string-index specifies which argument is the format string argument (starting from 1), while first-to-check is the number of the first argument to check against the format string.
Since non-static C++ methods have an implicit this argument, the arguments of such methods should be counted from two, not one, when giving values for string-index and first-to-check.
In other words, here are some valid example usages for this attribute applied to printf()-like function prototypes:
In C:
int lcd_printf(const char * format, ...) __attribute__((format(printf, 1, 2))); // 1 is the format-string index (1-based), and 2 is the variadic argument (`...`) index (1-based)
int lcd_printf(my_type my_var, const char * format, ...) __attribute__((format(printf, 2, 3))); // 2 is the format-string index (1-based), and 3 is the variadic argument (`...`) index (1-based)
int lcd_printf(my_type my_var, my_type my_var2, const char * format, my_type my_var3, ...) __attribute__((format(printf, 3, 5))); // 3 is the format-string index (1-based), and 5 is the variadic argument (`...`) index (1-based)
In C++:
int lcd_printf(const char * format, ...) __attribute__((format(printf, 2, 3))); // 2 is the format-string index (2-based), and 3 is the variadic argument (`...`) index (2-based)
int lcd_printf(my_type my_var, const char * format, ...) __attribute__((format(printf, 3, 4))); // 3 is the format-string index (2-based), and 4 is the variadic argument (`...`) index (2-based)
int lcd_printf(my_type my_var, my_type my_var2, const char * format, my_type my_var3, ...) __attribute__((format(printf, 4, 6))); // 4 is the format-string index (2-based), and 6 is the variadic argument (`...`) index (2-based)
Read more in my other answer here: How should I properly use __attribute__ ((format (printf, x, y))) inside a class method in C++?.
So, putting everything above together, you get the ideal solution for microcontrollers, which I've presented above.
as most commnly used arm compiler is gcc I will only focus on this one. The compiler can check the format & the prameters same as printf does
__attribute__ ((format (printf...
From the gcc documentation
format (archetype, string-index, first-to-check)
The format attribute specifies that a function takes printf, scanf, strftime or strfmon style arguments which should be
type-checked against a format string. For example, the declaration:
extern int
my_printf (void *my_object, const char *my_format, ...)
__attribute__ ((format (printf, 2, 3)));
causes the compiler to check the arguments in calls to my_printf for consistency with the printf style format string argument
my_format.
The parameter archetype determines how the format string is interpreted, and should be printf, scanf, strftime or strfmon. (You
can also use printf, scanf, strftime or strfmon.) The
parameter string-index specifies which argument is the format string
argument (starting from 1), while first-to-check is the number of the
first argument to check against the format string. For functions where
the arguments are not available to be checked (such as vprintf),
specify the third parameter as zero. In this case the compiler only
checks the format string for consistency. For strftime formats, the
third parameter is required to be zero.
In the example above, the format string (my_format) is the second argument of the function my_print, and the arguments to check start
with the third argument, so the correct parameters for the format
attribute are 2 and 3.
The format attribute allows you to identify your own functions which take format strings as arguments, so that GCC can check the
calls to these functions for errors. The compiler always (unless
-ffreestanding is used) checks formats for the standard library functions printf, fprintf, sprintf, scanf, fscanf, sscanf, strftime,
vprintf, vfprintf and vsprintf whenever such warnings are requested
(using -Wformat), so there is no need to modify the header file
stdio.h. In C99 mode, the functions snprintf, vsnprintf, vscanf,
vfscanf and vsscanf are also checked. Except in strictly conforming C
standard modes, the X/Open function strfmon is also checked as are
printf_unlocked and fprintf_unlocked. See Options Controlling C
Dialect. format_arg (string-index)
The format_arg attribute specifies that a function takes a format string for a printf, scanf, strftime or strfmon style function and
modifies it (for example, to translate it into another language), so
the result can be passed to a printf, scanf, strftime or strfmon style
function (with the remaining arguments to the format function the same
as they would have been for the unmodified string). For example, the
declaration:
extern char *
my_dgettext (char *my_domain, const char *my_format)
__attribute__ ((format_arg (2)));
causes the compiler to check the arguments in calls to a printf, scanf, strftime or strfmon type function, whose format string argument
is a call to the my_dgettext function, for consistency with the format
string argument my_format. If the format_arg attribute had not been
specified, all the compiler could tell in such calls to format
functions would be that the format string argument is not constant;
this would generate a warning when -Wformat-nonliteral is used, but
the calls could not be checked without the attribute.
I'm creating my own kind of C library with my own kind of functions and also to help be better understand C and not just depend on GNU C Library for everything.
So far I got a bit of functions running but I wan't to tweak my printo function a little bit to support unlimited arguments (Restricted to chars only but your answer may also support passing in integer arguments). Here is the printo code:
#include "../GV.h"
#include "OtherUtils.h"
int print(char *WRITEOUT1){
char *WRITEOUT=&WRITEOUT1[0];
int AMOUNT=GetCharSize(WRITEOUT1);
register int SYSCALLNO asm("rax")=SYSWRITE;
register int FD asm("rdi")=STANDARDFD;
register char *BUF asm("rsi")=WRITEOUT;
register int BYTES asm("rdx")=AMOUNT;
asm("syscall");
return 0;
}
GetCharSize function code if anyone needs it:
#include "../GV.h"
int GetCharSize(char *arg){
for(i=0;arg[i]!='\0';i++){
}
return i;
}
GV.h has variables defined like int i;
Before I asked this question I looked into C pre-processors like __VA_ARGS__ but I somewhat couldn't wrap my head around it.
The ... notation is a part of the syntax of the C language. It is used in defining the argument list of a function as the last argument and means "followed by zero or more arguments of any type". E.g.
int printf (char *format, ...);
A function defined in this way can now be called with all the mandatory arguments, plus any number of additional arguments, like printf.
A function defined is this way must have a means of knowing how many arguments there are, and what the type of each argument is. printf knows this from the format specification string, the only required argument of printf.
Assuming an Intel system that uses a stack to pass arguments, and where arguments are pushed right-to-left (last argument pushed first), so the first optional argument will be directly after the format specification string on the stack, printf now proceeds as follows: take the address of the format specfication string and increment that address with the size of a char *. This is the start of the optional arguments. Now look at the format specifier string to know the type of the next argument; get that type of argument from the stack, increment it with the size of that argument to get the next address. Do this until there are no more format specifiers.
In the following code,
#include<conio.h>
clrscr();
gotoxy(10, 20);
ch= getch(a);
we can see that the library functions have been called without defining their prototype, the three library functions: clrscr() gotoxy(int int) and getch() have their prototypes defined in the conio.h header file which appear in the header file itself like this,
void clrscr();
void gotoxy(int int);
int getch();
But in the following code how does the compiler know the prototype of the printf() function? Since the code gets executed without any errors, although in the first printf() the last int format specifier prints the garbage value and in the second the value of j doesn't get printed at all since it has not been specified.
#include<stdio.h>
int i=10, int j=20;
printf("%d%d%d",i,j);
printf("%d",i,j);
How does the header file stdio.h describe the scenarios when the format specifiers are float or char variables for the printf() functions?
The compiler knows the prototype because it's declared in <stdio.h>, which you're including.
<stdio.h> does not describe the format specifiers. It simply says
int printf(const char *, ...);
The ... indicates that printf takes a variable number of arguments (of unknown type).
Your first call to printf has undefined behavior (not enough arguments for the format string). Your second call is fine (excess arguments are simply ignored). C compilers are not required to detect format string issues like this.
The stdio header always includes the same declaration of printf() which is:
int printf(const char *fmt, ...);
the declaration is always the same no matter which placehoder you put into your format string.
Normally the definition\implementation of printf iterates over each character in the fmt string and will replace each identified placeholder with the appropriate argument. The compiled code will try to use the arguments instead of the placeholders in the same order as they are specified in the source code.
Any predefined function like printf should be prototyped or declared before using, in respective header file so that while executing the program compiler can verify whether programmer uses correct format or not.
printf() function prototype is declared in header file called stdio.h.
when compiler executes first line of your code which is #include<stdio.h> It came to know about printf prototype.
man 3 printf says
int printf(const char *format, ...);// last 3 dots specifies the variable no of arguments printf can take
In your code below printf is causing undefined behaviour because printf expects 3 arguments but you provided only 2 arguments.
printf("%d%d%d",i,j);