Rust dbg!() macro in C

Posted on 2020-12-19

Rust’s dbg!() macro

Two years ago, someone wrote about a new Rust feature in a release called a dbg!() macro that makes tracing code with less boilerplate. It was a very clever hack.

Doing the equivalent in C

At that time, I thought my favourite language, C, should also get that. So, I wrote this gist, but never really published it, so here it is.

The Github Gist link has an example usage. It doesn’t do colours on terminal etc. It also needs some GNUC features.

/* Copyright 2019 Ramakrishnan Muthukrishnan
 *
 * To the extent possible under law, the author(s) have dedicated 
 * all copyright and related and neighboring rights to this software 
 * to the public domain worldwide. This software is distributed without
 * any warranty.
 *
 * You should have received a copy of the CC0 Public Domain Dedication 
 * along with this software. 
 * If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. 
 */

#ifndef DBG_H_INCLUDED

#define DBG_H_INCLUDED 1

#if defined (__GNUC__)
#if defined (__STDC_VERSION__)

#if __STDC_VERSION__ >= 201112L

#include <stdio.h>

#define PRINTF_FMT_STR(x) _Generic((x), \
    char: "%c", \
    signed char: "%hhd", \
    unsigned char: "%hhu", \
    signed short: "%hd", \
    unsigned short: "%hu", \
    signed int: "%d", \
    unsigned int: "%u", \
    long int: "%ld", \
    unsigned long int: "%lu", \
    long long int: "%lld", \
    unsigned long long int: "%llu", \
    float: "%f", \
    double: "%f", \
    long double: "%Lf", \
    char *: "%s", \
    void *: "%p", \
    bool: "%s" \
)

#define DBG(x) \
    ({  typeof (x) x_;                                             \
        x_ = (x);                                                  \
        fprintf(stderr, "[%s:%d] %s = " , __FILE__, __LINE__, #x); \
        fprintf(stderr, PRINTF_FMT_STR(x), x_); \
        fprintf(stderr, "\n"); \
        x_; \
    })

#endif /* __STDC_VERSION >= XXXX */
#endif /* __STDC_VERSION__ */
#endif /* __GNUC__ */

#endif

Usage

A Rust example:

fn factorial(n: u32) -> u32 {
    if dbg!(n <= 1) {
        dbg!(1)
    } else {
        dbg!(n * factorial(n - 1))
    }
}

Here is the output:

[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = false
[src/main.rs:3] n <= 1 = true
[src/main.rs:4] 1 = 1
[src/main.rs:5] n * factorial(n - 1) = 2
[src/main.rs:5] n * factorial(n - 1) = 6
[src/main.rs:5] n * factorial(n - 1) = 24
[src/main.rs:11] factorial(4) = 24

and an equivalent C example:

#include <stdio.h>
#include <stdbool.h>

#include "dbg.h"

int factorial(int n)
{
    if (DBG(n <= 1))
        return DBG(1);
    else
        return DBG(n * factorial(n - 1));
}

int main(void)
{
    int x = 4;
    
    DBG(factorial(x));
    return 0;
}

and the output:

$ gcc -Wall  foo.c && ./a.out
[foo.c:8] n <= 1 = 0
[foo.c:8] n <= 1 = 0
[foo.c:8] n <= 1 = 0
[foo.c:8] n <= 1 = 1
[foo.c:9] 1 = 1
[foo.c:11] n * factorial(n - 1) = 2
[foo.c:11] n * factorial(n - 1) = 6
[foo.c:11] n * factorial(n - 1) = 24
[foo.c:18] factorial(x) = 24

Enjoy!