What (if any) are some potential problems with a C++ macro usage like this?
Would an inline function be a more appropriate solution?
#define EVENT_INFO(_format_, ...) CMyEvent::Generate(__FILE__, __LINE__, CMyEvent::EVT_HIGH, _format_, __VA_ARGS__)
void
CMyEvent::Generate(
const char* file, // filename
int line, // line number
CMyEvent::LEVEL level, // severity level
const char *format, // format of the msg / data
...) // variable arguments
{
// Get a message from the pool
CMyEvent* p_msg = GetMessageFromPool();
if(p_msg != NULL)
{
va_list arguments; // points to each unnamed argument
va_start(arguments, format);
// Fill the object with strings and data.
p_msg->Fill(file, line, level, 0, format, arguments);
va_end(arguments);
}
}
As you're using C++, you can avoid the pitfalls of using variable argument lists which are subject to many problems:
No check on quantity of arguments
No check on argument type
To make it more C++, do something like:
#define EVENT_INFO(args) EventLogStream (__FILE__, __LINE__, __PRETTY_FUNCTION__) << args
and invoke it like (warning: all code here is pseudocode and may be syntactically incorrect, but you should get the basic idea):
EVENT_INFO ("The answer to " << the_question << " is " << answer); // usually 42
The EventLogStream is similar to the cout object and like cout, you can provide class specific output:
class Vector3D
{
EventLogStream &operator << (EventLogStream &out) { out << "{" << x << ", " << y << ", " << z << "}"; }
}
If you could rewrite your messaging to use operator<<(), perhaps using std::ostreams with a custom stream buffer, you wouldn't need variable arguments or the ugly (IMHO) macro, and it would look a lot more like C++ than C.
variadic macro is great for use on logging. much better than using iostreams. inline function won't work unless you are willing to pass __FILE__, __LINE__ manually.
You can enclose format with parentheses for safety (not necessary IMHO)
Related
I'd like to implement a macro which does the following:
#define report(s) print(), throw std::runtime_error(s)
print() is a function that I always call to print some predefined stuff. s need to support:
report("abc"); // ok
report("abc"<<100); // == report("abc100")
Apart from whatever print() outputs, Nothing else should be printed. The exception will be caught by the caller and printed there.
I find it difficult to support << in the above macro.
P.S. report() is a macro already used every where in my code base and I just want to change its behaviour. Calls like report("abc"<<100); have to be supported. define it as a function and add ';' at the end doesn't look appropriate.
Perhaps the following (untested!) code might be inspirational
#define report(Log) do { std::ostringstream _os; \
_os << Log << std::flush; print(_os.str()); \
throw std::runtime(_os.str()); } while(0)
and you might use it as report("x=" << x);
BTW, you might even pass the source location using
#define report_at(Log,Fil,Lin) do { std::ostringstream _os; \
_os << Fil << ":" << Lin << ": " << Log << std::flush; \
print(_os.str()); \
throw std::runtime(_os.str()); } while(0)
(to lower probability of collision with _os you might even replace all its occurrences inside the brace with _os##Lin using preprocessor concatenation)
#define report_at_bis(Log,Fil,Lin) report_at(Log,Fil,Lin)
#define report(Log) report_at_bis(Log,__FILE__,__LINE__)
and this shows one of the cases where a macro is really useful.
I just encountered a DEBUG macro in C that I really like
#ifdef DEBUG_BUILD
# define DEBUG(x) fprintf(stderr, x)
#else
# define DEBUG(x) do {} while (0)
#endif
I'm guessing a C++ analogue would be :-
#ifdef DEBUG_BUILD
# define DEBUG(x) cerr << x
#else
# define DEBUG(x) do {} while (0)
#endif
Is the second code snippet analogous to the one in C?
Do you have any favorite C++ debug macros?
EDIT :
By "Debug Macros" I mean "macros that might come in handy while running a program in debug mode".
Is the second code snippet analogous to the one in C?
More or less. It's is more powerful, as you can include <<-separated values in the argument, so with a single argument you get something that would require a variable number of macro arguments in C. On the other hand, there is a slim chance that people will abuse it by including a semicolon in the argument. Or even encounter mistakes due to a forgotten semicolon after the call. So I'd include this in a do block:
#define DEBUG(x) do { std::cerr << x; } while (0)
Do you have any favourite C++ debug macros?
I like the one above and use it quite often. My no-op usually just reads
#define DEBUG(x)
which has the same effect for optimizing compilers. Although the comment by #Tony D below is correct: this can leave some syntax errors undetected.
I sometimes include a run-time check as well, thus providing some form of a debug flag. As #Tony D reminded me, having an endl in there is often useful as well.
#define DEBUG(x) do { \
if (debugging_enabled) { std::cerr << x << std::endl; } \
} while (0)
Sometimes I also want to print the expression:
#define DEBUG2(x) do { std::cerr << #x << ": " << x << std::endl; } while (0)
In some macros, I like to include __FILE__, __LINE__ or __func__, but these are more often assertions and not simple debug macros.
Here's my favorite
#ifdef DEBUG
#define D(x) x
#else
#define D(x)
#endif
It's super handy and makes for clean (and importantly, fast in release mode!!) code.
Lots of #ifdef DEBUG_BUILD blocks all over the place (to filter out debug related blocks of code) is pretty ugly, but not so bad when you wrap a few lines with a D().
How to use:
D(cerr << "oopsie";)
If that's still too ugly/weird/long for you,
#ifdef DEBUG
#define DEBUG_STDERR(x) (std::cerr << (x))
#define DEBUG_STDOUT(x) (std::cout << (x))
//... etc
#else
#define DEBUG_STDERR(x)
#define DEBUG_STDOUT(x)
//... etc
#endif
(I suggest not using using namespace std; though maybe using std::cout; using std::cerr; could be a good idea)
Note that you might want to do more things than just print to stderr when you are thinking about "debugging". Get creative, and you can build constructs that offer insight into the most complex interactions within your program, while allowing you to very quickly switch to building a super efficient version unencumbered by debug instrumentation.
For example in one of my recent projects I had a huge debug-only block which started with FILE* file = fopen("debug_graph.dot"); and proceeded to dump out a graphviz compatible graph in dot format to visualize large trees within my datastructures. What's even cooler is the OS X graphviz client will auto-read the file from disk when it changes, so the graph refreshes whenever the program is run!
I also particularly like to "extend" classes/structs with debug-only members and functions.
This opens up the possibility of implementing functionality and state that is there to help you track down bugs, and just like everything else that is wrapped in debug macros, is removed by switching a build parameter. A giant routine that painstakingly checks each corner case on every state update? Not a problem. Slap a D() around it. Once you see it works, remove -DDEBUG from the build script, i.e. build for release, and it's gone, ready to be re-enabled at a moment's notice for your unit-testing or what have you.
A large, somewhat complete example, to illustrate (a perhaps somewhat overzealous) use of this concept:
#ifdef DEBUG
# define D(x) x
#else
# define D(x)
#endif // DEBUG
#ifdef UNITTEST
# include <UnitTest++/UnitTest++.h>
# define U(x) x // same concept as D(x) macro.
# define N(x)
#else
# define U(x)
# define N(x) x // N(x) macro performs the opposite of U(x)
#endif
struct Component; // fwd decls
typedef std::list<Component> compList;
// represents a node in the graph. Components group GNs
// into manageable chunks (which turn into matrices which is why we want
// graph component partitioning: to minimize matrix size)
struct GraphNode {
U(Component* comp;) // this guy only exists in unit test build
std::vector<int> adj; // neighbor list: These are indices
// into the node_list buffer (used to be GN*)
uint64_t h_i; // heap index value
U(int helper;) // dangling variable for search algo to use (comp node idx)
// todo: use a more space-efficient neighbor container?
U(GraphNode(uint64_t i, Component* c, int first_edge):)
N(GraphNode(uint64_t i, int first_edge):)
h_i(i) {
U(comp = c;)
U(helper = -1;)
adj.push_back(first_edge);
}
U(GraphNode(uint64_t i, Component* c):)
N(GraphNode(uint64_t i):)
h_i(i)
{
U(comp=c;)
U(helper=-1;)
}
inline void add(int n) {
adj.push_back(n);
}
};
// A component is a ugraph component which represents a set of rows that
// can potentially be assembled into one wall.
struct Component {
#ifdef UNITTEST // is an actual real struct only when testing
int one_node; // any node! idx in node_list (used to be GN*)
Component* actual_component;
compList::iterator graph_components_iterator_for_myself; // must be init'd
// actual component refers to how merging causes a tree of comps to be
// made. This allows the determination of which component a particular
// given node belongs to a log-time operation rather than a linear one.
D(int count;) // how many nodes I (should) have
Component(): one_node(-1), actual_component(NULL) {
D(count = 0;)
}
#endif
};
#ifdef DEBUG
// a global pointer to the node list that makes it a little
// easier to reference it
std::vector<GraphNode> *node_list_ptr;
# ifdef UNITTEST
std::ostream& operator<<(std::ostream& os, const Component& c) {
os << "<s=" << c.count << ": 1_n=" << node_list_ptr->at(c.one_node).h_i;
if (c.actual_component) {
os << " ref=[" << *c.actual_component << "]";
}
os << ">";
return os;
}
# endif
#endif
Notice that for large blocks of code, I just use regular block #ifdef conditionals because that improves readability somewhat, as for large blocks the use of extremely short macros is more of a hindrance!
The reason why the N(x) macro must exist is to specify what to add when unit-testing is disabled.
In this part:
U(GraphNode(uint64_t i, Component* c, int first_edge):)
N(GraphNode(uint64_t i, int first_edge):)
It would be nice if we could say something like
GraphNode(uint64_t i, U(Component* c,) int first_edge):
But we cannot, because the comma is a part of preprocessor syntax. Omitting the comma produces invalid C++ syntax.
If you had some additional code for when not compiling for debug, you could use this type of corresponding inverse-debug macro.
Now this code might not be an example of "really good code" but it illustrates some of the things that you can accomplish with clever application of macros, which if you remain disciplined about, are not necessarily evil.
I came across this gem just now after wondering about the do{} while(0) stuff, and you really do want all that fanciness in these macros as well!
Hopefully my example can provide some insight into at least some of the clever things that can be done to improve your C++ code. It is really valuable to instrument code while you write it rather than to come back to do it when you don't understand what's happening. But it is always a balance that you must strike between making it robust and getting it done on time.
I like to think of additional debug build sanity checks as a different tool in the toolbox, similar to unit tests. In my opinion, they could be even more powerful, because rather than putting your sanity check logic in unit tests and isolating them from the implementation, if they are included in the implementation and can be conjured at will, then complete tests are not as necessary because you can simply enable the checks and run things as usual, in a pinch.
For question 1] Answer is yes. It will just print the message to standard error stream.
For question 2] There are many. My Fav is
#define LOG_ERR(...) fprintf(stderr, __VA_ARGS__)
which will allow one to include arbitrary number of variables to include in the debug message.
I like to use macros with __LINE__, __FILE__ as arguments to show where in the code the printout is from - it's not uncommon to print the same variable name in several places, so fprintf(stderr, "x=%d", x); won't mean much if you then add another one the same ten lines further down.
I've also used macros that override certain functions and store where it was called from (e.g. memory allocations), so that later on, I can figure out which one it was that leaked. For memory allocation, that's a little harder in C++, since you tend to use new/delete, and they can't easily be replaced, but other resources such as lock/unlock operations can be very useful to trace this way [of course, if you have a locking wrapper that uses construction/destruction like a good C++ programmer, you'd add it to the constructor to add file/line to the internal structure once you have acquired the lock, and you can see where it's held elsewhere when the you can't acquire it somewhere].
This is the log macro I am using currently:
#ifndef DEBUG
#define DEBUG 1 // set debug mode
#endif
#if DEBUG
#define log(...) {\
char str[100];\
sprintf(str, __VA_ARGS__);\
std::cout << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << str << std::endl;\
}
#else
#define log(...)
#endif
Usage:
log(">>> test...");
Output:
xxxx/proj.ios_mac/Classes/IntroScene.cpp][gotoNextScene][Line 58] >>> test...
… and as addendum to all responses:
Personally I never use macros like DEBUG to distinct debug from release code, instead I use NDEBUG which is must be defined for release builds to eliminate assert() calls (yes, I use assert() extensively). And if latter is not defined, then it is a debug build. Easy! So, actually there is no reason to introduce one more debug macro! (and handle possible cases when DEBUG and NDEBUG both are not defined).
This is my version, using a variadic template print function:
template<typename... ArgTypes>
inline void print(ArgTypes... args)
{
// trick to expand variadic argument pack without recursion
using expand_variadic_pack = int[];
// first zero is to prevent empty braced-init-list
// void() is to prevent overloaded operator, messing things up
// trick is to use the side effect of list-initializer to call a function
// on every argument.
// (void) is to suppress "statement has no effect" warnings
(void)expand_variadic_pack{0, ((cout << args), void(), 0)... };
}
#ifndef MYDEBUG
#define debug_print(...)
#else
#define debug_print(...) print(__VA_ARGS__)
#endif
The version I makes the debug_print a variadic template function which accepts a debug level which allows me to select what kind of output I want to output at runtime:
template<typename... ArgTypes>
inline void debug_print(debug::debug level, ArgTypes... args)
{
if(0 != (debug::level & level))
print(args...);
}
Note the print function crashes Visual Studio 2013 Preview (I haven't tested the RC). I have noticed it is faster (on Windows, where console output is slow) than my previous solution which used an ostream child class that overloaded operator<<.
You can also use a temporary stringstream inside print if you only want to call the real output function once (or write your own typesafe printf ;-))
I use the code below for logging. There are a few advantages:
I can turn them on/off at runtime.
I can compile out statements at a particular log level. For example, at the moment, I've unconditionally compiled in the KIMI_PRIVATE macro because I'm debugging something in the release build but since there is a lot of potentially secret sauce stuff being logged (lol), I compile it out of release builds.
This pattern has served me very well over the years. Note: although there is a global logMessage function, the code usually queues the log to a logging thread.
#define KIMI_LOG_INTERNAL(level,EXPR) \
if(kimi::Logger::loggingEnabled(level)) \
{ \
std::ostringstream os; \
os << EXPR; \
kimi::Logger::logMessage(level ,os.str()); \
} \
else (void) 0
#define KIMI_LOG(THELEVEL,EXPR) \
KIMI_LOG_INTERNAL(kimi::Logger::LEVEL_ ## THELEVEL,EXPR)
#define KIMI_ERROR(EXPR) KIMI_LOG(ERROR,EXPR)
#define KIMI_VERBOSE(EXPR) KIMI_LOG(VERBOSE,EXPR)
#define KIMI_TRACE(EXPR) KIMI_LOG(TRACE,EXPR)
#define KIMI_INFO(EXPR) KIMI_LOG(INFO,EXPR)
#define KIMI_PROFILE(EXPR) KIMI_LOG(TRACE,EXPR)
// Use KIMI_PRIVATE for sensitive tracing
//#if defined(_DEBUG)
# define KIMI_PRIVATE(EXPR) KIMI_LOG(PRIVATE,EXPR)
// #else
// # define KIMI_PRIVATE(EXPR) (void)0
// #endif
I use following micro,
#if DEBUG
#define LOGE2(x,y) std::cout << "ERRO : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x <<":"<< y <<std::endl;
#define LOGI2(x,y) std::cout << "INFO : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x <<":"<< y << std::endl;
#define LOGD2(x,y) std::cout << "DEBG : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x <<":"<< y << std::endl;
#define LOGE(x) std::cout << "ERRO : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x << std::endl;
#define LOGI(x) std::cout << "INFO : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x << std::endl;
#define LOGD(x) std::cout << "DEBG : " << "[" << __FILE__ << "][" << __FUNCTION__ << "][Line " << __LINE__ << "] " << x << std::endl;
#else
#define LOGE2(x,y) NULL
#define LOGI2(x,y) NULL
#define LOGD2(x,y) NULL
#define LOGE(x) NULL
#define LOGI(x) NULL
#define LOGD(x) NULL
#endif
USE:
LOGE("ERROR.");
LOGE2("ERROR1","ERROR2");
As is clear from the other answers, there are many. There is one that I like which both allows for a variable number of arguments, prints the names of the arguments, and which includes a newline.
void debug_print() { std::cerr << std::endl; }
template <typename Head, typename... Tail>
void debug_print(Head H, Tail... T) {
std::cerr << ' ' << H;
debug_print(T...);
}
#ifdef DEBUGFLAG
# define DEBUG(...) std::cerr << "dbg (" << #__VA_ARGS__ << "):", \
debug_print(__VA_ARGS__)
#else
# define DEBUG(...) do {} while(0)
#endif
Much of the explanation is as in this answer, but I find that the additional templating simplifies it when I want to debug-print several variables on one line without having different macros.
EDIT: The code marked as "not working" was actually working. It was because of a syntax problems in my tests, not detected by the compiler. So the question is already solved, thank you.
C++ is not a language I use everyday, so it is possible that the solution is trivial.
About the context first. I use C++ to develop on a microcontroller (Arduino-based, AVR microcontroller), so I do not use the STL, printf-like functions, new/malloc should be avoided and C++ <string> too.
I have an object called Serial similar to the C++ cout iostream, to communicate with the microcontroller with a serial interface. I have overloaded the "<<" operator of the class from which Serial is an instance so I can do something like that:
Serial << "debug " << "value is " << 3 << endl;
// Whithout the << operator it would be:
Serial.print("debug ");
Serial.print("value is ");
Serial.println(3);
I would like to create a function (or a macro) that enables this kind of line only if debugging is enabled, and which automatically add the "debug" string and append the "endl" value at the end.
So something like that (warning, code does not work because "data" cannot expand as a whole C++ instruction):
#ifdef DEBUG
#define PRINT_DEBUG(data) do {Serial << "debug " << data << endl;} while(0)
#else
#define PRINT_DEBUG(data) do {} while(0)
#endif
// This code works
PRINT_DEBUG("hello world");
// This code does not work
int value1 = 3;
char * value2 = "this is a string";
PRINT_DEBUG("sensor1 value:" << value1 << " other sensor value " << value2);
This kind of function/macro would allow me to easily output strings on my serial interface with a specific "string protocol" without having to repeat the "debug" string at the start. It would also allow me to easily disable the print of debug message by not setting the DEBUG macro. I also have only one "#ifdef DEBUG" instead of several ones in my code.
I managed to do something like that with variadic arguments, but I hate this solution because it is dangerous to use (I do not want to specify the number of arguments), and I cannot mix different type of data:
void __rawSend(char * args, ...) {
Serial.print(args);
va_list paramList;
va_start (paramList, args);
while(true) {
char * next = va_arg(paramList, char*);
if (next == NULL) {
break;
}
Serial.print(" ");
Serial.print(next);
}
Serial.println();
va_end(paramList);
}
#ifdef DEBUG
#define printDebug(...) do {__rawSend(OUTPUT_DEBUG, __VA_ARGS__, NULL);} while(0)
#else
#define printDebug(...) do {} while(0)
#endif
int intValue = 1;
char * stringValue = "data";
// This works
printDebug("hello",stringValue);
// This does not works
printDebug("data is", intValue);
How can I do that? Is it possible with macros (while avoiding variadic arguments and mixing different kind of types)? Is there a better solution?
Sorry all, the code marked as "not working" does actually work. It was because of a syntax problems in my tests, not detected by the compiler.
Anyway, my solution can benefit other people working with Arduino, as I have seen solutions using printf or trying to recreate printf.
I used the "<<" operator coming from http://arduiniana.org/libraries/streaming/
I tend to avoid macros for this sort of things and use classes and static polymoprhism instead :
// Define different types providing a stream interface
struct DebugStream
{
template <typename T>
std::ostream & operator<< (const T & x) const {
return Serial << "debug " << x;
}
// This one is for stream manipulators
std::ostream & operator<< (std::ostream& (*x) (std::ostream&)) const {
return Serial << "debug " << x;
}
};
// This type also provides a stream-like interface but does nothing
struct NoStream
{
template <class T>
const NoStream & operator<< (const T & x) const {
return *this;
}
const NoStream & operator<< (std::ostream& (*x) (std::ostream&)) const {
return *this;
}
};
// Instanciate a debug object having one of the previously defined types
//
// Make sure to declare debug in a common .hxx file included everywhere else
// but to define it only once in a .cxx file.
#ifdef DEBUG
DebugStream debug;
#else
NoStream debug;
#endif
// Use it like you would use the Serial iostream
debug << "value is " << 3 << std::endl;
Since everything is inlined and exact types are known at compile-time, the compiler can optimize out all unnecessary operations on NoStream instances.
If I understand your problems correctly...
Looks to me like you need to overload operator<< for all types you're going to send to the debug interface.
The var args macro has to have a way to deduce the types of its arguments. The way you have it implemented it's expecting all C type strings. You'd be better off with printf or a library like fastformat.
If your operator<< is not returning a reference to the class that allows operator<< to be chained, you'll get errors like "I have the following error for the line "DEBUG("hello" << " " << "world");" : invalid operands of types 'const char [6]' and 'const char [2]' to binary 'operator<<' ". I do not believe DEBUG("hello" << " " << "world"); can be made to work. DEBUG( "hello", "world"); might work.
I am trying to modify my log class to accept variables in my string. For example, if I wanted to output that there are 7 players in an area.
Here is my write to log function:
void Log::writeSuccess(string text,...)
{
// Write the sucessfull operation to the logfile
logfile << "<---> " << text << endl;
}
And here is my calling code:
int playernum = 7;
errorLog.writeSuccess("There are %i players in the area", playernum);
It just ends up outputting to the file: There are %i players in the area
Any way to fix this?
I wonder how on earth does your program even compile?!
You call writeSuccess with 2 arguments, whereas it is declared to take only one argument.
You should look at boost format
The problem with using printf-style format strings is that those strings are
dependent on the types of the provided arguments, and
dependent on the order of the provided arguments.
Not only is this error-prone when you are writing those lines. In my experience the types and order of the arguments will easily change in software that is actively maintained and extended, and it's much harder still to keep the format strings in sync with changes applied later, than it is to do so when you initially write the code.
The problem of needing to manually keep the parameter types in sync with the format string can easily be solved in C++, streams have proven that 25 years ago. Boost.Format even manages to combine format strings with type safety.
A different approach, solving both problems, is taken by some logging libraries I have seen: They use a syntax where you specify which parameter is to be inserted at a specific place in a string by using the parameter's name, and they free you from having to think about the parameter's type by individually converting all parameters to strings before inserting them:
log( "i now has the value of #(i), current size is #(x.get_size(y))",
LOG_PARAM(i) + LOG_PARAM(x.get_size(y)) );
If you don't want to use stdarg.h which doesn't look good in c++ IMO. you can do something like this. Keep in mind that although this is a small class (you can add to it for better logging), its not the most efficient way to do it.
#include <iostream>
#include <sstream>
class Log
{
public:
Log() : os()
{
}
~Log()
{
fprintf(stderr, "%s\n", os.str().c_str());
}
template<typename T>
std::ostringstream &operator<<(const T &t)
{
os << "Log file - " << t;
return os;
}
private:
std::ostringstream os;
};
int main(int argc, char *argv[])
{
//usage
for (int i = 0; i < 10; ++i)
Log() << "Hello world " << i;
return 0;
}
Look at stdarg standard library. It allows you to handle variable number of parameters.
In case you can't or won't use boost:
void Log::writeSuccess(const char* const fmt, ...) {
va_list ap;
va_start(ap, fmt);
char buff[1024];
vsnprintf(buff, sizeof(buff), fmt, ap);
logfile << buff;
}
Note: it assumes that the written length is limited.
Update: with gcc it's possible to do this in a type-safe way, you need the following declaration.
class Log {
void writeSuccess(const char* const fmt, ...) __attribute__ ((format (printf, 2, 3)));
//...
};
Info here. Note: it's a warning, not a compile error. If you ignore warnings that's your problem..:)
I'm creating a logger with the following sections:
// #define LOG(x) // for release mode
#define LOG(x) log(x)
log(const string& str);
log(const ostream& str);
With the idea to do:
LOG("Test");
LOG(string("Testing") + " 123");
stringstream s;
LOG(s << "Testing" << 1 << "two" << 3);
This all works as intended, but when I do:
LOG(stringstream() << "Testing" << 1 << "two" << 3);
It does not work:
void log(const ostream& os)
{
std::streambuf* buf = os.rdbuf();
if( buf && typeid(*buf) == typeid(std::stringbuf) )
{
const std::string& format = dynamic_cast<std::stringbuf&>(*buf).str();
cout << format << endl;
}
}
results in 'format' containing junk data instead of the usual correct string.
I think this is because the temporary ostream returned by the << operator outlives the stringstream it comes from.
Or am I wrong?
(Why does string() work in this way? Is it because it returns a reference to itself? I'm assuming yes.)
I would really like to do it this way as I would be eliminating an additional allocation when logging in release mode.
Any pointers or tricks to get it done this way would be welcomed. In my actual solution I have many different log functions and they are all more complex than this. So I would prefer to have this implemented somehow in the calling code. (And not by modifying my #define if possible)
Just to give an idea, an example of one of my actual #defines:
#define LOG_DEBUG_MSG(format, ...) \
LogMessage(DEBUG_TYPE, const char* filepos, sizeof( __QUOTE__( #__VA_ARGS__ )), \
format, __VA_ARGS__)
which matches varargs printf-like log functions taking char*, string() and ostream() as well as non-vararg functions taking string(), exception() and HRESULT.
I think I see what's happening. This produces the expected output:
log(std::stringstream() << 1 << "hello");
while this does not:
log(std::stringstream() << "hello" << 1);
(it writes a hex number, followed by the "1" digit)
A few elements for the explanation:
An rvalue cannot be bound to a non-const reference
It is OK to invoke member functions on a temporary
std::ostream has a member operator<<(void*)
std::ostream has a member operator<<(int)
For char* the operator is not a member, it is operator<<(std::ostream&, const char*)
In the code above, std::stringstream() creates a temporary (an rvalue). Its lifetime is not problematic, as it must last for the whole full expression it is declared into (i.e, until the call to log() returns).
In the first example, everything works ok because the member operator<<(int) is first called, and then the reference returned can be passed to operator<<(ostream&, const char*)
In the second example, operator<<(cannot be called with "std::stringstream()" as a 1st argument, as this would require it to be bound to a non-const reference. However, the member operator<<(void*) is ok, as it is a member.
By the way: Why not define the log() function as:
void log(const std::ostream& os)
{
std::cout << os.rdbuf() << std::endl;
}
Alter your LOG() macro to this:
#define LOG(x) do { std::stringstream s; s << x; log(s.str()); } while(0)
That will let you use the following syntax in your debugging logs, so you don't have to manually construct the string stream.
LOG("Testing" << 1 << "two" << 3);
Then define it to nothing for release, and you'll have no extra allocations.