macros with variable number of arguments - c++

I've been developing application which uses logging mechanism, and I implemented sort of printf function which takes various number of arguments and prints to log certain message, what I want is to add a function name to this function, but I dont what to write this argument in each function call.
So I decided to write a macros, turns out it not so easy
#define WriteToLogParams(szMessage, nLogLevel, param1, param2) WriteToLogParamsFunc(szMessage, __FUNCDNAME__, nLogLevel, param1, param2)
First I thought that there is some kind of macros overloading and I can easily do it , but turns out if I write another macros with the same name but with different number of argument it wont compile. So to make it work I should make each macros name unique.
So is there are any intelligent way to do this?
Thank you on advance.

You can use __VA_ARGS__ macro
for example:
WriteToLogParamsFunc(const char *__file, int __line, const char* __func, int nLogLevel, const char *szMessage, ...);
#define WriteToLogParams(nLogLevel, szMessage, ...) WriteToLogParamsFunc(__FILE__, __LINE__, __FUNCTION__, nLogLevel, szMessage, __VA_ARGS__ )

I believe there are some preprocessors that support variadic macros but I'm not sure if it's a standard and there definately are compilers that don't support it so it's not portable.
However, instead of trying to have a variable number of parameters, have only one parameter. This is what I have done:-
#define WriteToLogParams(args) WriteToLogParamsObject::Instance (__FUNCDNAME__) << args
where the WriteToLogParamsObject is a class that is created by the static member Instance and has overloaded streaming operators. This gives you the advantages that using these operators has, such as putting streaming overloads into classes:-
class SomeClass
{
static friend WriteToLogParamsObject &operator << (WriteToLogParamsObject &stream, const SomeClass &item_to_log)
{
stream << "member1 = " << item_to_log.m_member1 << ", member2 = " << item_to_log.m_member2;
// and so on (syntax might be off)
return stream;
}
};
And to use the macro:-
WriteToLogParams ("some message " << some_value << " another bit of text " << another_value << " and so on");

Related

What advantages does C++20's std::source_location have over the pre-defined macros __FILE__, __LINE__ and __FUNCTION__?

In my current project, I have used:
Log(__LINE__, __FUNCTION__, message);
But the new C++20 utility class std::source_location
comes with functions line(), column(), file_name(), function_name(), which do the same thing. So, the new C++20 way would be:
log(std::string message,const std::source_location& location = std::source_location::current())
{
std::cout << "Debug:"
<< location.file_name() << ':'
<< location.line() << ' '
<< message << '\n';
}
What is the advantage of the new, C++20 way over the old standard double-underscore macros, __LINE__, __FILE__, __func__, which have already been standard C++ for a long time?
I'm trying to decide if the advantage(s) are so great as to justify modifying the code in my current projects to use the new std::source_location object in preference to the macros.
Your example is the best motivation I can think of. In the past __LINE__ and _FILE__ were the best excuse to use macros for debug logs. Now this does not hold anymore.
In the past you had basically two choiches. Either let the caller pass info on where the logging happens:
log(__LINE__,__FILE__,"Hello World");
or use a macro to get same output via
log("Hello World");
//^--- log must be a macro if we want this __LINE__ in the log
Both is very inconvenient, because either the user has to pass parameters that have to be the same anyhow on each call, or we need to use a macro with all the known downsides. With source_location we can get log("Hello World"); without any macros involved, but still include line and file of the call, because default parameters are substituted at the call site. For example:
#include <source_location>
#include <string>
#include <iostream>
void log(std::string message,const std::source_location& location = std::source_location::current())
{
std::cout << "Debug:"
<< location.file_name() << ':'
<< location.line() << ' '
<< message << '\n';
}
int main() {
log("hello");
log("world");
}
Output:
Debug:/app/example.cpp:15 hello
Debug:/app/example.cpp:16 world
Pre-C++20 you had to choose between being verbose (passing __LINE__, __FILE__, __func__ to each call manually) and using a macro to do that for you.
std::source_location gives you a nice calling syntax without a macro. There are no other hidden advantages.
Note that both Clang, GCC, and MSVC have the same non-standard extension that lets you do it pre-C++20: __builtin_LINE(), __builtin_FILE(), and __builtin_FUNCTION() that can be used as default arguments to achieve the same effect. You should be able to write your own class source_location using them as well.
The main advantage of std::source_location is mostly convenience, because it represents a source location with a single, easily storable structure.
It has also the advantage of not being based on preprocessor magic, so for the language it is not just an integer literal but a valid type, which is much nicer from the point of view of type-safety.
If you are fine with the older approach, there's no reason to change good, already working code. This is generally true for most new features though, not only for std::source_location.

Concatenating a string and passing as a parameter?

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

Assert with dynamic message?

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.

Macros as default param for function argument

I'm writing logger file. I'd prefer to use macros __FUNCTION__ there. I don't like the way like:
Logger.write("Message", __FUNCTION__);
Maybe, it's possible to make something like:
void write(const string &message, string functionName = __FUNCTION__)
{
// ...
}
If not, are there any ways to do that stuff not by hands (I mean passing function name)?
You could do something like that by wrapping it all in a macro:
#define log(msg) Logger.write(msg, __FUNCTION__)
The downside is that you will need to have Logger in scope when using this macro.
You can finally do it without macros magic in c++20:
void write(std::string_view message, const std::source_location& location =
std::source_location::current()) {
std::cout << "Message= " << message
<< ", Function=" << location.function_name();
}
int main() {
write("Hello world!");
}
Macros work just by text substitution - the preprocessor puts the macro definition in place of its name. You can't have "intelligent" macros like the one you suggest.
There is no support for what you want in standard C++.

What are preprocessor macros good for?

After reading another question about the use of macros, I wondered: What are they good for?
One thing I don't see replaced by any other language construct very soon is in diminishing the number of related words you need to type in the following:
void log_type( const bool value ) { std::cout << "bool: " << value; }
void log_type( const int value ) { std::cout << "int: " << value; }
...
void log_type( const char value ) { std::cout << "char: " << value; }
void log_type( const double value ) { std::cout << "int: " << value; }
void log_type( const float value ) { std::cout << "float: " << value; }
as opposed to
#define LOGFN( T ) void log_type( const T value ) { std::cout << #T ## ": " << value; }
LOGFN( int )
LOGFN( bool )
...
LOGFN( char )
LOGFN( double )
LOGFN( float )
Any other 'irreplaceables'?
EDIT:
trying to summarize the reasons-why encountered in the answers; since that's what I was interested in. Mainly because I have a feeling that most of them are due to us still programming in raw text files in, still, poorly supporting environments.
flexibility of code-to-be compiled (e.g. #ifdef DEBUG, platform issues) (SadSido, Catalin, Goz)
debug purposes (e.g. __LINE__, __TIME__); I also put 'stringifying' under this reason (SadSido, Jla3ep, Jason S)
replacing e.g. PHP's require vs. include feature (#pragma once) (SadSido, Catalin)
readability enhancement by replacing complicated code (e.g. MESSAGEMAP, BOOST_FOREACH) (SadSido, fnieto)
DRY principle (Jason S)
an inline replacement (Matthias Wandel, Robert S. Barnes)
stringifying (Jason S)
compile different code under different conditions ( #ifdef __DEBUG__ );
guards to include each header once for each translation unit ( #pragma once );
__FILE__ and __LINE__ - replaced by the current file name and current line;
structuring the code to make it more readable (ex: BEGIN_MESSAGE_MAP() );
See interesting macro discussion at gotw here:
http://www.gotw.ca/gotw/032.htm
http://www.gotw.ca/gotw/077.htm
Most useful - header file guarding:
#ifndef MY_HEADER_GUARD
#define MY_HEADER_GUARD
// Header file content.
#endif
Later add [Windows only]
Exporting classes to DLL:
#ifdef EXPORT_MY_LIB
#define MY_API __declspec( dllexport)
#else
#define MY_API __declspec( dllimport)
#endif
Sample class:
class MY_API MyClass { ... };
platform specific sections.
ie
#ifdef WINDOWS
#include "WindowsImplementation.h"
#elif defined( LINUX )
#include "LinuxImplementation.h"
#else
#error Platform undefined.
#endif
I've posted this before, but of course cannot now find it. If you want to access the __FILE__ and __LINE__ macros, then another macro is by far the most convenient way to go - for example:
#define ATHROW(msg) \
{ \
std::ostringstream os; \
os << msg; \
throw ALib::Exception( os.str(), __LINE__, __FILE__ ); \
}
For doing cool magic tricks like in BOOST_FOREACH, injecting variables into an ambit.
BOOST_FOREACH( char c, "Hello, world!" )
{
... use char variable c here ...
} // c's scope ends here
// if there's an outer c defined, its scope resumes here
For don't-repeat-yourself (DRY) reasons. Things that involve repeated constructs at compile-time which cannot be abstracted away in other methods (templates or what have you). If you are finding you're repeating the same code constructs 20 times, that's a potential source of human error -- which hopefully can be abstracted away using templates but sometimes not. It's always a balance between the advantages of seeing raw code that can be type-checked and reviewed clearly, vs. the advantages of using macros for arbitrary substitution patterns (that generally can't be checked by automatic programming tools).
Stringifying and concatenation (the # and ## preprocessor patterns) can't be performed by templates.
Of course, at some point you may be better off using a tool (whether custom or off-the-shelf) for automatic code generation.
Modern languages take the philosophy that needing a proeprocessor ins a sign of a missing language feature, so they define all kinds of language features that the preprocessor took care of very simply back in the old K&R style C.
Your code example above could be simplified via an inline function, for example.
Personally, the most indispensable aspect of a preprocessor is to make it clear that some things are done compile time right in the source code. The java approach of eliminating dead code paths at compile time is just not as obvious when reading the code.
One of their uses is basically as a poor mans ( inlined ) template function.
For example:
#define MIN(X,Y) ((X) < (Y) ? : (X) : (Y))
This allows you to generate a custom MIN function for any types supporting these operators which is effectively inline at the point of useage. Of course there is no type checking and it's easy to end up with weird syntax errors or incorrect behavior if you don't get the parens just right.