21

I was looking to implement an api like printf for my logging. It should be similar to calling printf. For example:

persistent_log(LogType0, "This is buffered writing %d", i);

I looked into variable argument stuff , but it seems I need to know the number & type of arguments there. so I need more help in that regard.

Mark B
  • 95,107
  • 10
  • 109
  • 188
vindyz
  • 1,079
  • 2
  • 11
  • 23
  • Do you want to use C++0x or C style va_args? – Flexo Aug 11 '11 at 18:43
  • @awoodland , C style va_args – vindyz Aug 11 '11 at 18:47
  • http://cboard.cprogramming.com/c-programming/87400-make-function-like-printf-sprintf.html – ccozad Aug 11 '11 at 18:48
  • Note that you won't be able to check types in compile time if you use `va_args` approach. If you're OK with C++, use Boost.Format or roll up a solution on templates. Otherwise just accept the lack of type safety. – Kos Aug 11 '11 at 18:52
  • You know the number and type of your arguments, you just have to examine your format string. You should also look at vsprintf, vfprintf and similar functions. You might be able to use vfprintf in you persistent_log function. It would save you a lot of work if you can. – john Aug 11 '11 at 19:00
  • 3
    Since you want to use va_args I'm removing the C++ tag. – Mark B Aug 11 '11 at 19:02
  • First of all, it looks like you are trying to re-invent the sprintf wheel. Apart from that, I would strongly advise against using va_args. This is one of the most superfluous things in the C language. It simply doesn't make sense to use them in most (any?) kinds of applications. They are however quite ineffective and _very_ unsafe and bug-prone. In my experience, the need to use variable argument lists often originates from muddy thinking and bad program design. – Lundin Aug 11 '11 at 19:36
  • The [C FAQ](http://c-faq.com/) has some examples in section 15. – pmg Aug 11 '11 at 20:14

3 Answers3

26

Here's an excerpt from a past project that I found to work well for me. Some initialization steps are of course missing. The key here is the vfprintf function which will handle the details of printing the various arguments.

void _proxy_log(log_level_t level, const char *fmt, ...)
    __attribute__((format (printf, 2, 3)));

#define proxy_log(level, fmt, ...) _proxy_log(level, fmt"\n", ##__VA_ARGS__)

void _proxy_log(log_level_t level, const char *fmt, ...) {
    va_list arg;
    FILE *log_file = (level == LOG_ERROR) ? err_log : info_log;

    /* Check if the message should be logged */
    if (level > log_level)
        return;

    /* Write the error message */
    va_start(arg, fmt);
    vfprintf(log_file, fmt, arg);
    va_end(arg);

#ifdef DEBUG
    fflush(log_file);
    fsync(fileno(log_file));
#endif
}
Michael Mior
  • 28,107
  • 9
  • 89
  • 113
  • 3
    +1. Not too many people are aware of GCC's `__attribute__((format(...)))` facility to add compile-time type-checking. – Adam Rosenfield Aug 11 '11 at 19:08
  • Good point. Here's a link to [the documentation](http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#index-g_t_0040code_007bformat_007d-function-attribute-2429) – Michael Mior Aug 16 '11 at 02:39
  • 2
    To help future travelers: the first line, from 'void _prox...' to '2,3)));' is all the function declaration and goes into the *.h. The #define can pretty much go anywhere after the function is declared, and the implementation is the last bit. Maybe it's obvious, but I was confused for a while! – Hamy Nov 10 '12 at 08:28
  • @Hamy The `#define` is not a *function* declaration, but it's true that those lines should go in the header. – Michael Mior Jul 23 '18 at 16:53
4

Using "va_arg" http://www.cplusplus.com/reference/clibrary/cstdarg/va_arg/

Rodrigo
  • 135
  • 4
  • 45
  • 107
  • but I willneed to knwo the type before hand for this – vindyz Aug 11 '11 at 18:45
  • 1
    You learn that from inspecting the format string yourself (if you really want to have fun). Alternately, and this is easier, just dispatch to vfprintf when you've pulled your custom arguments out. – ChrisV Aug 11 '11 at 18:48
1

This is an old question, but here is my simplified base solution that exactly mimics printf and should be expandable to other more specific uses, it's basically a generalized version of Michael Mior's answer:

#include <stdarg.h>

void print(const char* fmt, ...)
{
    va_list arg;
    va_start(arg, fmt);

    vprintf(fmt, arg); 
    //vprintf can be replaced with vsprintf (for sprintf behavior) 
    //or any other printf function preceded by a v

    va_end(arg);
}

(This seems to work in c++ as well)

Nathan
  • 118
  • 4