Creating a custom printf with validation and syntax highlighting - c++

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__)

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)

clang-format Function and error check in the same line

I struggle to set up clang-format to allow short lines for such format,
ierr = fun(); CHKERRQ(ierr);
clang-format break the line, as result
ierr = fun();
CHKERRQ(ierr);
It is a solution to stop clang-fromat to break short lines? Or this is in principle wrong idea to format code like this. Any advice very appreciated.
EDIT: Following Guillaume answer
In fact, I have a slightly more complex problem, where errors codes of different types are returned by two libraries, in particular, PETSc and MoAB. Also, this system has own error codes, which need to be handled. All without compromising efficiency.
struct ErrorCheckerCode {
inline void operator<<(const MoFEMErrorCode err) {
if (PetscUnlikely(err)) {
// Handle & thorw PETSc/MoFEM error
}
return;
}
inline void
operator<<(const moab::ErrorCode err) {
if (PetscLikely(MB_SUCCESS != err)) {
// Handle & trow MOAB error
}
return;
}
static const char *fUNC;
static const char *fILE;
static int lINE;
};
struct ErrorCheckerFunction {
inline ErrorCheckerCode operator<<(const char *func) {
ErrorCheckerCode::fUNC = func;
return ErrorCheckerCode();
}
};
struct ErrorCheckerFile {
inline ErrorCheckerFunction operator<<(const char *file) {
ErrorCheckerCode::fILE = file;
return ErrorCheckerFunction();
}
};
struct ErrorCheckerLine {
inline ErrorCheckerFile operator<<(int line) {
ErrorCheckerCode::lINE = line;
return ErrorCheckerFile();
}
};
And definition follows this:
#define CHKERR \
ErrorCheckerLine() << __LINE__ << __FILE__ << PETSC_FUNCTION_NAME <<
So at the end I can handle errors like this
CHKERR fun_moab();
CHKERR fun_petsc();
CHKERR fun_mofem();
This implementation is essential, so it has to be done optimally, I wonder if that can be done in simpler, more efficient way. Criticism or advice is very welcome.
Note:
As a commend. It is funny how formatting of code triggers this type of developments.
I use clang-format a lot and I don't think it can achieve this kind of formatting.
I've encountered this pattern a lot in libraries using OpenGL and DirectX. You have a API with error codes or an error stack like in OpenGL and every function might fail or issue a warning. You want your code to be optimized in release and still have a debug mode to find preciselly where things went wrong in case you spot a bug.
Some libraries provide some kind of error callback that you can set and will effectively be called each time there is a relevent error.
In case you want something more customized or no error callback is provided, you can write a simple wrapper that does a systematic error checking and warnings logging.
With these solutions you can even implement some mechanism to activate the debugging at runtime.
Now, if you are writing the library or functions that return errors returned this way, you might want to include the debug mode directly in the library with the debug callback strategy.
Now, if you want to stick to the macro solution, you could do the following:
struct ErrorChecker
{
ErrorChecker& operator << (int ierr)
{
// Error checking code
return *this;
}
};
#define CHKERR ErrorChecker() <<
and then
CHKERR fun();

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.

Deferring evaluation of function-call arguments

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).

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.