Guy L
Guy L

Reputation: 2954

Two Phase Lookup operator << issue?

I'm trying to port my own code from VS2012 to g++4.8.

I'm getting this compliation error:

AllocationManager.cpp: In member function ‘void AllocationManager::printMemoryLeaks()’:
TRLogger.h:247:42: error: ‘streamAggrator’ was not declared in this scope
 #define TRLOG_INFO streamAggrator(logINFO) << PACKET_DESCRIPTION << __FUNCTION__ << ":" << __LINE__ << ": "
                                          ^
AllocationManager.cpp:39:2: note: in expansion of macro ‘TRLOG_INFO’
  TRLOG_INFO << "sdfs\n"; 

Where printMemoryLeaks is a dummy function (AllocationManager is not templated):

void AllocationManager::printMemoryLeaks(void)
 {

    TRLOG_INFO << "sdfs\n"; 
}

In the file TRLogger.h:

enum TLogLevel {logERROR, logWARNING, logINFO, logDEBUG, logDEBUG1, logDEBUG2, logDEBUG3, logDEBUG4};

class streamAggrator
{
public:
    streamAggrator(TLogLevel logLevel);

/* private: */ 
    FILELog fLog;
    WarnLog wlog;
    std::ostringstream& s1; 
    std::ostringstream& s2; 
};


template<typename T>
streamAggrator& operator<<(streamAggrator& agg, const T& obj) 
{
    agg.s1 << obj;
    agg.s2 << obj;
    agg.s2.flush();
    return agg; 
}

....

#define TRLOG_INFO streamAggrator(logINFO) << PACKET_DESCRIPTION << __FUNCTION__ << ":" << __LINE__ << ": "

How can I solve this function - I didn't find any place that I can use this or using to help the compiler.

Thanks, Guy

Upvotes: 1

Views: 117

Answers (1)

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153792

Your immediate problem is that you try to pass a temporary streamAggrator object to a function which takes a streamAggrator by non-const reference. You can't bind temporary object to non-const references. The work-around for this problem is to make the output operator member of your streamAggrator: while you cannot bind a temporary to a non-const reference, you can call non-const member functions. Note that you'll also get problems with maniputors like std::flush (the issue there is that these are templates themselves and you actually need to a concrete operator to call them with to have the compiler deduce their template arguments).

Clearly, I would solve the problem properly, i.e., instead of trying to dig about an attempt to a solution which doesn't create a stream, I would create a std::streambuf do do the actual work. Your examples doesn't do anything useful, i.e., I can't really tell what you are trying to do but the code looks remarkably like trying to do something like teestream: write once but send the output to multiple destintations. I have posted corresponding stream buffers quite a few times in the post (mostly on Usenet, though, but I think, at least, once on Stackoverflow, too).

Although I don't know how to get rid of the macro to fill in the __FILE__ and the __LINE__, the actual stream formatting should probably use a stream buffer:

struct teebuf: std::streambuf {
private:
    std::streambuf* sb1;
    std::streambuf* sb2;
public:
    teebuf(std::streambuf* sb1, std::streambuf* sb2): sb1(sb1), sb2(sb2) {}
    int overflow(int c) {
        this->sb1->sputc(c);
        this->sb2->sputc(c);
        return std::char_traits<char>::not_eof(c);
    }
    int sync() {
        this->sb1->pubsync();
        this->sb2->pubsync();
    }
};
class logstream
    : std::ostream {
    std::ofstream out;
    teebuf        sbuf;
public:
    logstream()
        : out("file.log")
        , sbuf(out.rdbuf(), std::clog.rdbuf()) {
        this->init(&this->sbuf);
    }
    logstream(logstream&& other)
        : out(std::move(other.out))
        , sbuf(std::move(other.sbuf)) {
        this->init(&this->sbuf);
};

I think you can return the log stream. I don't know what your logging level is meant to do but I guess its processing was removed while preparing the question: it is probably necessary to change the implementation to take the logging level suitably into account.

Upvotes: 4

Related Questions