7

In my code, I use variadic template functions for the logging purpose. But when I use std::endl as parameter, I get the following compiler error:

Error: no matching function for call to 'LOG_ERROR(const char [14], int&, )' LOG_ERROR("Sum of x+y = ", z, std::endl);

note: candidate: 'void LOG_ERROR()' inline void LOG_ERROR() {

note: candidate expects 0 arguments, 3 provided

My Code:

#include <iostream> inline void LOG_ERROR() { std::cout << std::endl; } template<typename First, typename ...Rest> void LOG_ERROR(First && first, Rest && ...rest){ std::cout << std::forward<First>(first); LOG_ERROR(std::forward<Rest>(rest)...); } int main() { int foo=40; LOG_ERROR("My foo = ", foo, std::endl); } 

The code works fine with "\n" but I would love to learn why it fails with std::endl and how I can fix it

4
  • 2
    The issue is also discussed here. I don't think it's a duplicate though, as your question involves passing std::endl in parameter pack to be expanded?! Commented Aug 16, 2018 at 7:10
  • 3
    std::endl is a function template, not just a function. You're passing it without deduction (either specific or deduced). If you populated the template to an actual function instantiation (such as std::endl<char, std::char_traits<char>>, it should work. Commented Aug 16, 2018 at 7:14
  • @WhozCraig The thing here is I won't be the user of the LOG_ERROR function but I would like the user to be able to use the function with std::endl. Commented Aug 16, 2018 at 7:25
  • 1
    @eneski I understand, but I don't know what to tell you. The problem is the context in which it is used. There is no "there" there until such time as std::endl is used in a deducible context, which it isn't in your case. Commented Aug 16, 2018 at 7:36

4 Answers 4

8

Long story short - std::endl is function template which template arguments can't be deduced while passing. You can help Your compiler this way:

LOG_ERROR("My foo = ", foo, std::endl<char, std::char_traits<char>>); 

As much as I feel this is ugly piece of code it works perfectly.

Sign up to request clarification or add additional context in comments.

5 Comments

auto endl = std::endl<char, std::char_traits<char>>; helps.
@YSC If I were to choose, I'd go with suggested somwhere down here: auto endl = [](auto&&os) -> decltype(auto) { return os << std::endl; }
Don't do this. It doesn't "work perfectly" - it's unspecified. The standard library reserves the right to break this code at any time - you should not take the address of or explicitly specify standard library functions/function templates. You have to use a lambda (or normal function object).
@Barry Could You reference to standard? I never heard of such a restriction
@bartop [namespace.std]. I guess technically since endl is an addressable, non-member function template, it's safe in this case as covered by [global.functions] but it's still nicer to just avoid it entirely.
7

Until someone comes with a better solution, you can use a trivial wrapper with an appropriate operator overload:

struct EndlWrap {}; std::ostream& operator << (std::ostream& os, EndlWrap) { return os << std::endl; } 

which should be usable like this:

LOG_ERROR("My foo = ", foo, EndlWrap{}); 

This has an advantage when your logging destination might be a non-standard stream, i.e., the template arguments of std::endl can still be deduced when it's <<'d into the stream.

8 Comments

Possibly: auto EndlWrap = [](auto&&os) -> decltype(auto) { return os << std::endl; }. it works also with std::wcout. (or templatize your function).
That's nice!! The question is tagged C++11, though.
So fallback to old functor class.
This would work, too: auto EndlWrap = [](std::ostream& os) -> std::ostream& { return os << std::endl; };
Right... functor class with a << template seems to be the way to go then.
|
1

std::endl is not a character type or any other type. It is output stream manipulator. Its return type is output stream.

So, you can not pass it without typecasting. Please look here

Comments

1

You can use defaulted template parameters and defaulted function arguments instead of a variadic template.

The code is less clean and you will have to choose a limitation on the number of parameters, but it will do the job:

template<class...> inline void LOG_ERROR_(); template<> inline void LOG_ERROR_<>() { std::cout << std::endl; } template<typename First, typename ... Rest> void LOG_ERROR_(First && first, Rest && ...rest){ std::cout << std::forward<First>(first); LOG_ERROR_<Rest...>(std::forward<Rest>(rest)...); //could be cleaner with if constexpr } using manip_t = std::basic_ostream<char>&(*)(std::basic_ostream<char>&); std::basic_ostream<char>& no_manip(std::basic_ostream<char>& o){ return o; } template<typename A=manip_t ,typename B=manip_t, typename C= manip_t ,typename D=manip_t // to be continued > inline void LOG_ERROR(A&& a=no_manip, B&& b=no_manip,C&& c=no_manip ,D&& d=no_manip /*to be continued*/){ LOG_ERROR_<A,B,C,D/*to be continued*/>( std::forward<A>(a),std::forward<B>(b),std::forward<C>(c), std::forward<D>(d)/*to be continued*/); } 

Depending on the compiler this code could produce ugly assembly. One solution is to write an overload for each possible number of argument, or have a good knowldge of compiler specific function attributes (always_inline,etc...)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.