What is the easiest way to print a variadic parameter pack using std::ostream?
Without recursive calls and commas where you wanted.
In c++11 / c++14 through parameter pack expansion:
template <typename Arg, typename... Args>
void doPrint(std::ostream& out, Arg&& arg, Args&&... args)
{
out << std::forward<Arg>(arg);
using expander = int[];
(void)expander{0, (void(out << ',' << std::forward<Args>(args)), 0)...};
}
DEMO
In c++17 using fold expressions:
template <typename Arg, typename... Args>
void doPrint(std::ostream& out, Arg&& arg, Args&&... args)
{
out << std::forward<Arg>(arg);
((out << ',' << std::forward<Args>(args)), ...);
}
DEMO 2
Parameter pack expansion not compiling in c++
Your way makes std::cout.operator<< (oneoperand)
be std::cout.operator<<( operand1, operand2, ...)
. You should use a thing like the following
template <class... Types>
struct Test {
Test(const Types &... args) {
(std::cout << ... << args) << std::endl;
}
};
C++14 variadic function template expect 2 arguments - 0 provided
Your calls to print
will be like this:
print(1, 2, 3);
print(2, 3);
print(3);
print();
Your template function needs at least one argument (var1
), and there is no function that takes zero arguments.
This can easily be solved by adding another function overload to handle the "no argument" case:
void print()
{
// Do nothing
}
Now this will be called in the
print();
case, and will end the recursion.
If C++17 is possible, then fold expressions are available and you could do it with only a single function:
template <typename... Types>
void print(Types&&... var)
{
((std::cout << var << "\n"), ...);
}
Variadic template pack expansion
One of the places where a pack expansion can occur is inside a braced-init-list. You can take advantage of this by putting the expansion inside the initializer list of a dummy array:
template<typename... Args>
static void foo2(Args &&... args)
{
int dummy[] = { 0, ( (void) bar(std::forward<Args>(args)), 0) ... };
}
To explain the content of the initializer in more detail:
{ 0, ( (void) bar(std::forward<Args>(args)), 0) ... };
| | | | |
| | | | --- pack expand the whole thing
| | | |
| | --perfect forwarding --- comma operator
| |
| -- cast to void to ensure that regardless of bar()'s return type
| the built-in comma operator is used rather than an overloaded one
|
---ensure that the array has at least one element so that we don't try to make an
illegal 0-length array when args is empty
Demo.
An important advantage of expanding in {}
is that it guarantees left-to-right evaluation.
With C++17 fold expressions, you can just write
((void) bar(std::forward<Args>(args)), ...);
Using std::endl in ostream ' '-operator overload with variadic template class containing a variant member leads to compiler error
std::endl
is a function template, so has overloads.
With
std::cout << std::endl;
we have to take (correct) address of an overloaded function.
For this, we have to see between each operator<<
is there is some which constraint the signature.
There is indeed the ones from basic_stream
, in particular
basic_ostream& operator<<(
std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&) );
(which is the expected best match).
but we also have to check other, in particular, the problematic one:
template<typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<VARIANT_TYPES...>& inst);
and as VARIANT_TYPES
would not be deducible from function set, VARIANT_TYPES
is empty pack.
Now checking if VariantContainer<>(std::endl)
would force signature of the function, so instantiate VariantContainer<>
constructors which does hard error due to std::variant<>
.
Now possible solutions should avoid hard error with
template<typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<VARIANT_TYPES...>& inst);
providing (valid or even incomplete) specialization for class
VariantContainer<>
Changing primary template to
template <typename T, typename... Ts>
class VariantContainer;
(no need to change operator <<
, as SFINAE will apply to invalid VariantContainer<>
)
- Changing
operator<<
to
template <typename T, typename... VARIANT_TYPES>
std::ostream& operator<<(std::ostream& os, const VariantContainer<T, VARIANT_TYPES...>& inst);
Why doesn't C++ variadic template accept iostream value as a parameter?
It's simply, because copy constructor in std::basic_ostream
is deleted (by standard)
read about this here at cppreference.
So std::cout
you can move or take by reference
example:
class A {
public:
A(const A& a) = delete;
};
void foo1(const A& a)
{
std::cout << "HEY1" << std::endl;
}
void foo2(const A a)
{
std::cout << "HEY2" << std::endl;
}
int main() {
A a{};
foo1(a);
// foo2(a);
return 0;
}
So in example you see that first function work fine when copy constructor is deleted, but if you uncomment foo2(a)
, that you get some information from compile e.g. (VS)
'A::A(const A &)': attempting to reference a deleted function
Specializing macro with arguments
Macros are mostly a bad choice in C++ – essentially because of they are namespace agnostic and may take effect where it is unexpected.
That said – a sample for how OPs issue could be solved e.g. using token pasting:
#include <iostream>
#define LOG(LEVEL, ...) LOG_##LEVEL(__VA_ARGS__)
#define LOG_INFO(...) log(Info, __VA_ARGS__)
#define LOG_WARN(...) log(Warn, __VA_ARGS__)
#define LOG_FATAL(...) do { log(Error, __VA_ARGS__); std::cerr << "Program aborted!\n"; } while (false)
enum Level { Info, Warn, Error };
void log(Level level, const char *text)
{
static const char *levelText[] = { "INFO", "WARNING", "ERROR" };
std::cerr << levelText[level] << ": " << text << '\n';
}
int main()
{
LOG(INFO, "Everything fine. :-)");
LOG(WARN, "Not so fine anymore. :-|");
LOG(FATAL, "Things became worst. :-(");
}
Output:
INFO: Everything fine. :-)
WARNING: Not so fine anymore. :-|
ERROR: Things became worst. :-(
Program aborted!
Live Demo on coliru
Another sample for the follow-up question - with variadic templates instead of macros:
#include <iostream>
enum Level { Info, Warn, Error, Fatal };
template <typename ...ARGS>
void log(Level level, ARGS&& ... args)
{
static const char *levelText[] = { "INFO", "WARNING", "ERROR", "FATAL" };
std::cerr << levelText[level] << ": ";
(std::cerr << ... << args);
std::cerr << '\n';
if (level == Fatal) std::cerr << "Program aborted!";
}
int main()
{
log(Info, "Everything fine.", ' ', ":-)");
log(Warn, "Not so fine anymore.", ' ', ":-|");
log(Error, "Things became bad.", ' ', ":-(");
log(Fatal, "Things became worst.", ' ', "XXX");
}
Output:
INFO: Everything fine. :-)
WARNING: Not so fine anymore. :-|
ERROR: Things became bad. :-(
FATAL: Things became worst. XXX
Program aborted!
Live Demo on coliru
I must admit that I needed some help for the parameter unpacking – found here:
SO: What is the easiest way to print a variadic parameter pack using std::ostream?.
How to expand parameter pack for dependent types?
You have to use the keyword typename
because value_type
is a dependent name in this context.
template <typename ... T>
struct aggregate_of_foo
{
typedef std::tuple<typename foo<T>::value_type...> tuple; //here
};
Related Topics
How to Sort an Stl Map by Value
Overloading Operator<<: Cannot Bind Lvalue to 'Std::Basic_Ostream<Char>&&'
Using Class Name in a Class Template Without Template Parameters
How to Use Standard Library (Stl) Classes in My Dll Interface or Abi
How to Check If an Object's Type Is a Particular Subclass in C++
Calling a Function for Each Variadic Template Argument and an Array
What Is the Meaning of "... ..." Token? I.E. Double Ellipsis Operator on Parameter Pack
Overriding Public Virtual Functions with Private Functions in C++
C++ Function Pointer (Class Member) to Non-Static Member Function
Passing Arguments to Std::Async by Reference Fails
Cpp - Valgrind - Invalid Read of Size 8
Which C++ Standard Is the Default When Compiling with G++
Cancelling a Thread Using Pthread_Cancel:Good Practice or Bad