Overload << Operator with Input Parameter

Hi,

Here is what I am looking to do:
1
2
3
4
5
6
// class constructor as log file input
Logger log(file_name);
// input argument as the log level
// stream overload - log text
log(LOG_DEBUG) << "log text here";
// LOG_DEBUG is an enum in the header 


I have everything working in Qt using a QTextStream for the output stream to send the data to the log file. The only issue that I am having is the input argument to the log statement (LOG_DEBUG), in which I am unsure in how to accomplish this. I know this isn't a Qt forum, but I feel like this is a general c++ question.

Here is my Qt code so far:
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
#include <QObject>
#include <QTextStream>
#include <QFile>
#include <QDebug>

enum LogLevels {
    LOG_WARNING = 0,
    LOG_INFO,
    LOG_DEBUG
};

class Logger : public QObject
{
    Q_OBJECT

public:
    Logger(QString file_name){
        file = new QFile(file_name);
        if (!file->open(QIODevice::WriteOnly)){
            qDebug() << "failed to open file";
        }
        out_stream = new QTextStream(file);
    }

    ~Logger()
    {
        file->close();
        delete file;
        delete out_stream;
    }

    template <typename T>
    friend Logger& operator<< (Logger& logger, T thing) {
        *logger.out_stream << thing;
        *logger.out_stream << "\n";
        logger.out_stream->flush();
        return logger;
    }

    friend Logger& operator<< (Logger& logger, QVariant thing) {
        *logger.out_stream << thing.toString();
        *logger.out_stream << "\n";
        logger.out_stream->flush();
        return logger;
    }

private:
    QTextStream* out_stream;
    QFile *file;

};


This code works as follows:
1
2
Logger log("/home/path/to/logfile.txt");
logger << "Here is some log text to stream";


Could anyone point me in the right direction? I think I am close.
1
2
3
4
Logger& Logger::operator()(LogLevels level){
   //...
   return *this;
}

friend Logger& operator<< (Logger& logger, T thing) ¿why isn't that a member function?

Also, ¿why are you using dynamic allocation?
Last edited on
Hi,

Thank you for the reply.

I used the * for the file and stream because I thought at some point I might want to make the class so that I would be able to change the stream to something else other than a QTextStream.

For some reason I was unable to get the templates to work for the stream operators as a member function. I thought if you were overriding the stream operators you cannot define them in your class header? Thats why I had them the way that they are.

I re-wrote some of the class to look like this (under the assumption that you can make them member functions):
Heres my class .h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef LOGGERSTREAM_H
#define LOGGERSTREAM_H

#include <QObject>
#include <QTextStream>
#include <QFile>
#include <QDebug>

class LoggerStream : public QObject
{
    Q_OBJECT

public:
    LoggerStream(QString file_name);
    ~LoggerStream();

private:
    QTextStream* out_stream;
    QFile file;
    
};

#endif // LOGGERSTREAM_H 


Heres my class .cpp
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
LoggerStream::LoggerStream(QString file_name)
{
    file.setFileName(file_name);
    if (!file.open(QIODevice::WriteOnly)){
        qDebug() << "failed to open file";
    }
    out_stream = new QTextStream(&file);
}

template <typename T>
LoggerStream& LoggerStream::operator<< (LoggerStream& logger, T thing)
{
    *logger.out_stream << thing;
    *logger.out_stream << "\n";
    logger.out_stream.flush();
    return logger;
}

LoggerStream& LoggerStream::operator<< (LoggerStream& logger, QVariant thing)
{
    *logger.out_stream << thing.toString();
    *logger.out_stream << "\n";
    logger.out_stream.flush();
    return logger;
}

LoggerStream::~LoggerStream()
{
    file.close();
    delete out_stream;
}


I am getting the following compilation errors (which I what I was getting before when I tried to make them class members):

loggerstream.cpp:13: error: 'LoggerStream& LoggerStream::operator<<(LoggerStream&, T)' must take exactly one argument
Last edited on
> must take exactly one argument
LoggerStream& LoggerStream::operator<< (T thing)
the left operand is this

Also, you must define template functions in the header.


By the way, as you need a destructor you'll also need a copy constructor and an assignment operator
Again, thank you very very much for the help. It does work now as member functions as the following:
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
#ifndef LOGGERSTREAM_H
#define LOGGERSTREAM_H

#include <QObject>
#include <QTextStream>
#include <QFile>
#include <QDebug>

enum LogLevels {
    LOG_WARNING = 0,
    LOG_INFO,
    LOG_CRITICAL,
    LOG_DEBUG1,
    LOG_DEBUG2,
    LOG_DEBUG3
};

class LoggerStream : public QObject
{
    Q_OBJECT

public:
    LoggerStream(QString file_name);
    ~LoggerStream();
    template <typename T> LoggerStream & operator << (T thing);
    LoggerStream & operator << (QVariant thing);

private:
    QTextStream* out_stream;
    QFile file;
    
};

#endif // LOGGERSTREAM_H 


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
#include "loggerstream.h"

LoggerStream::LoggerStream(QString file_name)
{
    file.setFileName(file_name);
    if (!file.open(QIODevice::WriteOnly)){
        qDebug() << "failed to open file";
    }
    out_stream = new QTextStream(&file);
}

template <typename T>
LoggerStream& LoggerStream::operator << (T thing)
{
    *this->out_stream << thing;
    *this->out_stream << "\n";
    this->out_stream->flush();
    return *this;
}

LoggerStream& LoggerStream::operator << (QVariant thing)
{
    *this->out_stream << thing.toString();
    *this->out_stream << "\n";
    this->out_stream->flush();
    return *this;
}

LoggerStream::~LoggerStream()
{
    file.close();
    delete out_stream;
}


Yet I still don't see how the syntax would work for adding the log levels as an input argument as you posted before?

1
2
3
4
5
Logger& Logger::operator()(LogLevels level)
{
   //...
   return *this;
}


I guess I dont really get the concept of adding an argument after the operator.

Edt: I got it, thank you so much again for the help!
Last edited on
Topic archived. No new replies allowed.