std::带有颜色和标题的 clog 包装器无法正确打印整数

std::clog wrapper with color and header fails to print integers properly

本文关键字:打印 整数 clog 有颜色 标题 std 包装      更新时间:2023-10-16

我需要一个类来包装对std::clog的调用,以便:

  • 每条消息都以一个标头为前缀,该标头包含时间和生成消息的实体的名称。
  • 消息根据错误
  • 类型(例如调试、警告、错误(着色。
  • 使用它的方式完全等同于std::clog << "..."的所有功能(即具有隐式的基本类型到字符串转换、流操纵器、刷新等的能力(。

我的尝试是基于在这个论坛(*(中找到的许多例子,但我想以一种错误的方式,因为我的类有点错误。 本质上,我尝试的是通过使用覆盖overflowxputn方法来扩展std::streambuf,最终它们会调用clogoperator<<

注意:我发现很难同时保持我的问题完整(**(,最小和可验证,因此任何建议/意见将不胜感激。不过,对我来说最重要的是我采取的方法,而不是特定的错误或实现缺陷。

class LogStream : public std::streambuf
{
public:
enum class Color { RED_BRIGHT, RED_DARK, /* ...others */ NO_COLOR };
LogStream(void);
LogStream(std::basic_ostream<char>& out, std::string cname, char c, Color icon_color, Color text_color = Color::NO_COLOR);
/* Non-copiable. */
LogStream(const LogStream&) = delete;
LogStream& operator=(const LogStream&) = delete;
static void setNameLength(int l);
protected:
int_type overflow(int_type c = traits_type::eof());
std::streamsize xsputn(const char* s, std::streamsize n);
private:
/* Important stuff: */
std::basic_ostream<char>& m_out;
bool m_new_line;
void conditionalPrintHeader(void);
void endLine(void);
/* For message decoration purposes: */
Color m_color_icon;
Color m_color_text;
char m_icon;
std::string m_cname;
static std::map<Color, const std::string> m_color_lut;
};
/* A macro to create static instances of a logger later in each CPP file. */
#define CREATE_LOGGER(klass)                        
namespace Log {                                 
static std::ostream dbg(new LogStream(      
std::clog,                              
#klass,                                 
'>',                                    
LogStream::Color::RED_BRIGHT,           
LogStream::Color::NO_COLOR));           
static std::ostream err(new LogStream(      
std::clog,                              
#klass,                                 
'e',                                    
LogStream::Color::RED_BRIGHT,           
LogStream::Color::RED_DARK));           
}

我的覆盖函数是这样实现的:

std::streamsize LogStream::xsputn(const char* s, std::streamsize n)
{
conditionalPrintHeader();
if(s[n - 1] == 'n') {
m_new_line = true;
endLine();
}
m_out << s;
return n;
}
LogStream::int_type LogStream::overflow(int_type c)
{
if(c == traits_type::eof()) {
return traits_type::eof();
} else {
char_type ch = traits_type::to_char_type(c);
return xsputn(&ch, 1) == 1 ? c : traits_type::eof();
}
}
void LogStream::conditionalPrintHeader(void)
{
if(m_new_line) {
m_out << "... header and color escape codes ...";
m_new_line = false;
}
}
void LogStream::endLine(void)
{
m_out << "color escape code for no color.";
}

函数conditionalPrintHeaderendLine本质上尝试实现以下基本结构:

[header string] [ANSI color code] [<the log message>] [end color code]

这样当我这样做时:

Log::warn << "Integer: " << 123 << ", Bool: " << std::boolalpha << true << ", Float: " << 3.1415f << "n";

终端输出:

HEADER + [START COLOR] Integer: 123, Bool: true, Float: 3.1415 [END COLOR]

大多数时候,除了我需要打印整数值外,一切正常。我得到的不是数字,而是额外的垃圾,如下所示:

[ ... ] Integer: 123�~c, Bool: true, Float: 3.1415

笔记:

(*( 启发或直接促成我的解决方案的类似问题:

  • https://stackoverflow.com/a/10921803/1876268 --我把大部分的实现概念带到了这里。
  • 使用 std::cout 添加时间戳
  • 声明/定义自定义类 cout 对象的正确方法
  • https://codereview.stackexchange.com/a/187373/176115
  • 正确实现 std::streambuf::overflow

(**( 我粘贴了整个头文件和源文件,以便尽可能完整,以防我在其他地方错过错误:Log.hpp、Log.cpp。

现在我认为问题在于xsputn的字符串参数(我假设它不是以 null 结尾的(。我解决了整数的问题,如下所示,但我仍然不清楚这种方法是否好。

std::streamsize LogStream::xsputn(const char* s, std::streamsize n)
{
conditionalPrintHeader();
if(s[n - 1] == 'n') {
m_new_line = true;
endLine();
}
std::string str;
for(int c = 0; c < n; c++) {
str += s[c];
}
m_out << str;
return n;
}