0

I'm trying to create a log helper, and want to concatenate the line number to the log info.

I want to accomplish a Log like this: (" -l:" + LINE + args)

05-22 15:04:16.626 21270-22699/com.mydomain.myproject D/myfile.c: -l:5 myloginfo ...

This is what I have in my loghelper.h file:

#ifdef __ANDROID__
#include <android/log.h>
#define FILETAG (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1  : __FILE__)
#define LINEIDENTIFIER ' -l:'
#define TOKENAPPEND(x, y, z) x##y##z
#define APPENDLINE(x, y, z) TOKENAPPEND(x, y, z)
#define printf(fmt,args...)  __android_log_print(ANDROID_LOG_DEBUG, FILETAG , fmt, APPENDLINE(LINEIDENTIFIER, __LINE__, ##args))
#define printfe(fmt,args...)  __android_log_print(ANDROID_LOG_ERROR, FILETAG, fmt, APPENDLINE(LINEIDENTIFIER, __LINE__, ##args))
#define printfw(fmt,args...)  __android_log_print(ANDROID_LOG_WARN, FILETAG, fmt, APPENDLINE(LINEIDENTIFIER, __LINE__, ##args))
#endif

calling from myfile.c like this:

#include "loghelper.h"

...

printf(" myloginfo %s", "...");

I'm getting the following error:

error: pasting formed '' -l:'52', an invalid preprocessing token
error: expected ')'
error: too many arguments provided to function-like macro invocation
error: use of undeclared identifier 'APPENDLINE'
error: pasting formed '57"success!"', an invalid preprocessing token
error: expected ')'

Q: What am I doing wrong? What should I change to make it work?

alexm
  • 1,285
  • 18
  • 36
  • After fixing the spaces before the macro parameter lists, you should also [double-stringize](https://stackoverflow.com/questions/2751870/how-exactly-does-the-double-stringize-trick-work) `__LINE__` to get a string literal that you can concatenate – M Oehm May 22 '17 at 18:39
  • @lurker you are right that is one of the problems, I will update de question with that fixed and the new error I'm getting – alexm May 22 '17 at 18:43
  • @M Oehm I didn't grasp what you meant by double-stringize, isn't that what I'm doing with the APPENDLINE and TOKENAPPEND macros? Would you please explain to me again? – alexm May 22 '17 at 18:46
  • No. That's token pasting, and it won't help you here. I'll write an answer. – M Oehm May 22 '17 at 18:46
  • Ok great, I'm just learning macros in c and if you can explain will be of much help – alexm May 22 '17 at 18:51

2 Answers2

2

You try to use the token pasting operator ## where you can't use it. Token pasting can be used to create new tokens, usually new identifiers:

#define MY(X) my_##X     // MY(func) -> my_func

Here, you want to concatenate string literals. There are two things to note here:

  • You don't need the preprocessor to concatenate string literals. The C syntax will join adjacent string literals: "to" "gether" will be treated like "together".
  • The __LINE__ macro expands to an integer, so you must turn it into a string literal. The stringizing operator # will not work on the special token __LINE__, so you must write a macro to stringize. If you do that, you get the string literal "__LINE__", which isn't very useful. Use double stringizing, i.e. add one more level of indirection, to get the actual line mumber as string.

Putting all this into practice, a simple logging macro could look like this:

#define M_STR_(x) #x
#define M_STR(x) M_STR_(x)

#define LOG(...) fprintf(stderr, "Line " M_STR(__LINE__) ": " __VA_ARGS__)

I've used a shorter example here for te sake of simplicity. Things to note are:

  • I've used the standard ... and __VA_ARGS__ notation here and I haven't split up the format string and the arguments in the macro parameters list. That way, the macro will work without printing arguments and you don't need the non-standard ##__VA_ARGS__ to suppress the lone comma.
  • This macro will only work when the format string is a string literal, as it ought to be. The Format string "%s" will be expanded to "Line " "12" ": " "%s".
  • Prepending string literals to the format string is easy, but it is more complicated to append to it, for example if you want to end the format string with a new line. You could use your approach to separate fmt from the args, but it might be easier to stick to the simple approach and have the macro expand to two print statements: One for the formatted string and another one for the new-line. (But make sure to wrap it, so that these two commands cannot be separated.)

Finally, the method applied to your printf repclacement:

#define printf(...)                                       \
    __android_log_print(ANDROID_LOG_DEBUG, FILETAG,       \
    "-l " M_STR(__LINE__) ": " __VA_ARGS__)
M Oehm
  • 28,726
  • 3
  • 31
  • 42
0

Here is the final working code based on @M Oehm's answer, for if you are wondering how it ended up.

#ifdef __ANDROID__
#include <android/log.h>
#define FILETAG (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1  : __FILE__)
#define GETLINE_(x) #x
#define GETLINE(x) GETLINE_(x)
#define printf(...)  __android_log_print(ANDROID_LOG_DEBUG, FILETAG , "-l" GETLINE(__LINE__) ": " __VA_ARGS__)
#define printfe(...)  __android_log_print(ANDROID_LOG_ERROR, FILETAG,  "-l" GETLINE(__LINE__) ": " __VA_ARGS__)
#define printfw(...)  __android_log_print(ANDROID_LOG_WARN, FILETAG, "-l" GETLINE(__LINE__) ": " __VA_ARGS__)
#endif
alexm
  • 1,285
  • 18
  • 36