pouët.net

Go to bottom

When C gets ugly. Or not. X-Macros.

category: general [glöplog]
 
Hi guys. I'm trying to be clever here (bad thing, I know).

I have a logging class in my project. I want to have function calls that automatically include the __FILE__ and __LINE__ macros without the user specifying them. I would use macros even with variable arguments to accomplish this:

#define logError(...) LoggingClass::realLogError(__FILE__, __LINE__, __VA_ARGS__)

(this is actually C99)

This works ok, but I have 2 versions of realLogError() with different string types. Say, one is "const char * string, ..." (2 arguments, 1 variable) and the other is an already concatenated string "const whateverstring" (1 argument). How do I make THE SAME macro call one of the two functions depending on the type? It should probably work with X-Macros, but I can't wrap my head around that shit. Anybody used them and can help me?
added on the 2009-01-20 12:18:24 by raer raer
Code:#define object_def \ struct_member( x, int ); \ struct_member( y, int ); \ struct_member( z, int ); \ struct_member( radius, double ); void print_star( const star *_star ) { /* print_##type will be replaced with print_int or print_double */ #define struct_member( name, type ) printf( "%s: ", #name ); print_##type( _star->name ); printf("\n"); object_def #undef struct_member }


How I understand the example is that print_star issues a "print_<type>(<argument>)" call based on the type of argument.

added on the 2009-01-20 13:02:35 by raer raer
"Hi, it looks like you're trying to shoot yourself to a foot.."

You don't :) Use a different macro. If you're trying to do something complex that relies on both macros having the same name, consult clippy or do real varargs.

Stupid nit, that double-colon-stuff is definitely not C99.
added on the 2009-01-20 13:43:32 by 216 216
quote from mfc:
Code: // The following trace macros are provided for backward compatiblity // (they also take a fixed number of parameters which provides // some amount of extra error checking) #define TRACE0(sz) ::AfxTrace(_T("%s"), _T(sz)) #define TRACE1(sz, p1) ::AfxTrace(_T(sz), p1) #define TRACE2(sz, p1, p2) ::AfxTrace(_T(sz), p1, p2) #define TRACE3(sz, p1, p2, p3) ::AfxTrace(_T(sz), p1, p2, p3)
added on the 2009-01-20 13:49:45 by Gargaj Gargaj
With C99 I meant the varargs in macros. But thanks anyway.
added on the 2009-01-20 13:51:08 by raer raer
i guess i do not understand your problem, but doesn't c++ do this for you? i mean, if realLogError is overloaded with different argument types, and you just pass them on in the macro, what's the problem?

that said, considering you're using c++ anyway, why not just only accept an std::string as an argument? they're easily concatenated using the + operator, anyway.

i guess i'd even use an outputstream for it, such as

#define LOG_STREAM LoggingClass::createLogLine(__FILE__, __LINE__)
LOG_STREAM << "foo" << "bar";

where createLogLine nicely streams the file and line information, maybe after outputting a newline, and then returns a reference to some std::ostringstream. or something. just some ideas, probably not applicable at all.
added on the 2009-01-20 13:51:33 by skrebbel skrebbel
Uhm, yeah. I know, but TRACE is teh cancer and not wat I want...
added on the 2009-01-20 13:52:35 by raer raer
oh, the advantage being, of course, that numbers and stuff are easily streamed.
added on the 2009-01-20 13:53:31 by skrebbel skrebbel
I'm working with log levels here and enabling log messages selectively through file names and tag-like switches. I could probably do that with streams too, you might be right...

Well. Actually I solved the problem. I was too stupid and the solution was already what I wrote in the first post, as skrebbel pointed out. I just screwed up. Sorry...
added on the 2009-01-20 14:03:10 by raer raer
hah :-)

a propos streaming, the only problem with streams if that if you'd want to turn off logging for performance by making an empty define in release builds, or something, you're kind of screwed because the arguments will always be evaluated.

i've seen LOG_STREAM( << "foo" << "bar") and stuff like that, but it gets a lot uglier that way. i'd be very interested to hear about other people's takes on logging.
added on the 2009-01-20 14:07:23 by skrebbel skrebbel
Here logging is controled by loglevels (e.g. DEBUG, WARNING etc.) and you can override/enable logging for a specific file. This can all happen at runtime so that you can switch on all output even in release builds. To not hurt performance much, the loglevel is checked before the actual call is made, which save you passing the arguments. The code size grows though.
added on the 2009-01-20 14:14:26 by raer raer
The check must be pretty cheap obviously, but the class user doesn't have to bother with it. asserts also work in release mode the same way, which is a nice thing too imo.
added on the 2009-01-20 14:21:58 by raer raer

login

Go to top