What Is the Easiest Way to Print a Variadic Parameter Pack Using Std::Ostream

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



Leave a reply



Submit