Jump to content
  • Advertisement
Sign in to follow this  
Replicon

C-style macros with arbitrary number of parameters?

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

If you intended to correct an error in the post then please contact us.

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 this post


Link to post
Share on other sites
Advertisement
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by kinetik_mjg
It'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 this post


Link to post
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;

This approach also has the advantage of not requiring variadic macros.

Share this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by 255
I recently had the same problem (thread) and ended up with quite a nice solution:

#define mkstr(args) (static_cast<std::ostringstream&>( const_cast<std::ostream &>(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 255
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?



#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 this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!