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?

7
  • 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
    Commented Oct 10, 2014 at 17:45
  • Check out this question. Works both in C and C++. stackoverflow.com/questions/1056411/… Commented Oct 10, 2014 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 :-) Commented Oct 10, 2014 at 17:49
  • @NiklasHansson Thanks for the link. I shall check it out :) Commented Oct 10, 2014 at 17:50
  • I know this is potentially a dumb question, but I gotta ask. your toolchain supports variadic macros, right?
    – WhozCraig
    Commented Oct 10, 2014 at 17:51

2 Answers 2

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.

4
  • 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? Commented Oct 10, 2014 at 18:23
  • @user1692342 You code was already correct( with some problems ), ##__VA_ARGS__ is a compiler extension.
    – 2501
    Commented Oct 10, 2014 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
    Commented Oct 10, 2014 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
    Commented Sep 11, 2017 at 1:50
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.

3
  • 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);} Commented Oct 10, 2014 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
    Commented Oct 10, 2014 at 18:06
  • It works If i have arguments passed :) If there are no arguments then junk values are printed :( Commented Oct 10, 2014 at 18:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.