I wrote my own logger that I can use as follows:
LOG("This is the number five: " << 5 << ".");
I'd like to write an error checking method that simply takes a return code, checks if it's an error, and if so prints an error message
Thus, I'd like to turn:
if(result == -1)
{
LOG("ERROR! We got a result of: " << result << ".");
// Do some other stuff
}
into:
checkForError(result, "ERROR! We got a result of: " << result << ".");
void checkForError(int result, SomeStringType message)
{
if(result == -1)
{
LOG(message);
// Do some other stuff
}
}
is this possible? I've tried passing it as a char*, a std:string, and as a stringstream, but I can't seem to get it to work correctly
Is there any way to do this so I'm simply concatenating the error message in the call to the function?
Yes, it's possible. You could do it like this:
#define LOG(X) \
do { \
std::ostringstream log_stream; \
log_stream << X; \
LOG::log_internal(log_stream.str()); \
} while (0)
where LOG::log_internal might be declared in your logger header like
struct LOG {
static void log_internal(const std::string &);
};
The main drawback of this is that it's not the most hygienic macro ever -- if the argument X contains semicolons, then you could have additional statements executed inside the macro's scope, and there won't be a compiler error. This macro is far from cryptic though in my opinion, and fortunately you don't actually have to worry about an attacker performing SQL-injection-esque attacks against your macros.
I use loggers that basically look like this in my projects, and I learned to do it this way from other projects. (Actually the one I use is a little more developed, it also passes __FILE__ and __LINE__ to the LOG::log_internal function, and there's log channels and log levels which may or may not be active etc. etc...)
Related
I have a macro function which works as you would expect:
#define PRECONDITION(testBool) ( !(testBool) ? \
( fprintf(stderr, "%s:%i: Precondition '%s' failed.\n", \
__FILE__, __LINE__, #testBool), \
exit(1) ) : void(sizeof(0)) )
This is great, since I can create nice assertions:
PRECONDITION(5 > 6); // prints "<file>:<line>: Precondition '5 > 6' failed."
While this works, I'm trying to learn a better and more modern way of doing this,
using constexpr, so I can have type safety, use std::err <<.
However, I have not been able to find a source stating how to do this / whether or not it
is actually possible, so I ask here. What I image is something along the lines of:
constexpr void PRECONDITION(bool testBool)
{
if(testBool) { return; }
std::cerr << __SOME_MAGIC__ << ":" << __SOME_MAGIC__ << ":"
<< "Precondition '" << __SOME_MAGIC__ << "' failed." << std::endl;
}
Is it possible to achieve this behaviour?
Short answer: No.
Long answer: Macro expansion is just a textual transform of the source code. Once transformed by macro definitions, source code is "given" to the compiler to compile it. So, macros are expanded before compiler has even started doing its job while constexpr functions are executed on compile time. Therefore, macros and constexpr expressions are 2 different things.
Let's say I have a project with many functions. For debugging purpopses, I want each one of them print out a diagnostic message (or a few) when called:
int f(int arg) {
cerr << "f() called with argument" << arg << endl;
int ret = 42;
cerr << "f() returned " << ret << endl;
return ret;
}
and so on. In one function, there may be as many as five to six messages printed out on cerr. I want to be able to disable them unless a debug flag (like NDEBUG) is set. One thing I could do is wrap each cerr statement with an if-statement.
#ifdef NDEBUG
const bool DEBUG_ON = true;
#else
const bool DEBUG_ON = false;
#endif
int f(int arg) {
if (DEBUG_ON) {
cerr << "f() called with argument" << arg << endl;
}
int ret = 42;
if (DEBUG_ON) {
cerr << "f() returned " << ret << endl;
}
return ret;
}
But it can get really tedious and, in the end, virtually unreadable with a couple dozens of such statements. My idea was to replace cerr with a custom object with an overloaded << operator which would send any arguments passed to it "into the void" like this:
class NullOutStream {
public:
NullOutStream operator<<(...) {
return *this;
}
};
NullOutStream debug_out;
#ifdef NDEBUG
#define debug_out cerr
#else
#define debug_out debug_out
#endif
Although it works perfectly, it doesn't seem very elegant. Is there a standard/nicer way to accomplish this?
There is no "standard" way to do this. There are many logging/assertion libraries that deal with similar issues, and there are different approaches to solve this. A few things to keep in mind:
It may or may not be desirable to keep the side effects of your log statement (as yours does). For example, if you did something like this:
debug_out << ++i;
This would increment i no matter whether you are in debug mode or not. This is probably desirable in this case. Conversely, if you do this:
debug_out << some_object.expensive_to_string_operation();
This would call expensive_to_string_operation() even when you are not in debug mode. This is probably not desirable in this case.
This would not happen if you used printf-style debug macro, e.g. something like this:
#ifdef NDEBUG
#define logf(...)
#else
#define logf(...) printf(__VA_ARGS__)
#endif
The reason being that when in non-debug mode, the arguments would be removed by the preprocessor.
You may want to do other things before or after each log statement, e.g. record a time stamp, flush a log file etc. It is possible to do this with streams by using destructor tricks, but it is more complicated to implement. It is much easier to do this using a function call, e.g. something like this:
#define log(msg) printf("%s: %s\n", timestamp(), msg)
You may want to record file name / line number with your logs. Again, this is easier to do with a function call than with a stream.
Streams may be better if you want custom formatting by object type - the printf interface doesn't lend itself to that very well.
I would recommend to have a look at some existing logging libraries to get an idea of different approaches. I suggest looking at Google's glog library because it has an interesting combination of using streams while retaining the ability to do 'per call' things (e.g. record time stamps, line numbers etc).
When you stream variables to an output stream such as cout, type conversion is automatic. What I'm trying to figure out is how to do this via a function call, for example:
inline void DEBUG(ostream& s) // Don't know if this prototype is appropriate
{
cout << s;
}
main()
{
int i = 5;
DEBUG("The value is: " << i << endl); // This doesn't compile
DEBUG("The value is: " + i + endl); // Neither does this
}
I found similar questions on here, but they all involve passing the stream object as a parameter, whereas I'm trying to pass the "streamed data" to a function that already has the stream object, so it's the other way round. Is this even possible? I don't want to resort to explicit type conversions. I also found this question, but I really don't want to write a whole logger class if I can avoid it.
At the moment I'm implementing it as a macro, which works, but I'd rather use an inline function if possible.
#define DEBUG(s) (cout << s)
Of course it does not compile. There are many reasons for that.
First, Operator << is not defined for standard streams, and you are trying to do exactly that: stream stream into stream in your DEBUG(). (Pun intended).
Second, operator << is not defined for string literals, and you are trying to invoke it here:
"The value is: " << i
+ is not defined for literals either, by the way.
To achieve the semantic you want to see, you will have to start with the stream. String literal need to be converted to stream first, and than you can apply << to it. This is ONLY way to achieve what you want.
Edit:
Now since I understand the rationale, I can give a better answer. There are many ways how people are trying to segregate different levels of debugging uniformely, and there are several libraries aiming for that (log4cpp, boost.log to name just few). Before you start implementing your own logging, I would definitely suggest looking into those. There is much more to the good logging than just debug levels.
If, for any reason, you want to use your own homebrew, here are the couple of recepies you might explore:
Use your own logger class (one of the very rare examples, close to
the single one! where Singleton is appropriate). You can than set the
logging level in the beggining of your application, and than just
call Logger::debug() << ...
Enrich above solution with macros. The problem with functions is that, unlike macros, they loose context. So if you want to log file and line number of the logging invocation (and you usually do!), you might want to do LOG_DEBUG << ...; here LOG_DEBUG would expand into something like Logger::debug() << __FILE__ << ":" << __LINE__ << ....
Once you've done this, you will see that sometimes you call other functions inside the << chain. At this point you might realize that those functions would be called regardless of your debug level, and might think you do not want to call them when debugging is not enabled (something along the lines LOG_DEBUG << " Object now is " << object.serialize(); So you will want to enrich the LOG_DEBUG macro to not execute anything when debug level does not match.
And the saga continues... Ready to use the library?
Well, what (at least some) logging libraries would do is create a temporary proxy object that would act as a stream:
#include <iostream>
struct LoggerProxy {
LoggerProxy(const char* file, int line)
{
std::cout << "File " << file << ", line " << line << ": ";
}
template<typename T>
LoggerProxy& operator<<(T&& t)
{
std::cout << t;
return *this;
}
};
#define LOG_DEBUG LoggerProxy{__FILE__, __LINE__}
int main()
{
LOG_DEBUG << "Value is: " << 4;
}
You can do a lot of fancy stuff with this, such as debug level checks, output to different streams or multiple backends (such as simultaneous output to std::cout/cerr and log file) and many more.
Let's suppose that we have several levels of logging: trace, debug, info, error.
I was wondering if there is a way to write the following code:
enum log_level = {trace, debug, info, error};
log_level global_log_level = info;
void log(log_level level, string& message){
if (level >= global_log_level){
std::cout << message << std::endl;
}
}
string create_message(){
...
}
log_level level = debug;
log (level, create_message());
without create_message being called if level is smaller that global_severity_level. Indeed, create_message can be quite long, and no matter what it creates a string. If there are a lot of "debug" logs, those ones can become a substantial overhead when running in non-debug mode.
I know it is possible to do so if the function "log" is a macro, calling create_message() only if severity > minimal_severity; but isn't there another way to do this without macros?
EDIT
In the above, I didn't specify create_message, because it could be anything, in particular:
log(level, "Created object " + my_object.getName());
In this case, is there a way to write log such that the full string is not created, in a relatively transparent way for the programmer calling log?
Many thanks
Similar to #sftrabbit, but as suggested by #ipc.
Use a template to avoid the std::function machinery, and the compiler may be able to inline this and thus it hopefully will end up being faster.
template< typename F >
void log(log_level level, F message_creator){
if (level >= global_log_level){
std::cout << message_creator() << std::endl;
}
}
There are several alternatives. An interesting one is to pass create_message as a std::function<std::string()> and call it from within log:
void log(log_level level, std::function<std::string()> message_creator){
if (level >= global_log_level){
std::cout << message_creator() << std::endl;
}
}
Then you would call it like so:
log(level, create_message);
This can work with arbitrary expressions as arguments if you wrap them in a lambda:
log(level, [&](){ return "Created object " + my_object.getName(); });
If you really don't want to argument to be evaluated at all (as you've described in the comments), then you'll need to check the level outside of the call:
if (level >= global_log_level) {
log(level, create_message());
}
#sftrabbit answer is prefered. But just if you dont want to change log(), you can call it:
log (level, (level >= global_log_level)? create_message() : "");
You can create a macro
#define log(level, message) { \
if(level >= global_log_level) {\
cout << message; }}
Now if you call log(debug, create_message()); create_message will be called only if debug level is the desired one.
In my program I want to use asserts that show an error message. Apart from the well known workarounds for C and C++ there's the "real" solution as BOOST offers BOOST_ASSERT_MSG( expr, msg ) (see also assert() with message)
But a static message isn't enough for me, I also want to show sometimes the failed variables, e.g. in a case like
BOOST_ASSERT_MSG( length >= 0, "No positive length found! It is " << length )
As you can see I'd like to format the message "string" as an stringstream or ostream as that'd allow me to easily show custom types (assuming I've defined the relevant formating function).
The problem here is that BOOST_ASSERT_MSG is by default requiring a char const * so that's not compatible.
Is there a way to redefine / overload assertion_failed_msg() in such a way that using a stream as message will work? How?
(My naive approach failed as the compiler first wanted to do an operator<<("foo",bar) on the message itself...)
You could define your own macro
#define ASSERT_WITH_MSG(cond, msg) do \
{ if (!(cond)) { std::ostringstream str; str << msg; std::cerr << str.str(); std::abort(); } \
} while(0)
It's relatively trivial to achieve this.
BOOST_ASSERT_MSG( length >= 0, (std::stringstream() << "No positive length found! It is " << length).str().c_str() )
Here is a solution that doesn't rely on macros. Instead, it uses a tiny bit of templating and lambda syntax.
template<typename Fn>
void assert_fn( bool expr, Fn fn) {
if (!expr) {
fn();
abort();
}
}
The argument fn can be any callable.
For instance you can call it like so:
assert_fn( a==b, [&](){ cout << "Assertion failed: a="<< a <<
" is different from but b=" << b << endl; } );
The advantage is that the output is that you are not calling abort explicitly and the output is fully customizable. The this advantage, of course, are the seven extra characters of lambda function boilerplate: [&](){} )
I use the BOOST_ASSERT_MSG with my own wrapper around it, so that specifying the assert message with multiple operator<< seems less complex.
#if defined ASSERT_ENABLED
#define ASSERT(cond, msg) {\
if(!(cond))\
{\
std::stringstream str;\
str << msg;\
BOOST_ASSERT_MSG(cond, str.str().c_str());\
}\
}
#else
#define ASSERT(...)
#endif
usage example, provide custom message like you are outputting to cout:
ASSERT(execSize == (_oldSize - remaining), "execSize : " << execSize << ", _oldSize : " << _oldSize << ", remaining : " << remaining);
What it does is, if ASSERT_ENABLED is defined,enable the assertion messages. if(!(cond)) part is optimization, which avoids the costly string operations specified by macro parameter msg, if cond is true
If you are working on Windows only, you can take a look to assert macro. Under the hood it uses _wassert. You can write your own assert macro using it. For instance in my case if I get some point, I want to show assert without conditions:
#ifdef DEBUG
const std::wstring assert_msg = /* build the string here */;
_wassert(assert_msg.c_str(), _CRT_WIDE(__FILE__), (unsigned)(__LINE__));
#endif
I think on other OS you can do the same trick, just take look at assert macro.