0

I am trying to print logs into a file by writing macros. My macro looks like as shown below:

#define LOG(fmt,...){\
    FILE *F;\
    F = fopen("output.txt","a");\
    fprintf(F,fmt " %s %d",__VA_ARGS__,__FILE__,__LINE__);}

And I plan to call LOG in the following format:

LOG("values are : %d %d",num1,num2);

But when I compile I get the error

error: expected expression before ‘,’ token
     fprintf(F,fmt " %s %d",__VA_ARGS__,__FILE__,__LINE__);}

Can someone please explain where I am going wrong?

a3f
  • 8,517
  • 1
  • 41
  • 46
user1692342
  • 5,007
  • 11
  • 69
  • 128
  • 1
    You know `__FILE__` is a `char[]`, not a `int`, right? And unrelated to your problem, I sincerely hope there is an `fclose()` somewhere in the **real** code, as otherwise you're leaking stream pointers like a sieve leaks rain water. – WhozCraig Oct 10 '14 at 17:45
  • Check out this question. Works both in C and C++. http://stackoverflow.com/questions/1056411/how-to-pass-variable-number-of-arguments-to-printf-sprintf – Niklas Hansson Oct 10 '14 at 17:47
  • @WhozCraig Yeah sorry for the mistake there. There is fclose in the main program. In the hurry of copy paste missed it :-) – user1692342 Oct 10 '14 at 17:49
  • @NiklasHansson Thanks for the link. I shall check it out :) – user1692342 Oct 10 '14 at 17:50
  • I know this is potentially a dumb question, but I gotta ask. your toolchain supports variadic macros, right? – WhozCraig Oct 10 '14 at 17:51
  • You probably can't afford to leak open file streams at the rate that code would do so. You should check that the `fopen()` worked and close the file before you finish. Or, more sensibly, you should call a function which handles stuff like caching the open file stream and manages the formatting more simply. – Jonathan Leffler Oct 10 '14 at 18:26
  • See also [C `#define` macro for debug printing](http://stackoverflow.com/questions/1644868/c-define-macro-for-debug-printing/1644898#1644898). – Jonathan Leffler Oct 10 '14 at 18:33

2 Answers2

3
#define LOG(fmt,...){\
    FILE *F;\
    F = fopen("output.txt","a");\
    fprintf(F,fmt " %d %d",__VA_ARGS__,__FILE__,__LINE__);}

Multiple issues

  1. You never fclose F.
  2. __FILE__ is a string.
  3. If you want to be able to call it without parameters __VA_ARGS__ must go at the end...
  4. ... or use this little hack:

    #define LOG(fmt,...){\
        FILE *F;\
        F = fopen("output.txt","a");\
        fprintf(F,fmt " %s %d", ##__VA_ARGS__, __FILE__,__LINE__);}
    

(##__VA_ARGS__ is a GCC extension that removes the preceding comma if there are no args).

Also check out this answer about "custom" printf-like functions.

Community
  • 1
  • 1
dom0
  • 7,356
  • 3
  • 28
  • 50
  • 1
    I tried your code: THe following error occurs: error: expected expression before ‘,’ token #define VA_ARG(...) , ##__VA_ARGS__ note: in expansion of macro ‘VA_ARG’ fprintf(F,fmt " %s %d", VA_ARG(__VA_ARGS__),__FILE__,__LINE__);} – user1692342 Oct 10 '14 at 17:57
  • Oh yeah, I forgot. That's an GCC extension. My bad. Also there was another mistake in the code I posted, fixed now. – dom0 Oct 10 '14 at 18:06
  • It works If i have arguments passed :) If there are no arguments then junk values are printed :( – user1692342 Oct 10 '14 at 18:15
3

First of all you have to wrap you macro into a do-while loop, so it will be handled with expressions correctly.

#define LOG( fmt , ... ) do{  }while(0)

Then you have to make sure that fopen() call succeeds and that you close the file after usage.

FILE* f = fopen( "output.txt" , "a" ) ;
if( !f )
    break ;    //break works because you are in a loop
fclose( f ) ;    //also flushes the stream

Then you include the print in the full macro.

#define LOG( fmt , ... )    \
        do{ \
            FILE* f = fopen( "output.txt" , "a" ) ; \
            if( !f )    \
                break ; \
            fprintf(f, fmt" %s %d\n",__VA_ARGS__,__FILE__,__LINE__);    \
            fclose( f ) ;   \
        }while( 0 )

The call is in the form:

LOG("values are : %d %d",4444,55555);

where you have to input at least one correct optional parameter, with the corresponding flag in the string.

2501
  • 25,460
  • 4
  • 47
  • 87
  • 1
    I replaced __VA_ARGS__ with ##__VA_ARGS__ and it started working. Thanks :) I didnt understand why a loop is required. What happens when the loop isnt there? – user1692342 Oct 10 '14 at 18:23
  • @user1692342 You code was already correct( with some problems ), ##__VA_ARGS__ is a compiler extension. – 2501 Oct 10 '14 at 18:24
  • 1
    @user1692342 The macro might not get handled correctly. Try a couple examples with and without do-while using if statements and you will see that the code will be broken in certain cases. – 2501 Oct 10 '14 at 18:25
  • "##__VA_ARGS__ is a compiler extension." -- wow, good to know. I thought preprocessor behavior was more or less universal, and the environment probing was mostly there to enable platform-agnostic code for the compiler. When you say it's a compiler extension, do you mean that a preprocessed file containing `##a b c` becomes `a` during compilation, or were you using 'compiler' colloquially to mean the whole pipeline? – John P Sep 11 '17 at 01:50