# C-style macros with arbitrary number of parameters?

This topic is 4649 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

So, I've got some log function, call it "log()" or something, which looks like: void Logger::log(LogLevel lev, const char *format, ...); I'm trying to wrap a call to this into a macro... something like this: #define LOG_ERROR(err) Logger::log(Logger::LOG_ERROR, err) But I get a (reasonable) error that complains about that, since calling say LOG_ERROR("hardy %s har", "har"); will have more arguments, and the preprocessor doesn't like that... Is there a way to tell the preprocessor to just take WHATEVER 'err' is gonna be, and treat it as just some text, and not try to parse all the commas? The reason I want a macro is so I can build the LOG_DEBUG macro such that it's defined as 'empty' (i.e. nothing) when '_DEBUG' is not defined, so that debug log entries I'll have scattered all over won't be compiled into my release build. Does anyone know how to do this (or do it differently?) thanks!

##### Share on other sites
There isn't a way to do that, as far as I know (well, not one that would work in this case). Why are you using a macro and *printf-style, anyway? Just use an inline function and C++ streams:
// ...enum { ERROR_LOGGER, /* ... */ };template<int type>class Logger {};template<>class Logger<ERROR_LOGGER> : public std::ostream {public:   // All the overloaded << operator functions go here.};// ... other specializations// ... in some function ...Logger<ERROR_LOGGER> errorLogger;errorLogger << "<error>";
Of course, you don't need to use template specialization, that's just an example.

##### Share on other sites
It's not really possible. Some compilers have extensions that allow varargs-like macros, e.g. for GCC:
#define LOG(fmt, ...) fprintf(logstream, fmt, __VA_ARGS__)
...but it's not at all portable. You can cheat, and do things like:
#define LOG Logger::log
Which would cause
LOG(fmt, "some string", 10)
to end up being
Logger::log(fmt, "some string", 10)
after the preprocessor has had its way with the source. I don't recommend this solution, however.

##### Share on other sites
Actually, I use the method kinetik_mjg suggests you don't use...

// two defines one of which is formatted// notice the formatted one takes no arguments#define ALOG(txt) ALog::GetDefaultLog()->WriteLine(AL_EL_LOG, LOGSOURCE, txt)#define ALOGF ALog::GetDefaultLog()->WriteLine// WriteLine is this function:void WriteLine(ALogErrorLevel el, const char* source, const char* text, ...);

This works pretty well, I think [smile]. Simple and effective. I recomend you use that method - much better than compiler-specific methods.

##### Share on other sites
the C++ preprocessor is a simple machine and does not have a feature like that.
The parse break on ',' and consider what follows as another argument. You can use a trick like this in order to achieve your goal:
struct Logger{   typedef enum   {      log_error,      log_system,      log_warning   } LogType;   typedef struct   {      LogType Type;      std::string Message;   } LogEntry;   static std::list<LogEntry> LogTable;   static std::stringstream Out;}#DEFINE LOG_ERROR(Log) Logger::Out.clear();/Logger::Out.str("");/Logger::Out Log:/LogTable.push_back(Logger::log_error, Out.str());int main(){   int flarp;   LOG_ERROR(<< "Hi" << " how are you?" << flarp)}

or using boost::format

##### Share on other sites
Quote:
 Original post by kinetik_mjgIt's not really possible. Some compilers have extensions that allow varargs-like macros, e.g. for GCC: ...but it's not at all portable.

Variadic Macros are part of the C99 standard so they are portable on C99 compliant C compilers, for the time being they are only supported as compiler vendor language extensions in C++.

##### Share on other sites
I recently had the same problem (thread) and ended up with quite a nice solution:
// usage: mkstr(something << "something_else" << 123)#define mkstr(args) (static_cast<std::ostringstream&>( const_cast<std::ostream &>(static_cast< const std::ostream & >( std::ostringstream() )) << args )).str()

Then you can define a debug macro like this, for example:
#define DEBUG_LOG(args) std::cerr << mkstr(args) << std::endl;

##### Share on other sites
((std::ostringstream()) << args ).str()
you create a new temporary ostringstream everytime? I have to benchmark in order to see if your solution is quicker than mine. I think that it's better to take a central ostringstream and resetting it everytime new data comes. If I remember well in Carmack code there is something like the char* va(char*, ...) function that returns newly formatted string from an array of 3 buffer. I use a C++ style same solution.

##### Share on other sites
Good point bjogio. However, that would not work in a threaded program; you'd have to make a mechanism to get thread-specific objects.
OTOH how fast does error reporting really have to be?

##### Share on other sites
Quote:
 Original post by 255I recently had the same problem (thread) and ended up with quite a nice solution:#define mkstr(args) (static_cast( const_cast(static_cast< const std::ostream & >( std::ostringstream() )) << args )).str()

This is the first time i've come across that thread and you know all of those casts are unnecessary and pretty wrong, do you understand whats going on with that code? do you know why Sharlin's solution doesn't work, i can tell you if you want, in any case the correct code is just:

#define mkstr(args) static_cast<std::ostringstream&>(const_cast<std::ostringstream&>(std::ostringstream()) << args).str()

Note that ostringstream is type alias for std::basic_ostringstream, gives you something to think about [smile].

Quote:
 Original post by bjogio((std::ostringstream()) << args ).str()you create a new temporary ostringstream everytime? I have to benchmark in order to see if your solution is quicker than mine. I think that it's better to take a central ostringstream and resetting it everytime new data comes. If I remember well in Carmack code there is something like the char* va(char*, ...) function that returns newly formatted string from an array of 3 buffer. I use a C++ style same solution.

Quote:
 Original post by 255Good point bjogio. However, that would not work in a threaded program; you'd have to make a mechanism to get thread-specific objects.OTOH how fast does error reporting really have to be?

#include <boost/pool/pool_alloc.hpp>#include <sstream>typedef boost::fast_pool_allocator<char> char_alloc;typedef std::basic_ostringstream<char, std::char_traits<char>, char_alloc > fast_ostringstream;

Does that make you guys abit more happy now [smile]

##### Share on other sites
Quote:
 Original post by 255Good point bjogio. However, that would not work in a threaded program; you'd have to make a mechanism to get thread-specific objects.OTOH how fast does error reporting really have to be?

yes, you're right. My mind is bended in single threaded way. When I'm referring to speed I talk about outofscope-driven destruction of objects. The macro is for error but it can be used for every log function right? N called to logger in a function will result in N construction and destruction. Anyway it's a trivial speech, when something is about linear complexity nothing wrong.

##### Share on other sites
Hold on! I do agree that there are better ways of doing this but this is a neat little hack..

#define LOG_ERROR(params) {Logger::Log ##params ;}

And you use it like this:

LOG_ERROR( (Logger::LOG_ERROR , "hardy %s har", "har") )

You then migt consider doing the following:

Logger::LogError(char *format,...);

In order to simplify the macro to this:

#define LOG_ERROR(params) {Logger::LogError ##params ;}
LOG_ERROR( ("hardy %s har", "har") )

Edit:

This can also be expanded...

#define MACRO(x,y,z) {varargfunc1 ##x ; varargfunc2 ##y ; normalfunc( z );}
MACRO( ("blah %d",3) , ("mary %s","jane") , 57 )

##### Share on other sites
Simple with MSVC++:

Make Logger a singleton and do something like this:

Move the functionality of Logger::log to Logger::operator().

Then:

In debug:
#define LOG Logger::getInstance()

In release:
#define LOG __noop

Usage:

LOG("hardy %s har", "har");

That expands to Logger::getInstance()("hardy %s","har");, which is equivalent to
Logger::getInstance().operator()("hardy %s","har");

##### Share on other sites
Quote:
 Original post by snk_kidThis is the first time i've come across that thread and you know all of those casts are unnecessary and pretty wrong, do you understand whats going on with that code? do you know why Sharlin's solution doesn't work, i can tell you if you want, in any case the correct code is just:#define mkstr(args) static_cast(const_cast(std::ostringstream()) << args).str()

GCC isn't satisfied with that:
error: invalid const_cast of an rvalue of type    std::ostringstream' to type std::ostringstream&'

It needs a static_cast<const std::ostringstream &> around the constructor like so:
#define mkstr(args) static_cast<std::ostringstream&>( const_cast<std::ostringstream&>(static_cast<const std::ostringstream &>(std::ostringstream())) << args ).str()

and it works.
It's still cleaner than the original with the redundand upcast. Thanks for bringing this up again. Looks like back then I just settled for the first thing that worked.[smile]

What I'd still like to know is why doesn't this work (it prints the address of the first string constant, as discussed in the other thread):
std::cout << (static_cast<std::ostringstream&>(std::ostringstream() << "A" << 4)).str() << std::endl;

##### Share on other sites
Sweet, thanks for all the awesome advice!