Smart print for C

Usage example:

#include "print.h"

int main() {
print("number:", 25,
"fractional number:", 1.2345,
"expression:", (2.0 + 5) / 3
number: 25 fractional number: 1.2345 expression: 2.33333 Generic calls are not only easier to type than standard printf (), but there will be no more compiler warnings that the format character after% "is of the wrong type.

Genericprint, can print all major C types, integers, signed and unsigned, floating point and pointers:

char *s = "abc";
void *p = main;
long l = 1234567890123456789;
unsigned char byte = 222;
char ch = 'A';
print("string:", s, "pointer:", p, "long:", l);
print(byte, ch)
string: "abc" pointer: 0x402330 long: 1234567890123456789
222 <0xDE> 'A'65
Different types are displayed in different colors, the palette can be customized, or the color can be turned off altogether.

You can even print arrays:

int x[] = { 1, 2, 3 };
char *args[] = { "gcc", "hello.c", "-o", "hello" };
print (x, args);
[1 2 3] ["gcc" "hello.c" "-o" "hello"] How it works? In fact, print is a macro, more precisely a variadic macro, which generates a call to a real function. The first parameter the macro constructs for this function is the number of arguments entered by the user. A well-known trick is used for this:

void __print_func(int count, ...);
#define count_arg(q,w,e,r,t,y,...) y
#define print (a ...) __print_func (count_arg (a, 5,4,3,2,1,0), a);
This solution does not shine with elegance, thanks to the limitations of the preprocessor, as you can see, the maximum number of arguments in this example is 6 (in my library now is 26).

The second parameter of the spread operator ... is the list of all arguments itself. The __print_fun () function uses the usual stdarg.h to traverse this list:

void prn(int count, ...) {
va_list v;
va_start(v, types);
for (int i = 0; i < count; i++) {
printf("%'li", va_arg(v, unsigned long));
Now, the tricky question is: how do you know the types? After all, va_arg is not a magician, we must specify the type for each argument. In the example above, this is an unsigned long, but what the user will actually transmit, we do not yet know.

Most C compilers understand this:

int x;
int y = __builtin_types_compatible_p (typeof (x), int);
This is a compile-time construct, accepts types and returns a boolean value, in this example y will be 1 or true because int == int.

Also, there is such a challenge as __builtin_choose_expr (a, b, c). Is it analogous? b: c compile time, using these compiler extensions, you can write something like a switch that returns the type of a variable as a number, 3 for int, 2 for double, etc .:

#define __get_type(x) \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), double), 1, \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char), 2, \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int), 3, \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), void*), 4, \
....... etc
Further, using standard tricks with variadic macro, that is, we write __get_type (), many or, more precisely, count times separated by commas, create an array char types [], and substitute it as the second parameter in the call to the print function, its title will become like this:

void __print_func (int count, char types [], ...) { Now we can safely take arguments using va_arg, looking at their types from the array:

for (int i = 0; i < count; i++) {
if (types[i] == 'd') {
double d = va_arg(v, double);
printf("%'G", d);
else if (types[i] == 'i') {
int d = va_arg(v, int);
printf("%'i", d);
In fact, to print arrays, you also need to pass sizeof (), which looks something like this:

(short []) (sizeof (a), sizeof (b), sizeof (c), .........) For economy, the type and size are packed in unsigned short: __get_type (x) + sizeof (x) << 5 ...

All the work of the preprocessor and builtins is compiled very efficiently, here is a call like this:

print (42, 42); Gcc -O1 is compiled into the following code:

xor eax, eax
mov ecx, 42
mov edx, 42
lea rsi, [rsp+12]
mov edi, 2
mov DWORD PTR [rsp+12], 0x00840084
call __print_func
The extensions described above support the compilers GCC5.1 +, Clan3.4. + 1, Intel C 17.0.0+, and TinyC. They are not on MSVC, perhaps there are similar ones, but I could not find the relevant information.

This is how the color is drawn:

void __print_color(int a) {
if (!__print_enable_color) return;
if (a == -1) printf("\x1b(B\x1b[m");
else printf("\x1b[38;5;%im", a);
By changing the value of the global variable __print_enable_color to 0, you can disable color output. And the __print_setup_colors () function allows you to set the palette:

void __print_setup_colors (int normal, int number, int string, int hex, int fractional) { It will also be necessary to add automatic color off if stdout is not a console, but a file or pipe.

There is fprint (fd ...) to work with stderr and any descriptors.

Perhaps you have a question why not_Generic, but __builtin_types_compatible_p ? The fact is that_Generic does not distinguish arrays from pointers, for example, int * is the same for it as int [], therefore, it would not work to display arrays with_Generic.

Link to github
Tags: printf
Papay 10 march 2021, 16:00
Vote for this post
Bring it to the Main Page


Leave a Reply

Avaible tags
  • <b>...</b>highlighting important text on the page in bold
  • <i>..</i>highlighting important text on the page in italic
  • <u>...</u>allocated with tag <u> text shownas underlined
  • <s>...</s>allocated with tag <s> text shown as strikethrough
  • <sup>...</sup>, <sub>...</sub>text in the tag <sup> appears as a superscript, <sub> - subscript
  • <blockquote>...</blockquote>For  highlight citation, use the tag <blockquote>
  • <code lang="lang">...</code>highlighting the program code (supported by bash, cpp, cs, css, xml, html, java, javascript, lisp, lua, php, perl, python, ruby, sql, scala, text)
  • <a href="http://...">...</a>link, specify the desired Internet address in the href attribute
  • <img src="http://..." alt="text" />specify the full path of image in the src attribute