8

I tried to do logger using C++11 variadic templates, but it doesn't work for std::endl, because std::endl is template function and the compilator doesn't know what specialization of std::endl to select. Is there any way how i can force to always select std::endl<char, std::char_traits<char>>? If possible, i want to use directly std::endl.

EDIT: it looks like it is not currently possible with C++11 and and best way is to use #define or what vsoftco answered.

#include <iostream>
#include <string>

class Logger {

public:

    template<typename T>
    void log(T val);

    template <typename T, typename ...Args>
    void log(T val, Args... args);

};

// explicit specialization not working 
template<>
void Logger::log(std::basic_ostream<char, std::char_traits<char>> (*modifier) (std::basic_ostream<char, std::char_traits<char>>)) {

    std::cout << modifier;

}

template<typename T>
void Logger::log(T val) {

    std::cout << val;

}

template<typename T, typename ...Args>
void Logger::log(T val, Args... args) {

    log(val);
    log(args...);

}

int main(int argc, char* argv[])
{   
    Logger log;

    log.log("Nazdar ", "bazar ", "cau", std::endl, "kik"); // ERROR: cannot determine which instance of function template "std::endl" is intended
    log.log("Nazdar ", "bazar ", "cau", std::endl<char, std::char_traits<char>>, "kik");

    std::cin.get();

    return 0;
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
Krab
  • 6,526
  • 6
  • 41
  • 78

2 Answers2

6

A simpler option to achieve the same goal:

// global or class member
enum MyEndl { my_endl };

// class member function
void log(MyEndl x) { std::cout << std::endl; }

usage:

log.log("Nazdar ", "bazar ", "cau", my_endl, "kik");
M.M
  • 138,810
  • 21
  • 208
  • 365
  • this is also neat! +1 But what about sending `0` to `log.log(...)`? You don't use an `enum class`, won't you get an ambiguity? I mean, you won't display `0`, but the `enum` will be a better match. I'm temporary retracting the +1 :) – vsoftco Mar 29 '15 at 01:31
  • @vsoftco I don't get any ambiguity when compiling, and it selects the template version for `0` (altho not sure of the exact rules) – M.M Mar 29 '15 at 01:36
  • No, I think the `enum` will be a better match, but what if you want to display `0`? Your solution must use "0" instead of the integer `0`. Of course, you can use some crazy improbable number like `-18947618723` as the start of the enum. – vsoftco Mar 29 '15 at 01:37
  • 1
    ok, put back the +1, I think the `template` is preferred vs the `enum`, always forget these rules... I think the `enum` has a type, even if implicitly convertible to `int`, is not the same as `int`, so `0` ends up being a better match for the template. – vsoftco Mar 29 '15 at 01:42
3

I came up with this, basically re-defining std::endl via a custom wrapper my_endl taking default template parameters. Not the most elegant, but it does the job. Of course, for more such manipulators, one should write a specialized wrapper, but I guess even this can somehow be possible by a more clever implementation.

#include <iostream>
#include <string>
#include <type_traits>

class Logger {

public:

    template<typename T>
    void log(T val);

    template <typename T, typename ...Args>
    void log(T val, Args... args);
};

template<typename T>
void Logger::log(T val) {
    std::cout << val;
}

template<typename T, typename ...Args>
void Logger::log(T val, Args... args) {

    log(val);
    log(args...);

}

template< class CharT = char, class Traits = std::char_traits<CharT> >
inline std::basic_ostream<CharT, Traits>& my_endl( std::basic_ostream<CharT, Traits>& os )
{
    return std::endl(os);
} 

// or, use the excellent (and better) suggestion by 0x499..., 
// auto manip = std::endl<char, std::char_traits<char>>; 
// log.log(..., manip)


int main(int argc, char* argv[])
{
    Logger log;

    // log.log("Nazdar ", "bazar ", "cau", std::endl, "kik"); // ERROR: cannot determine which instance of function template "std::endl" is intended
    log.log("Nazdar ", "bazar ", "cau", my_endl<>, "kik");

    std::cin.get();

    return 0;
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • just want to mention that @0x499... answer is more elegant and slightly better, since it avoids the indirection of an additional function call (of course, can make mine inline, but you should really use `auto` in this case). – vsoftco Mar 29 '15 at 00:50
  • @i actually wanted to use that `std::endl`, but probably it isn't possible. In that case `#define` will be maybe better way, cause it doens't allocate space. – Krab Mar 29 '15 at 00:54
  • @Krab yeah, I don't think is possible (although never say never) to use `std::endl`. At least, cannot see how to do it... – vsoftco Mar 29 '15 at 00:55
  • @Krab you mean `#define std::endl std::endl<...>`? – vsoftco Mar 29 '15 at 01:02