I have the following code:
logging.hxx
#include <iostream>
#include <string>
#include <sstream>
#include "singleton.hxx"
#include "utils.hxx"
class FGaugeLogger: public Singleton<FGaugeLogger> {
public:
enum class LogLevel: int {
DEBUG = 0,
INFO = 1,
WARNING = 2,
ERROR = 3,
FATAL = 4,
};
FGaugeLogger() {};
template<typename T>
void log(LogLevel level, const T& msg) {
if (level >= _level) {
if (level >= LogLevel::WARNING) {
std::cerr << "[" << level << "] " << msg << std::endl;
} else {
std::cout << "[" << level << "] " << msg << std::endl;
}
}
}
private:
LogLevel _level {LogLevel::DEBUG};
};
std::ostream& operator<<(std::ostream& s, const FGaugeLogger::LogLevel level);
#define LOG(level, expr) \
std::stringstream ss; \
ss << expr; \
std::string str = ss.str(); \
FGaugeLogger::instance()->log(FGaugeLogger::LogLevel::level, ss.str());
logging.cxx
#include "logging.hxx"
std::ostream& operator<<(std::ostream& s, FGaugeLogger::LogLevel level) {
s << "level";
return s;
}
main.cxx
#include "logging.hxx"
int main(int argc, char* argv[]) {
LOG("info message");
return 0;
}
This gives the following message when compiled, followed by 100s of overloads that all don't match:
In file included from /media/frederic/WD-5TB/.fg/Programme/fgauge/src/main.cxx:1:
/media/frederic/WD-5TB/.fg/Programme/fgauge/src/logging.hxx: In member function ‘void FGaugeLogger::log(FGaugeLogger::LogLevel, const T&)’:
/media/frederic/WD-5TB/.fg/Programme/fgauge/src/logging.hxx:27:58: error: no match for ‘operator<<’ (operand types are ‘std::basic_ostream<char>’ and ‘FGaugeLogger::LogLevel’)
27 | std::cerr << "[" << level << "] " << msg << std::endl;
| ~~~~~~~~~~~~~~~~ ^~ ~~~~~
| | |
| | FGaugeLogger::LogLevel
| std::basic_ostream<char>
Any ideas what could be wrong here ? AFAICS my code is identical to the third example in this answer to a similar question.
The problem is that C++ files are parsed from top to bottom, and the order of things generally matters. At the point where you have written:
The
operator<<forFGaugeLogger::LogLevelhasn't been declared yet.A simple fix would be to declare it as a
friendinside the class:See minimal example at Compiler Explorer
You could also define it inside the class, making it a hidden friend (see Hidden friends: declarations and definitions).
Alternative Solution
You could also define
logout-of-line after the definition ofoperator<<, likeHowever, this is overall more work compared to the first solution, which only required you to move code into the class and make it
friend. Defining the member function template out-of-line would require some code duplication.