By r0ng


2018-11-08 22:16:49 8 Comments

In C++17, fold expression is available, so to print arguments, we could use

template<typename ...Args>
void output_argus(Args&&... args) 
{
    (cout << ... << args) << EOL;
}


int main()
{
    output_argus(1, "test", 5.6f);
}

having the output
1test5.6

What if I would like using the fold expression appending an extra character '\n' to each element to get the following results?

1
test
5.6

Is that even possible? If yes, how?

3 comments

@Yakk - Adam Nevraumont 2018-11-09 01:07:42

This is @n.m.'s solution without the rude global greedy operator<<.

template<class Os>
struct chain_stream {
  Os& stream;
  template<class Rhs,
    std::enable_if_t<std::is_same_v<Os&, decltype(std::declval<Os&>() << std::declval<Rhs>())>, bool> =true
  >
  friend chain_stream<Os> const& operator<<( chain_stream<Os> const& os, Rhs&& rhs ) {
    os.stream << std::forward<Rhs>(rhs);
    return os;
  }
  // iomanipulator:
  friend chain_stream<Os> const& operator<<( chain_stream<Os> const& os, Os&(*rhs)(Os&) ) {
    os.stream << rhs;
    return os;
  }
  template<class Rhs,
    std::enable_if_t<
      std::is_same_v< std::result_of_t< Rhs&&(chain_stream const&) >, void >
      || std::is_same_v< std::result_of_t< Rhs&&(chain_stream const&) >, Os& >,
    bool> =true
  >
  friend chain_stream<Os> const& operator<<( chain_stream<Os> const& os, Rhs&& rhs ) {
    std::forward<Rhs>(rhs)( os );
    return os;
  }
};

now we can do:

 (chain_stream{std::cout} << ... << [&](auto& x){x << args << '\n';});

and it works.

@n.m. 2018-11-08 23:09:40

I know that the comma operator is probably the easiest way to do that, but for completeness here's something I came up with, mainly because I wanted to show off my little generalisation of iomanip. The standard library iomanips are functions. There's an << overload that takes a function pointer. I extended that for arbitrary callable objects that take and return streams by reference.

template <class Stream, class Func>
auto operator << (Stream& s, Func f) -> 
        std::enable_if_t<std::is_same_v<decltype(f(s)), Stream&>, Stream&>
{
    return f(s);
}

With this little tool in our toolbox, it's easy to write a fold expression that does absolutely anything we want.

template<typename ...Args>
void output_args(Args&&... args)
{
     (std::cout << ... << [&](auto& x)->auto&{return x << args << '\n';});
}

This technique can be used in scenarios where we need to capture the value of the fold expression, rather than its side effects. The comma operator is less useful in such contexts.

@Yakk - Adam Nevraumont 2018-11-09 00:54:49

A free greedy << operator seems rude. Instead, I'd tag one of the types (the function or the stream).

@Yakk - Adam Nevraumont 2018-11-09 01:07:59

See my solution based off yours.

@n.m. 2018-11-09 05:43:39

@Yakk-AdamNevraumont the idea is to have function objects working exactly like plain old functions. Plain old functions are already working as io manipulators.

@Yakk - Adam Nevraumont 2018-11-09 12:20:36

yes, but the << overload for them has specific types. Yours will work on things itterly unrelates to ostreams. Possibly by accident, in the worst case cause build breaks due to non-sfinae errors.

@n.m. 2018-11-09 13:10:10

@Yakk-AdamNevraumont Yes, it is easy to restrict this to ostreams, didn't do this here to reduce verbosity.

@max66 2018-11-08 22:28:22

What if I would like using the fold expression appending an extra character '\n' to each element to get the following results?

You can use the power of the comma operator

 ((std::cout << args << std::endl), ...);

or, as suggested by Quentin (thanks) and as you asked, you can simply use \n instead of std::endl (to avoid multiple flushing of the stream)

 ((std::cout << args << '\n'), ...); 

@Quentin 2018-11-08 22:31:45

std::endl is completely overkill, OP just wants a newline.

@max66 2018-11-08 22:34:27

@Quentin - good point; added to the answer.

Related Questions

Sponsored Content

21 Answered Questions

[SOLVED] What is the "-->" operator in C++?

8 Answered Questions

[SOLVED] What is a lambda expression in C++11?

23 Answered Questions

0 Answered Questions

Fold expressions with operator >>

2 Answered Questions

[SOLVED] Fold expressions in MSVC

7 Answered Questions

3 Answered Questions

[SOLVED] Folding expressions in C++17 - Usecase for comparison operators

2 Answered Questions

8 Answered Questions

2 Answered Questions

Sponsored Content