自定义操纵器使用 Visual C++ 而非 g++/clang 进行编译
Custom manipulator compiles with Visual C++ but not g++/clang
我有一个记录器 class QueuedLog
,它对日志消息进行排队,并在需要时将队列中的所有日志消息插入 std::ostream
。为了分隔每条日志消息,我编写了一个名为 endm
的操纵器,其使用方式与 std::endl
类似。例如,这是一个用例:
QueuedLog log(INFO);
// do stuff
log << "test: 0x" << std::hex << std::uppercase << 15 << endm; // add a log message
// do more stuff
log << "log something else" << endm;
std::cout << log << std::endl; // insert all the queued up log messages into cout
// do more stuff
log << "start another message...";
// calculate something, add it to a log message and end the message
log << std::dec << 42 << endm;
std::cout << log << std::endl; // insert all the queued up log messages into cout
log << "add yet another message" << endm;
// don't need to log that last message after all
// leave it in the QueuedLog instead of inserting it into cout
我的代码可以用 Visual C++ 很好地编译,但是当我尝试使用 endm
操纵器时,g++ 和 clang++ 编译失败。这是 QueuedLog
的最小版本(通常驻留在单独的头文件中),使用一个小示例来说明问题:
#include <ios>
#include <iostream>
#include <string>
#include <sstream>
#include <deque>
#include <stdexcept>
namespace Logger {
enum LogType {
NONE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL
};
// Converts a LogType to a `std::string` which can be prepended to a log message.
std::string prepend_type(LogType type) {
switch (type) {
case DEBUG: return std::string("[DEBUG] ");
case INFO: return std::string("[INFO] ");
case WARN: return std::string("[WARN] ");
case ERROR: return std::string("[ERROR] ");
case FATAL: return std::string("[FATAL] ");
default: return std::string("");
}
}
class QueuedLog {
/* Holds a partially contructed log message.
A std::stringstream is used instead of a std::string so that non-string data types can be inserted into
the QueuedLog without requiring conversion. Also, client code can apply I/O manipulators like std::hex.
*/
std::ostringstream stream;
std::deque<std::string> messages; // Holds the queued, completed log message(s).
// The LogType of new messages inserted into the QueuedLog. This can be changed at any time.
LogType log_type;
public:
QueuedLog(LogType logtype = NONE) : log_type(logtype) {} // Constructs a QueuedLog with no text and an initial LogType.
// Inserts a character sequence into the QueuedLog.
template<typename T> inline QueuedLog& operator<<(const T& message) {
//inline QueuedLog& operator<<(const std::string& message) { // non-template version doesn't work, either
// Only prepend with logtype if it is the beginning of the message
if (stream.str().empty()) stream << prepend_type(log_type);
stream << message;
return *this;
}
// Overload used for manipulators like QueuedLog::endm()
inline QueuedLog& operator<<(QueuedLog& (*pf)(QueuedLog&)) {
(*pf)(*this);
return *this;
}
// Adds the newline character and marks the end of a log message.
friend inline QueuedLog& endm(QueuedLog& log) {
log.stream << log.stream.widen('\n');
// Add the completed message to the messages deque, and reset the stream for the next message
log.messages.push_back(log.stream.str());
log.stream.str(""); // clear the underlying string
log.stream.clear(); // clear any error flags on the stream
return log;
}
/* Inserts all the completed log messages in the QueuedLog object into a std::ostream.
If the QueuedLog contains an incomplete log message (a message that has not been terminated by QueuedLog::endm())
then that partial message will not be inserted into the std::ostream.
*/
friend inline std::ostream& operator<<(std::ostream& os, QueuedLog& log) {
while (!log.messages.empty()) {
os << log.messages.front();
log.messages.pop_front();
}
return os;
}
};
} // end namespace Logger
using namespace Logger;
int main() {
QueuedLog log(INFO);
log << "test: 0x" << std::hex << std::uppercase << 15; // compiles by itself with all compilers
log << endm; // but compilation error w/ g++/clang++ when trying to use endm
std::cout << log << std::endl;
}
或者,这个(可能过度)简化的例子:
class QueuedLog {
public:
friend inline void endm() {
}
};
int main() {
endm;
}
我试图用 rextester 的所有三个编译器编译它,但它只能用 Visual C++ 成功编译。
g++ 给出以下错误:
error: ‘endm’ was not declared in this scope
clang++的报错信息类似:
error: use of undeclared identifier 'endm'
为什么这在 Visual C++ 中有效,但在 g++ 或 clang++ 中无效?我如何为 g++/clang++ 修复它?该解决方案不需要同时在所有三个编译器中工作,我只想知道如何为 g++ 和 clang++ 修复它。
在 class 定义中定义的友元函数仅在 class 定义中可见。您需要在 class 定义之外声明函数。在 class QueuedLog
之外添加此行,但在 namespace Logger
:
之内
extern QueuedLog& endm(QueuedLog&);
我有一个记录器 class QueuedLog
,它对日志消息进行排队,并在需要时将队列中的所有日志消息插入 std::ostream
。为了分隔每条日志消息,我编写了一个名为 endm
的操纵器,其使用方式与 std::endl
类似。例如,这是一个用例:
QueuedLog log(INFO);
// do stuff
log << "test: 0x" << std::hex << std::uppercase << 15 << endm; // add a log message
// do more stuff
log << "log something else" << endm;
std::cout << log << std::endl; // insert all the queued up log messages into cout
// do more stuff
log << "start another message...";
// calculate something, add it to a log message and end the message
log << std::dec << 42 << endm;
std::cout << log << std::endl; // insert all the queued up log messages into cout
log << "add yet another message" << endm;
// don't need to log that last message after all
// leave it in the QueuedLog instead of inserting it into cout
我的代码可以用 Visual C++ 很好地编译,但是当我尝试使用 endm
操纵器时,g++ 和 clang++ 编译失败。这是 QueuedLog
的最小版本(通常驻留在单独的头文件中),使用一个小示例来说明问题:
#include <ios>
#include <iostream>
#include <string>
#include <sstream>
#include <deque>
#include <stdexcept>
namespace Logger {
enum LogType {
NONE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL
};
// Converts a LogType to a `std::string` which can be prepended to a log message.
std::string prepend_type(LogType type) {
switch (type) {
case DEBUG: return std::string("[DEBUG] ");
case INFO: return std::string("[INFO] ");
case WARN: return std::string("[WARN] ");
case ERROR: return std::string("[ERROR] ");
case FATAL: return std::string("[FATAL] ");
default: return std::string("");
}
}
class QueuedLog {
/* Holds a partially contructed log message.
A std::stringstream is used instead of a std::string so that non-string data types can be inserted into
the QueuedLog without requiring conversion. Also, client code can apply I/O manipulators like std::hex.
*/
std::ostringstream stream;
std::deque<std::string> messages; // Holds the queued, completed log message(s).
// The LogType of new messages inserted into the QueuedLog. This can be changed at any time.
LogType log_type;
public:
QueuedLog(LogType logtype = NONE) : log_type(logtype) {} // Constructs a QueuedLog with no text and an initial LogType.
// Inserts a character sequence into the QueuedLog.
template<typename T> inline QueuedLog& operator<<(const T& message) {
//inline QueuedLog& operator<<(const std::string& message) { // non-template version doesn't work, either
// Only prepend with logtype if it is the beginning of the message
if (stream.str().empty()) stream << prepend_type(log_type);
stream << message;
return *this;
}
// Overload used for manipulators like QueuedLog::endm()
inline QueuedLog& operator<<(QueuedLog& (*pf)(QueuedLog&)) {
(*pf)(*this);
return *this;
}
// Adds the newline character and marks the end of a log message.
friend inline QueuedLog& endm(QueuedLog& log) {
log.stream << log.stream.widen('\n');
// Add the completed message to the messages deque, and reset the stream for the next message
log.messages.push_back(log.stream.str());
log.stream.str(""); // clear the underlying string
log.stream.clear(); // clear any error flags on the stream
return log;
}
/* Inserts all the completed log messages in the QueuedLog object into a std::ostream.
If the QueuedLog contains an incomplete log message (a message that has not been terminated by QueuedLog::endm())
then that partial message will not be inserted into the std::ostream.
*/
friend inline std::ostream& operator<<(std::ostream& os, QueuedLog& log) {
while (!log.messages.empty()) {
os << log.messages.front();
log.messages.pop_front();
}
return os;
}
};
} // end namespace Logger
using namespace Logger;
int main() {
QueuedLog log(INFO);
log << "test: 0x" << std::hex << std::uppercase << 15; // compiles by itself with all compilers
log << endm; // but compilation error w/ g++/clang++ when trying to use endm
std::cout << log << std::endl;
}
或者,这个(可能过度)简化的例子:
class QueuedLog {
public:
friend inline void endm() {
}
};
int main() {
endm;
}
我试图用 rextester 的所有三个编译器编译它,但它只能用 Visual C++ 成功编译。
g++ 给出以下错误:
error: ‘endm’ was not declared in this scope
clang++的报错信息类似:
error: use of undeclared identifier 'endm'
为什么这在 Visual C++ 中有效,但在 g++ 或 clang++ 中无效?我如何为 g++/clang++ 修复它?该解决方案不需要同时在所有三个编译器中工作,我只想知道如何为 g++ 和 clang++ 修复它。
在 class 定义中定义的友元函数仅在 class 定义中可见。您需要在 class 定义之外声明函数。在 class QueuedLog
之外添加此行,但在 namespace Logger
:
extern QueuedLog& endm(QueuedLog&);