Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modify printf()s via macro to include file and line number information

Tags:

c

I was just wondering if there is some macro-hack we can use, to alter existing printf() statements in a project.

/* file.c */
printf ("%s", strerror(errno));

/* After macro processing, this line would become */
printf ("%s %s %d", strerror(errno), __FILE__, __LINE__);
like image 330
user138645 Avatar asked Mar 21 '13 14:03

user138645


People also ask

Is printf a macro?

In Julia, printf is a macro and not a function. Since a macro generates a custom code every time it's used, printf works the same way when running through a format instruction. At runtime, printf generates a customized code for each specific format specifier and then outputs the results as needed.

What is __ LINE __ in C++?

__LINE__ is a preprocessor macro that expands to current line number in the source file, as an integer. __LINE__ is useful when generating log statements, error messages intended for programmers, when throwing exceptions, or when writing debugging code.

What is the use of printf function?

The printf function (the name comes from “print formatted”) prints a string on the screen using a “format string” that includes the instructions to mix several strings and produce the final string to be printed on the screen.

How does printf know how many arguments?

The printf function uses its first argument to determine how many arguments will follow and of what types they are. If you don't use enough arguments or if they are of the wrong type than printf will get confuses, with as a result wrong answers.


3 Answers

With the caveat in my comment, you could do it using a variadic macro:

#define PRINTF_FL(format, ...) \
    printf(format " %s %d", __VA_ARGS__, __FILE__, __LINE__)
like image 68
Some programmer dude Avatar answered Nov 15 '22 07:11

Some programmer dude


Try this:

#define debug(fmt, ...) printf("%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__);

I used name debug instead of printf, because I don't think you should override standard functions. You could break something.

Use this like:

debug("This is debug no %d", 5);

To get output similar to:

program.c:12: this is debug no 5

(file: program.c, line: 12).

like image 36
kamituel Avatar answered Nov 15 '22 07:11

kamituel


And here is my "half penny".

Function name & debug messages:

// debug mode, -DEBUG
#ifdef EBUG
    #define FNAME() fprintf(stderr, "\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__)
    #define DBG(...) do{fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \
                    fprintf(stderr, __VA_ARGS__);           \
                    fprintf(stderr, "\n");} while(0)
#else
    #define FNAME()  do{}while(0)
    #define DBG(...) do{}while(0)
#endif //EBUG

Use macro FNAME() to show just name of function & file/line, DBG(text) to show printf-like debug message with info about function name & file/line.

And something that may be useful for coloured debug & error/warning messages.

In header file:

extern int globErr;
#define ERR(...) do{globErr=errno; _WARN(__VA_ARGS__); exit(-1);}while(0)
#define WARN(...) do{globErr=errno; _WARN(__VA_ARGS__);}while(0)
#define WARNX(...) do{globErr=0; _WARN(__VA_ARGS__);}while(0)
// functions for color output in tty & no-color in pipes
EXTERN int (*red)(const char *fmt, ...);
EXTERN int (*_WARN)(const char *fmt, ...);
EXTERN int (*green)(const char *fmt, ...);

In C file:

int globErr = 0; // errno for WARN/ERR
// pointers to coloured output printf
int (*red)(const char *fmt, ...);
int (*green)(const char *fmt, ...);
int (*_WARN)(const char *fmt, ...);
/*
 * format red / green messages
 * name: r_pr_, g_pr_
 * @param fmt ... - printf-like format
 * @return number of printed symbols
 */
int r_pr_(const char *fmt, ...){
    va_list ar; int i;
    printf(RED);
    va_start(ar, fmt);
    i = vprintf(fmt, ar);
    va_end(ar);
    printf(OLDCOLOR);
    return i;
}
int g_pr_(const char *fmt, ...){
    va_list ar; int i;
    printf(GREEN);
    va_start(ar, fmt);
    i = vprintf(fmt, ar);
    va_end(ar);
    printf(OLDCOLOR);
    return i;
}
/*
 * print red error/warning messages (if output is a tty)
 * @param fmt ... - printf-like format
 * @return number of printed symbols
 */
int r_WARN(const char *fmt, ...){
    va_list ar; int i = 1;
    fprintf(stderr, RED);
    va_start(ar, fmt);
    if(globErr){
        errno = globErr;
        vwarn(fmt, ar);
        errno = 0;
        globErr = 0;
    }else
        i = vfprintf(stderr, fmt, ar);
    va_end(ar);
    i++;
    fprintf(stderr, OLDCOLOR "\n");
    return i;
}

const char stars[] = "****************************************";
/*
 * notty variants of coloured printf
 * name: s_WARN, r_pr_notty
 * @param fmt ... - printf-like format
 * @return number of printed symbols
 */
int s_WARN(const char *fmt, ...){
    va_list ar; int i;
    i = fprintf(stderr, "\n%s\n", stars);
    va_start(ar, fmt);
    if(globErr){
        errno = globErr;
        vwarn(fmt, ar);
        errno = 0;
        globErr = 0;
    }else
        i = +vfprintf(stderr, fmt, ar);
    va_end(ar);
    i += fprintf(stderr, "\n%s\n", stars);
    i += fprintf(stderr, "\n");
    return i;
}
int r_pr_notty(const char *fmt, ...){
    va_list ar; int i;
    i = printf("\n%s\n", stars);
    va_start(ar, fmt);
    i += vprintf(fmt, ar);
    va_end(ar);
    i += printf("\n%s\n", stars);
    return i;
}

In main():

if(isatty(STDOUT_FILENO)){ // make color output in tty
    red = r_pr_; green = g_pr_;
}else{ // no colors in case of pipe
    red = r_pr_notty; green = printf;
}
if(isatty(STDERR_FILENO)) _WARN = r_WARN;
else _WARN = s_WARN;

After that you will be able to use coloured output in case running in terminal and non-coloured in case of pipe. Functions red and green are analogues of printf for coloured output. Function _WARN used in macros to show user message and string for errno: ERR for case of errors (ends with exit), WARN — analogue of ERR but without exit and WARNX to show messages without errno.

like image 31
Eddy_Em Avatar answered Nov 15 '22 06:11

Eddy_Em