How to make a variadic macro for std::cout?
You do not need preprocessor macros to do this. You can write it in ordinary C++. In C++11/14:
#include <iostream>
#include <utility>
void log(){}
template<typename First, typename ...Rest>
void log(First && first, Rest && ...rest)
{
std::cout << std::forward<First>(first);
log(std::forward<Rest>(rest)...);
}
int main()
{
log("Hello", "brave","new","world!\n");
log("1", 2,std::string("3"),4.0,'\n');
}
Live demo
In C++17:
template<typename ...Args>
void log(Args && ...args)
{
(std::cout << ... << args);
}
is all it takes. Live demo
Output:
Hellobravenewworld!
1234
Research variadic templates, parameter packs and fold expressions rather than variadic macros, which are rarely useful in modern C++.
When using variadic macros you need __VA_ARGS__
to expand all the arguments.
The problem however is that those arguments are comma-separated. Presumabely it just separates arguments to a function call, but since macros works with just text you can actually use __VA_ARGS__
in other contexts as well, where a comma-separated list makes sense.
The trick you can employ is to define your own comma operator for std::ostream
(the type of std::cout
). For example:
#include<iostream>
#define LOG(...) std::cout , __VA_ARGS__ , std::endl
template <typename T>
std::ostream& operator,(std::ostream& out, const T& t) {
out << t;
return out;
}
//overloaded version to handle all those special std::endl and others...
std::ostream& operator,(std::ostream& out, std::ostream&(*f)(std::ostream&)) {
out << f;
return out;
}
int main() {
LOG("example","output","filler","text");
return 0;
}
Now, the LOG invocation will expand to:
std::cout , "example" , "output" , "filler" , "text" , std::endl;
and the commas will invoke the overloaded comma operators.
If you don't like overloaded operator,
polluting all std::ostream
-s, you can encapsulate std::cout
with your own special logger class.
Not sure there is any way of defining a variadic macro in C++ (at least, not a portable way). Why don't you use a variadic template approach? Something like
#include <iostream>
void LOG() {}
template<typename Head, typename... Args>
void LOG(const Head& head, const Args&... args )
{
std::cout << head << " ";
LOG(args...);
}
int main()
{
LOG("This", "is" , "the", 3, "rd test");
}