Deferring evaluation of function-call arguments - c++

I wrote a small logging class that takes two or more arguments:
void my_log(int level, pattern [, fillins...]);
(The pattern and fill-ins are processed in a sprintf-like manner.)
The implementation of my_log begins:
if ( level < _min_level ) return;
I thought this would tidily short-circuit the call to very_elaborate_representation() ...
my_log(DEBUG, "%s", my_object.very_elaborate_representation());
when _min_level is greater than DEBUG.
But it doesn't: apparently (and not surprisingly) arguments are evaluated before the function call.
The formatting is expensive, and intended only for debugging and trouble-shooting.
Is there a clean way to solve this problem with C++11, other than wrapping the call with an if-test?

Not sure if it helps, but this is how I have previously implemented a logging module:
In the header file:
#define LOG(level,...) do {if (level >= MIN_LEVEL) log_printf(__VA_ARGS__);} while (0)
void log_printf(const char* data,...);
In the source file:
void log_printf(const char* data,...)
{
char str[64] = {0}; // You can think of other ways for allocating it
va_list args;
va_start(args,data);
vsnprintf(str,sizeof(str),data,args);
va_end(args);
printf(str);
}
In order to issue a log message, you only need to call LOG(SOME_LEVEL,some-parameters).

Related

Replace C++ class/static method with preprocessor?

I'd like to use the built-in compiler checks to verify format strings of a custom logging framework to catch the odd runtime crash due to mismatching format string <-> parameters in advance.
Arguments of the custom C++ logging methods are identical to the printf() family so I was attempting to replace all calls to
MyLogger::Error(
with
fprintf(stderr,
Though unfortunately the (clang) preprocessor chokes on the scope resolution operator (::), i.e. instead of ULog::Warn( only the ULog substring is recognized:
#define MyLogger::Error( fprintf(stderr,
Any suggestions on how to make this work much appreciated.
Have you tried a variadic template? found here.
#include <iostream>
namespace MyLogger
{
template <typename... T>
auto Error(const char * _Format, T &&... args)
{
return printf(_Format, std::forward<T>(args)...);
};
}
#define printf(...) MyLogger::Error(__VA_ARGS__)
int main()
{
MyLogger::Error("Non-Macro Print \n");
printf("Macro Print \n");
return 0;
}
Elaborating on the approach suggested by #Someprogrammerdude I've extended the custom logging class to use the clang/gcc format attribute to enable compiler format checking.
The declaration simply becomes
static void Error(const char *format,...) __attribute__ ((format (printf, 1, 2)));
It's even better than the original idea to use the preprocessor to temp. enable checks by replacing calls to the custom formatter with calls to printf() as it's enabled all the time catching argument mismatches immediately!
(FWIW - already fixed dozens of issues and couple potential crashes on our 120+ LOC code base)
What if you modify MyLogger::Error to
MyLogger::Error(args){
if (0) {
fprintf(stderr,args)
}
//actual function
}
This way you get the built-in warnings and it does not effect the efficiency of your code. (You can obviosly actually use the print if you wan to write to stderr, but I think if you wanted that you used that already)

Creating a custom printf with validation and syntax highlighting

I'd like to be able to create a logging mechanism, which very simply passes arguments through to printf, but I need syntax highlighting, and validation of input. This is the outline for the Log namespace I have so far.
#pragma once
#include "pch.h"
namespace Log
{
// Determines whether to create the console window or not.
// Turn off for before release.
extern bool CreateConsoleWindow;
// Initialisation.
extern bool Init();
// Writes a line to the console, appended with \r\n.
extern void WriteLine(const char* _Format, ...);
// Writes a line to the console, with a [Debug] prepend.
extern void DebugInfo(const char* _Format, ...);
// Writes a line to the console, with a [Server] prepend.
extern void ServerInfo(const char* _Format, ...);
// Destruction.
extern bool Dispose();
}
This is my implementation for Log::WriteLine(_Format, ...):
// Writes a line to the console, appended with \r\n.
void WriteLine(const char* _Format, ...)
{
// Print the message.
char buffer[4096];
va_list args;
va_start(args, _Format);
auto rc = vsnprintf(buffer, sizeof(buffer), _Format, args);
va_end(args);
// Append the new line.
printf("\r\n");
}
But when I do this, I lose all the validation and syntax highlighting, which is essential, being a beginner, from the input string. For instance:
auto hash = Crypto::GetHash(syntax[1].c_str()); // unsigned long, by way of multiple nested macros.
printf("Hash: %d", hash);
This would show %d in lime green, and there would be a warning on hash in the the printf, saying it won't be displayed, because it expects %lu. I rely on VS to teach me what I'm doing wrong, so that I can learn from my mistakes.
If I do the same with my function:
auto hash = Crypto::GetHash(syntax[1].c_str()); // unsigned long, by way of multiple nested macros.
Log::WriteLine("Hash: %d", hash);
Then %d is the same brown as the rest of the string, and there is no validation error.
These two things are essential. Is there any way to make this work properly? I'm new to C++ after about a decade of .NET experience, and the learning curve is massive. I thought I'd start with the basics, just an easy logging system, so I can see what is being output at any time, in a clean and elegant way. In C#, this would have only taken a couple of minutes to put together, but in C++, it's now four hours later, I have about 40 tabs open in three windows of Firefox, and I'm still smashing my head against every brick wall it's possible to come across.
I've tried to define a macro inside the namespace:
#define WriteLine(_Format, ...) printf(_Format, __VA_ARGS__)
But is says it can't find printf. I've tried decorating with _Printf_format_string_:
extern int _cdecl DebugInfo(_Printf_format_string_ const char* _Format, ...);
But that makes no difference. I've tried __attribute__((format(printf, 1, 2)))
extern void WriteLine(const char* _Format, ...) __attribute__((format(printf, 1, 2)));
But that throws up a mass of errors about expecting a {, unexpected identifier, and expected a declaration, but doesn't say where, or why, or how.
Am I asking too much of C++, for such a basic request?
Modern code editors and IDEs (and compilers!) provide additional support for the printf family of functions beyond the scope of the C++ and C languages. One prime example of that is extended error checking for the format strings, which cannot be done in C at all, and in C++ only with major pains. There are a few ways of achieving your aim with only minor modifications.
<iostreams> Solution
By default, std::cout is synchronized with printf, so you may not actually need to deal with printf directly. There are many solutions out there for how to achieve various results with the iostreams library framework, even though it is a bit of a hassle to use. It is type-safe, thus not requiring additional IDE support to directly show mistakes.
Note that iostreams has a rather bad reputation for its complexity and ease-of-use.
Typesafe Variadic Template
You can use a variadic template along the lines of:
void add_format_specifier(std::ostream& out, int const&) {
out << "%d";
}
void add_format_specifier(std::ostream& out, char const*) {
out << "%s";
}
void add_format_specifier(std::ostream& out, hex const&) {
out << "%x";
}
template<typename... Args>
void WriteLine(Args&&... args) {
std::ostringstream format;
((void)0, ..., add_format_specifier(format, args));
printf(format.str().c_str(), args...);
}
This solution is type-safe and compiler-checked, again without relying on special-casing printf. Personally, I would suggest going down this route, as it combines maximum flexibility with easy type-checking - and does away with the format specifier ;). You may wish to decide whether using printf as the low-level primitive is really what you want to do here, but it is easily exchanged for other methods of output.
Compile-Time Checking of a Format String
It is possible to add custom compile-time checking of format specifiers that you can then static_assert. The basic building blocks that you need is a variadic template that also takes your format specifier (basically, you need to preserve types inside your variadic function), and which calls a format specifier along the lines of:
template<std::size_t N>
consteval bool check_format(char const (&format)[N], std::size_t i) {
for(; i < N; ++i) {
if(format[i] == '%') {
if(i + 1 >= N) {
return false;
}
if(format[i + 1] == '%') {
++i; // skip literal '%'
} else {
return false; // no more specifiers expected
}
}
}
return true;
}
template<std::size_t N, typename T, typename... Args>
consteval bool check_format(char const (&format)[N], std::size_t i) {
for(; i < N; ++i) {
if(format[i] == '%') {
if(i + 1 >= N) {
return false; // unterminated format specifier
}
if(format[i + 1] == '%') {
++i; // skip literal '%'
} else {
if constexpr(std::is_same_v<T, int>) {
// quickly check if it is an acceptable integer format specifier
if(format[i + 1] != 'd' && format[i + 1] != 'x') {
return false;
} else {
return check_format<N, Args...>(format, i + 2);
}
} else {
return false; // unknown format specifier
}
}
}
}
return false;
}
See here for a bit more context. (Note: This example relies on the C++20 consteval specifier, which may not be supported by your compiler. Similar effects can be achieved with constexpr.)
This solution will give you compile-time errors, but no syntax highlighting - C++ cannot impact how your IDE draws strings (yet ;) ).
Macro Solution
While your macro #define WriteLine(_Format, ...) printf(_Format, __VA_ARGS__) does not really allow you to do all that much with respect to implementing anything beyond renaming printf, it will work, provided that the user code also include <cstdio>. If you provide a header with that macro, you may wish to just add the inclusion in there for ease of use.
There is only a tiny improvement to be made, so that the macro also works for calls of the form WriteLine("abc"):
#include <cstdio>
#define WriteLine(_Format, ...) printf(_Format __VA_OPT__(,) __VA_ARGS__)

Replace C macro for a function call with a C++ template?

Is it possible to replace this preprocessor macro:
#define AL_CALL(a) do { a; \
ALenum e = alGetError(); \
if(e != AL_NO_ERROR) \
UtilitySoundNode::printALError(e,__FILE__, __LINE__); \
} while(0)
with a C++ template? If it is possible, will make any sense to do it (pros/cons - overhead/debugging)?
Note:
Basically I am wondering if there is an elegant way to handle this kind of error handling in C++.
EDIT:
Of course I made a mistake a is a function call. As one may guess it is a function call with parameters of a OpenAL function.
AL_CALL(someAlFunction(param1, param2))
NOTE:
Somebody decided to edit the macro and make it nicer but I'd prefer to keep the original one too. So here it is:
#define AL_CALL(a) {a; ALenum e = alGetError();if(e != AL_NO_ERROR)PUtilitySoundNode::printALError(e,__FILE__, __LINE__);}
One problem here seems to be that the "a" can be some arbitrary function (with parameters) which sets the error code returned by alGetError().
That can be rewritten to C++ by using a functor object. To pass the arguments (and object instance if necessary) std::bind or boost::bind can be used (note that to bind reference args the std::ref/boost::ref is necessary).
However, if you'd want to still have the __FILE__ and __LINE__ passed the the printError() that C++ template still would need to be called by a macro which will pass those to the template. __FILE__ and __LINE__ are only expanded by the preprocessor, so there is no way around using a macro for them.
But the macro could be much simpler then and most of the work can be done in the C++ template (which has many advantages e.g. for debugging, because in most debuggers you cannot step into a macro).
EDIT: adding the code as an example:
template<typename T>
void ALcallAndCheck(T c, const char *file, size_t line)
{
c();
ALenum e = alGetError();
if(e != AL_NO_ERROR)
UtilitySoundNode::printALError(e, file, line); \
}
#define AL_CALL(a) ALcallAndCheck(a, __FILE__, __LINE__)
Then, instead of
AL_CALL(SomeFunction(2, refAttr));
the call will become:
AL_CALL(std::bind(SomeFunction, 2, std::ref(refAttr)));
EDIT 2:
The previous indeed does not work with expressions, which the original macro allows. To work also for expressions, the macro can be altered to:
#define AL_CALL(a) ALcallAndCheck([&]{ (a); }, __FILE__, __LINE__)
That will create a lambda which will evaluate anything that comes into the macro. Then even the std::bind is not necessary and it can be called directly as:
AL_CALL(SomeFunction(2, refAttr));
AL_CALL(SomeOtherFunction1()+SomeOtherFunction2(8));
No, the use of __FILE__ and __LINE__ pretty well require the preprocessor.
Note that using a template instead of a macro does not produce an exact analog. The macro defined in your question allows a to represent a statement as well as an expression. A template does not have that kind of flexibility. The template defined below assumes a is a non-void expression.
There is no standard way to implicitly inject a function caller's file name and line number without the caller passing in that information to the called function. A preprocessor macro allows a means to make the syntax appear to be implicit injection, when in fact the information is being passed.
template <typename T>
void AL_CALL (T a, const char *file, int line) {
ALenum e = alGetError();
if(e != AL_NO_ERROR)
UtilitySoundNode::printALError(e, file, line);
}
#define AL_CALL(X) AL_CALL((X), __FILE__, __LINE__)
You may be able to use system specific facilities (e.g., CaptureStackBackTrace + SymFromAddr or backtrace + backtrace_symbols) to get approximately the same information implicitly, but it may require debugging symbols to be present, and inline functions may not produce the expected output.
template<class A>
void al_call(A&&a){
ALenum e = a();
if(e != AL_NO_ERROR)
UtilitySoundNode::printALError(e,__FILE__, __LINE__);
}
Use:
al_call( [&]{ return bob(); });
Instead of:
AL_CALL( bob() )
The line/file info is not useful above.
So
template<class A>
void al_call(A&&a, char const*file, unsigned line){
ALenum e = a();
if(e != AL_NO_ERROR)
UtilitySoundNode::printALError(e,file, line);
}
#define AL_CALL(...) al_call([&]()mutable{return __VA_ARGS__;}, __FILE__, __LINE__)
and it is almost a drop in replacement.

How to overload printf or cout

I use cout statements in my program for debugging purposes. I would like to make a function that works like it, or works like printf, but is sensitive to a global variable. If this global variable is true, then it will print to screen. If it is false, then it won't print anything. Is there already a function like this? If not, then how can it be made?
Something like this:
int myPrintf(const char* format, ...)
{
if (globalCheck == 0)
return 0
va_list vl;
va_start(vl, format);
auto ret = vprintf(format, vl);
va_end(vl);
return ret;
}
va_start and va_end take the arguments in the ... and encapsulate them in a va_list. with this va_list you can then the vprintf which is a variant of printf designed exactly for this need.
Side note - usually it is bad practice to use global variables. A better thing to do is to encapsulate it in a class like this -
class ConditionalPrinter {
public:
ConditionalPrinter() : m_enable(true) {}
void setOut(bool enable) { m_enable = enable; }
int myPrintf(const char* format, ...);
private:
bool m_enable;
}
and then to check m_enable instead of the global variable.
Usage of this looks like this:
ConditionalPrinter p;
p.myPrintf("hello %d", 1); // printed
p.setOut(false);
p.myPrintf("hello2 %d", 1); // not printed
....
Don't write it yourself. Doing it right is much harder then you think. Even harder when you need threads and efficiency. Use one of existing logging libraries like:
glog: http://code.google.com/p/google-glog/ (I prefer this - it is lightweight and do what it needs to do)
Log4cpp http://log4cpp.sourceforge.net/ (powerful and configuration compatible with popular java logging)
... add your favorite to this wiki
As someone else said, there are several good logging frameworks available. However, if you want to roll your own, the first thing to note is that cout isn't a function, it's a stream. The function is operator<<. What you can do is something like the following:
/* trace.h */
extern ostream debug;
void trace_init();
void trace_done();
/* trace.cpp */
#include "trace.h"
ostream debug(cout.rdbuf());
static ofstream null;
void trace_init()
{
null.open("/dev/null");
if(output_is_disabled) { // put whatever your condition is here
debug.rdbuf(null.rdbuf());
}
}
void trace_done()
{
null.close();
}
You might have to adjust a bit if you're on a platform without /dev/null. What this does is let you write
debug << "here's some output << endl;
and if you have the output enabled, it will write to cout. If not, it will write to /dev/null where you won't see anything.
For that matter, you could just set cout's rdbuf to somewhere where you won't see that output, but I would find that to be a really bad idea. Creating new streams gives you a lot more flexibility in controlling your output.

Can I return early from a variable-argument function?

Suppose I have two C++ functions for debug output:
void Trace( const wchar_t* format, ... )
{
va_list args;
va_start( args, format );
VarArgTrace( format, args );
va_end( args );
}
void VarArgTrace( const wchar_t* format, va_list args )
{
WCHAR buffer[1024];
//use ::_vsnwprintf_s to format the string
::OutputDebugStringW( buffer );
}
the above uses Win32 OutputDebugStringW(), but it doesn't really matter. Now I want to optimize the formatting so that when there's no debugger attached formatting is not done (I measured - speedup is significant):
void Trace( const wchar_t* format, ... )
{
if( !IsDebuggerPresent() ) {
return;
}
//proceed as previously
va_list args;
.....
}
will the fact that I return early once IsDebuggerPresent() returns null affect anything except that formatting will be skipped?
I mean I no longer call va_start and va_end - will this matter? Will skipping va_start and va_end cause any unexpected behavior changes?
No, there is no obligation to use va_start in a varargs function.
If you don't use va_start you cannot use va_end; if you use va_start you should use va_end, no matter how the function returns.
The only requirement on an early return is that if you have used (executed) va_start(), you must use va_end() before you return.
If you flout this rule, you'll get away with it on most systems, but some system somewhere needs the va_end(), so don't risk omitting it. It is undefined behaviour to omit it.
Other than that rule, it is up to you how you handle your return. Your proposed early return is not a problem.
All those macros do (on Windows at least) are pointer manipulation for a bookmark in the arg list. Returning early will be fine. This is compiler-specific, though I can't imagine why early return would be a problem on other platforms.
From x86 vadefs.h:
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
That's quite safe, because in C languages, the job of 'cleaning' the passed parameters from the stack is always done by the calling function, not the called one.
Some languages (Pascal springs to mind) may do this the other way round. This is potentially more efficient, but only works because there's no concept of a variable number of arguments.
There's not much overhead in the varargs stuff, usually just a few pointer manipulations.
There's probably slightly more overhead in setting up the stack to each varargs call, so instead of
void Trace( const wchar_t* format, ... )
{
if( !IsDebuggerPresent() ) {
return;
}
//proceed as previously
va_list args;
.....
}
you're probably better off using the preprocessor to prevent calls to trace stuff in non debug builds.
Littering your code with
if( IsDebuggerPresent() ) Trace( stuff... );
seems quite distateful too.
Did VC2010 get variadic macros yet? :)