Ostream to sockets

I'm trying to write a class, similar to a std::ostream object which allows me to:
A) Construct with a name
B) Track log level
C) Prepend name, time, and log level to messages
D) Send messages to a remove server (instead of sending to stdout or a file)

Something like this could work:
1
2
3
4
5
6
7
8
template<class T> 
Logger::Logger& operator<<(const T& output)
{
  std::stringstream iss;
  iss << m_name << m_warningLevel << currentTime() << output;
  socket.send(iss.str());
  return *this;
}


However, with C++ style logs, the following would result in a billion disjointed messages:
log << "Value is: " << val1 << std::endl;

I'd like to intercept a std::endl or std::flush and use that to identify the end of a message and to send it.

I've been looking into how to re-implement std::streambuf, but I've gotten a bit lost there. Plus std::streambuf::pubsync (which is called by std::fill) is not a virtual function that I can override so I don't think that's the solution either.

This is the code I have so far, but I think I'm on the wrong track. Line 34 is where I was trying to intercept the std::flush. Obviously it would be better if I could inherit something like a streambuf or ostream, then override flush(), but that function isn't virtual.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <iostream>
#include <sstream>
#include <string>

// Usage: 
// Logger log("Module's Name");
// log << "Text or double or anything really" << std::endl;
// log(Logger::WARN) << "Some warning message" << std::endl;

class Logger
{
public:
  enum LogLevel
  {
    INFO,
    WARN,
    ERROR
  };

  Logger(std::string name) 
    : m_moduleName(name),
      m_logLevel(INFO)
    {}

  template<class T> 
  Logger& operator<<(const T& output)
  { // (int, double, strings, etc.)
    m_stream << output;
    return *this;
  }

  Logger& operator<<(std::ostream& (*manip)(std::ostream &))
  { // endl, flush, setw, setfill, etc.
    // if (manip == std::flush) this->flush();
    manip(m_stream);
    return *this;
  }

  Logger& operator<<(std::ios_base& (*manip)(std::ios_base&))
  { // setiosflags, resetiosflags
    manip(m_stream);
    return *this;
  }

  Logger& operator() (LogLevel e)
  {
    m_logLevel = e;
    return *this;
  }
  
  void flush()
  {
    std::stringstream iss;

    switch (m_logLevel)
    {
    case WARN:   iss << "WARN : [";  break;
    case ERROR:  iss << "ERROR: [";  break;
    case INFO:
    default:     iss << "INFO : [";  break;
    }
    iss << m_moduleName << "] " 
        << currentTime() << ": "
        << m_stream.str();

    socket.send(iss.str()); 
    
    m_stream.clear();  
    m_stream.str(std::string());
    m_logLevel = INFO;
  }
  
private:
  const std::string  m_moduleName;
  std::stringstream  m_stream    ;
  int                m_logLevel  ;

  // Placeholder
  double currentTime() { return 0.0; } 

  struct Socket
  { // Placeholder until I get this part done.
    void send(const std::string& s) { std::cout << s; }
  } socket;
};


If I uncomment line 34, I get
$ g++ -o test test.cpp
In file included from test.cpp:1:0:
logger.h: In member function ‘Logger& Logger::operator<<(std::ostream& (*)(std::ostream&))’:
logger.h:34:23: error: assuming cast to type ‘std::basic_ostream<char>& (*)(std::basic_ostream<char>&)’ from overloaded function [-fpermissive]
     if (manip == std::flush) this->flush();
                       ^

If I use -fpermissive, I get:
$ g++ -o test test.cpp -fpermissive
In file included from test.cpp:1:0:
logger.h: In member function ‘Logger& Logger::operator<<(std::ostream& (*)(std::ostream&))’:
logger.h:34:23: warning: assuming cast to type ‘std::basic_ostream<char>& (*)(std::basic_ostream<char>&)’ from overloaded function [-fpermissive]
     if (manip == std::flush) this->flush();
                       ^

But in that case, the if condition is never true.
Last edited on
Stupid mistake, I forgot to handle std::endl explicitly.

This works fine:
1
2
3
4
5
6
7
  Logger& operator<<(std::ostream& (*manip)(std::ostream&))
  { // endl, flush, setw, setfill, etc.
    manip(m_stream);
    if (manip == std::flush || manip == std::endl) 
        this->flush();
    return *this;
  }


Though I DO need to use -fpermissive to avoid the error. And since I'm supposed to use -Werror (treat all warnings as errors) I have a problem. Does anyone have an idea about how to avoid the warning?
$ g++ -o test test.cpp -fpermissive
In file included from test.cpp:1:0:
logger.h: In member function ‘Logger& Logger::operator<<(std::ostream& (*)(std::ostream&))’:
logger.h:35:23: warning: assuming cast to type ‘std::basic_ostream<char>& (*)(std::basic_ostream<char>&)’ from overloaded function [-fpermissive]
     if (manip == std::flush || manip == std::endl)
                       ^
logger.h:35:46: warning: assuming cast to type ‘std::basic_ostream<char>& (*)(std::basic_ostream<char>&)’ from overloaded function [-fpermissive]
     if (manip == std::flush || manip == std::endl)

Solved it! No warnings now!

1
2
3
4
5
6
7
8
9
10
11
12
  typedef std::ostream& (*ManipFn)(std::ostream&);

  Logger& operator<<(ManipFn manip)
  { // endl, flush, setw, setfill, etc.
    manip(m_stream);

    if (   manip == static_cast<ManipFn>(std::flush) 
        || manip == static_cast<ManipFn>(std::endl ) 
       )
        this->flush();
    return *this;
  }

Last edited on
Topic archived. No new replies allowed.