overriding operator () in class

Hi,

I am trying to finish a logging class and I am having trouble overriding the operator () (LogLevel leve). Basically, I open a text stream to a file stream data into the file using a singleton class for cross threading.

Here is my format for creating the class in one of the threads (getting the singleton instance) and streaming data into the class.

1
2
LOG = LoggerStream::instance();
LOG(LOG_DEBUG3) << "Application started\n";


where the () operator takes in the log level and prints the time and the log level on every message input

Here's the override method in the class:
 
LoggerStream& LoggerStream::operator ()(LogLevel level);


The the operator override method streams the current time and log level before the << message is streamed into the file. where,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
LoggerStream& LoggerStream::operator () (LogLevels level)
{
    // check if file is open
    if (!_isOpen)
        return *this;

    // create date / time stamp
    QTime time = QTime::currentTime();
    QDateTime dateTime = QDateTime::currentDateTime();

    // create log string including the log level output
    QString headerString = dateTime.date().toString() + " " + QVariant(time.hour()).toString() + " " + QVariant(time.minute()).toString()
            + " " + QVariant(time.second()).toString() + " " + QVariant(time.msec()).toString() + " " + logLevelString(level);

    // stream the data into the QTextStream
    QString fileText = QString("[") + headerString + QString("]: ");
    streamData(fileText);
    return *this;
}


so for example:
LOG(LOG_DEBUG3) << "Hello";

output:
[Aug 22nd 2013 9:31:22:0988473 LOG_DEBUG3] Hello

For some reason I am getting a compilation error at the LOG() operator:
error: C2064: term does not evaluate to a function taking 1 arguments

Is there anything wrong with using the () operator like I am to prepend to the message when the log level input?
Last edited on
I don't understand, what is LOG? An instance of LoggerStream? And then you assign the singleton instance to this object? Singleton classes are usually not copyable.
Hi,

Thank you for the reply. LoggerStream is a singleton class that has all of the operator overrides.

Log is the instance of the singleton class (initialized in my main.cpp, and I would like my sub classes to use the same instance). The singleton class is located in a .dll so that multiple threads / .exe's can access the same instance of the class (mutex's exist).

The format of the log message I am looking for is:
LOG(LOG_LEVEL) << "message to stream in";

Where LOG is the singleton instance, and LOG_LEVEL is an enum of the log levels.

I thought that the () operator override would allow me to pass in the LOG_LEVEL in the parenthesis, but I am getting the compilation error as shown above...

I have already overwritten the << operator for all of the different types of streams into the class including QString, QVariant, ect.

If it would help, I could post all of the code. It is a Qt class in a dll using a QTextStream and QFile for the streaming.

Is my implementation of the () operator incorrect?

Thank you!
closed account (o1vk4iN6)
Why not just do something like :

 
LOG << LOG_DEBUG3 << "Application started";


At least that's how the standard library allows you modify some of the streams, ie using hex or dec for numbers:

 
std::cout << hex << 11;


If for some reason you want output to do something like this:

1
2
3
4
5
LOG(LOG_DEBUG3) << "some message";

// do some calculations

LOG(LOG_DEBUG3) << "this message should be continuous with the previous one";

Last edited on
Is my implementation of the () operator incorrect?

Looks ok to me.

But what type is LOG? Pointer? Reference? ??

My toy version below works ok :-)

[16:33:50 ERROR]: Error message
[16:33:50 WARN ]: Warning message
[16:33:50 INFO ]: Informational message : 1 + 1 = 2
[16:33:50 DEBUG]: Debug message


But it follows the lead of your posted code and does nothing to control whether or not output gets written (i.e. does not check the logging level, etc.)

Also, is LoggerStream derived from std::ostream ??

Andy

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <iostream>
#include <sstream>
#include <string>
#include <ctime>
using namespace std;

enum LogLevel {
    ERROR,
    WARN,
    INFO,
    DEBUG
};

const char* logLevelString(int level) {
    switch(level) {
        case ERROR: return "ERROR";
        case WARN : return "WARN ";
        case INFO : return "INFO ";
        case DEBUG: return "DEBUG";
        default: { /* do nothing */ }
    };

    return "???";
}

class LoggerStream {
private:
    int m_currentLevel;

    LoggerStream() : m_currentLevel(WARN) {
    }

public:
    static LoggerStream& instance() {
        static LoggerStream s_logStrm;
        return s_logStrm;
    }

    int getLogLevel() const {
        return m_currentLevel;
    }

    int setLogLevel(int level) {
        swap(m_currentLevel, level);
        return level;
    }

    LoggerStream& operator () (LogLevel level);

    template<typename TData>
    void streamData(const TData& data) {
        cout << data;
    }
};

LoggerStream LOG = LoggerStream::instance();

template<typename TData>
LoggerStream& operator<<(LoggerStream& logStrm, const TData& data) {
    ostringstream oss;
    oss << data;
    logStrm.streamData(oss.str());
    return logStrm;
}

LoggerStream& operator<<(LoggerStream& logStrm, const char* data) {
    logStrm.streamData(data);
    return logStrm;
}

LoggerStream& operator<<(LoggerStream& logStrm, ostream& (*pfn)(ostream&)) {
    logStrm.streamData("\n"); // cheat (assume endl only manip we'll see)
    return logStrm;
}

LoggerStream& LoggerStream::operator() (LogLevel level) {
    time_t timeNow = time(0);
    struct tm* ptm = gmtime(&timeNow);
    char timeString[32] = "";
    strftime(timeString, sizeof(timeString)/sizeof(char), "%H:%M:%S", ptm);

    string fileText = "[";
    fileText += timeString;
    fileText += " ";
    fileText += logLevelString(level);
    fileText += "]: ";

    streamData(fileText);
    return *this;
}

int main() {
    LOG(ERROR) << "Error message"   << endl;
    LOG(WARN)  << "Warning message" << endl;
    LOG(INFO)  << "Informational message : 1 + 1 = " << (1 + 1) << endl;
    LOG(DEBUG) << "Debug message"   << endl;

    return 0;
}
Last edited on
closed account (o1vk4iN6)
merely suggestioning a different approach to what you want to do.
Hi.

My implementation is very similar to andywestken's example without the log level. Also, I am writing this in Qt (but I thought this was a general c++ question regarding operators, thats why I came here).

Here is my class code (it is in a dll, so thats why you will see the export on the top). I added the ostream operator, I like the endl addition you did in your example.

http://pastebin.com/CXa0vKi9

For some reason when I build, I am getting the following compilation error when implementing:
For some reason I am getting a compilation error at the LOG() operator:
error: C2064: term does not evaluate to a function taking 1 arguments

Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    // create logger class
    LoggerStream *LOG = LoggerStream::instance();
    LOG->setFileName(fileName);
    if (!LOG->open()){
        QMessageBox err;
        err.setWindowTitle("Error");
        err.setText("Error opening streaming log file");
        err.setInformativeText("Logging will not be enabled");
        err.exec();
    }

    // create logger file
    LOG = LoggerStream::instance();
    LOG(LOG_DEBUG3) << QString("Multi Process Data Collection application started") << "\n";


I see in your example, you have a reference to self in all of your operator overloads as your first parameter and you do not have a definition for each operator in your class header as public.

Is this needed?

Thank you again!
Last edited on
If LOG is a pointer you will have to dereference the pointer first. (*LOG)(LOG_DEBUG3)
As Peter87 has already said, operator() is defined for LoggerStream references, not pointers, so you need to dereference the pointer before using operator()

You could, of course, use a macro to keep the usage tidy, such as

1
2
3
LoggerStream* plog_ = LoggerStream::instance();

#define LOG(LEVEL) if(0 != plog_) (*plog_)(LEVEL) 


But once you're using a macro, you might as well use a normal function rather than operator(), and could even pass the file name and line number to it.

Where LogStream has a method

LogStream& getStream(LogLevel level, const char* filePath, int lineNum);

the macro could become

#define LOG(LEVEL) if(0 != plog_) plog_->getStream(LEVEL, __FILE__, __LINE__)

I see in your example, you have a reference to self in all of your operator overloads as your first parameter and you do not have a definition for each operator in your class header as public.

Is this needed?

No, it's just a stylistic thing. Coding them as member functions is equally valid.

But I do usually code my insertion operators that way (as non-member functions) if I have a suitable method for the insertion operator to call to do the real work as it makes the class body less cluttered.

I like the endl addition you did in your example.

Actually, it shouldn't be needed. If you derive your LoggerStream from std::ostream then you will get the handling of all the built-in types, std::string, and io manipulators as part of the deal.

Andy
Last edited on
I like the macro.

The macro would also require that the instance name is LOG? It might be better to remove the pointer so that I don't have to use that macro everywhere I use the singleton.

Dereferencing the pointer worked! Thank you so much!
Last edited on
The macro would also require that the instance name is LOG?

The instance names I'm using is plog_

LOG is the name of the macro.

It might be better to remove the pointer so that I don't have to use that macro everywhere I use the singleton.

I don't quite understand this. Do you mean you'll change you code to use a reference rather than a pointer?

Andy
I ended up keeping the pointer. The singleton class works great with the macro. I was a little confused with 'the big picture' of how the macro would be implemented while having multiple threads access the singleton (plus it was sunday, I have a hard time getting my brain going :) ).

I thought I would need to get a pointer to the singleton instance in every thread and use the pointer, but the beauty of the macro made it so I didn't have to do that :)

I do have one other question regarding the operators though.

Is there a way to tell what is the LAST operator being executed on a single line? I am trying to come up with an elegant way to make is so a 'endl' or '\n' does not have to be included at the end of every LOG input.

so right now I have to,
 
LOG(LOG_DEBUG3) << "Some log text" << std::endl;


I would really like,
 
LOG(LOG_DEBUG3) << "Some log text"; // automatically append std::endl 


It is known that in every log statement there will be the LOG(LOG_LEVEL), so I was going to start a sequence in the () operator, but there has to be some blocking until all of the operator overrides on that line have completed.

And if I put an endl at the end of all of the << operator overrides, then I will not be able to chain << on the same line...
Is there a way to tell what is the LAST operator being executed on a single line?

I didn't try to solve that problem as I want to be able to code stuff like

1
2
3
4
LOG(LOG_DEBUG3) << "Some log text";
if(extra_stuff_about) 
    LOG(LOG_DEBUG3) << " (more stuff");
LOG(LOG_DEBUG3) << std::endl;


and have it logged on one line.

I did wonder about returning a proxy object, rather than just the stream pointer from the logger which would automatically trigger a line end (if pending) when it went out of scope. (But didn't get round to doing it.)

Andy
Last edited on
Topic archived. No new replies allowed.