提问人:f ckx 提问时间:3/24/2022 最后编辑:f ckx 更新时间:3/27/2022 访问量:915
如何创建自定义的 ostream / streambuf 类以实现灵活的输出流?
How to create customized ostream / streambuf classes for flexible output stream?
问:
我发现了很多关于输出重定向、创建流缓冲区和 ostream 类的信息,但我还没有设法成功地将其应用于我的目的。这篇文章变得很长,因为我想描述我的分步方法。
我有一个应用程序,它使用类 MyNotifier,该类捕获应用程序中的事件并根据事件数据编写日志消息。默认情况下,它将日志消息发送到 std::cout,但 MyNotifier 的构造函数接受 std::ostream& 类型的变量来覆盖它。我正在尝试构造一个该类型的类,该类应将日志发送到不同的输出通道,例如通过MQTT客户端。我的MQTT启动并运行良好。我的问题是关于自定义ostream类的创建。
这是应该使用新类的代码(请参阅app_main中的注释行),当我使用 std::cout 时,它会输出。对于测试,通过直接调用 MyNotifier::Notify 来生成事件。
class MyNotifier {
public:
//constructor
MyNotifier(std::ostream& os = std::cout) : ost(os) {}
//takes eventdata as a code for the event
//composes some output string based on the input and outputs it to the customizable output stream ost
virtual void Notify( unsigned long eventdata);
protected:
std::ostream& ost;
}; //class MyNotifier
Implementation:
void MyNotifier::Notify(unsigned long eventdata) {
//takes eventdata as dummy for an event
//composes some output string based on the input and outputs it to the customizable output stream ost
char s[200];
int wr = sprintf(s, "RECEIVED EVENT %s ", "of type 1 ");
sprintf( s + wr , "with number %lu\n", eventdata);
std::cout << "MyNotifier::Notify" << s << std::endl; //this shows up
ost << "dummy custom_ostream output: " << eventdata << std::endl;
//trial to send over MQTT, in the end ost should generate MQTT output
esp_mqtt_client_publish(mqtt_client, "/fckx_seq/GUI", "value", 0, 1, 0); //works fine
} //MyNotifier::Notify
void app_main(void) {
MyNotifier notifier; //instantiate with default output stream (std::cout)
//MyNotifier notifier(std::cout); //instantiate with default output stream explicitly, also works with the same result
//MyNotifier notifier(custom_ostream) //desired way to send logs over a Custom_ostream object
notifier.Notify(3142727); //notify some event
}
这给出了 cout 上所需的输出:
类型 1 的 RECEIVED 事件,编号为 3142727
在自定义输出的第一步中,我只自定义 streambuf 类 (OutStreamBuf)。它被一个“普通”的ostream类使用:
class OutStreamBuf : public std::streambuf {
protected:
/* central output function
* - print characters in uppercase mode
*/
//converts each character to uppercase
virtual int_type overflow (int_type c) {
if (c != EOF) {
// convert lowercase to uppercase
c = std::toupper(static_cast<char>(c),getloc());
//output to standard output
putchar(c);
}
return c;
}
// write multiple characters MUST USE CONST CHAR* ?
virtual std::streamsize xsputn (char* s, std::streamsize num) {
std::cout << "**size: " << num << std::endl;
std::cout << "OutStreamBuf contents: " << s << std::endl;
return 1;
}
}; //OutStreamBuf
Implementation:
OutStreamBuf outStreamBuf;
std::ostream custom_ostream(&outStreamBuf);
MyNotifier notifier(custom_ostream); //instantiate with customized output stream
notifier.Notify(314132345); //notify some event
custom_ostream << "Test << operator" << std::endl;
Output:
**MyNotifier::Notify direct: RECEIVED EVENT of type 1 with number 314132345
DUMMY CUSTOM_OSTREAM OUTPUT: 314132345 <------ THIS IS THE DESIRED OUTPUT
TEST << OPERATOR**
In my second step I want to get hold of the buffer contents to be able to forward this to my MQTT handler. So I decided that I need a customized ostream object. In the second trial I therefore created a customized ostream class (OutStream) with an *embedded* customized streambuf class:
class OutStream : public std::ostream {
private:
//private local Outbuf for OutStream
class Outbuf : public std::streambuf {
protected:
/* central output function
* - print characters in uppercase mode
*/
//converts each character to uppercase
virtual int_type overflow (int_type c) {
if (c != EOF) {
// convert lowercase to uppercase
c = std::toupper(static_cast<char>(c),getloc());
//output to standard output
putchar(c);
}
return c;
}
// write multiple characters MUST USE CONST CHAR* (?)
virtual std::streamsize xsputn (char* s, std::streamsize num) {
std::cout << "**size: " << num << std::endl;
std::cout << "OUTBUF contents: " << s << std::endl;
return 1;
}
}; //Outbuf
Outbuf outbuf;
std::streambuf * buf;
public:
//constructor
OutStream() {
//buf = this->rdbuf(); //compiles OK, but what does it do ?
buf = rdbuf(); //compiles OK, but what does it do ?
std::cout << "SOME MESSAGE FROM OutStream constructor" <<std::endl;
esp_mqtt_client_publish(mqtt_client, "/fckx_seq/GUI", "OutStream constructor",
, 1, 0);
};
// << operator
//https://www.geeksforgeeks.org/overloading-stream-insertion-operators-c/
//have a good look on what parameters the operator should take , see the above article
friend std::ostream & operator << (std::ostream &stream, const OutStream& outStream){
esp_mqtt_client_publish(mqtt_client, "/fckx_seq/GUI", "OutStream << operator", 0, 1, 0); //doesn't show
stream << "Test << operator inside " << std::endl; //doesn't show
return stream; //return the stream
};
}; //OutStream
Implementation:
``` OutStream custom_ostream; //using a composite ostream/streambuf object
MyNotifier notifier(custom_ostream); //instantiate with customized output stream
notifier.Notify(314132345); //notify some event
custom_ostream << "Test << operator" << std::endl;
这不会显示自定义输出。因此,我在构造函数中添加了一个日志(正确显示)和一个带有日志的修改后的 << 运算符(也未显示):
来自 OutStream 构造函数的一些消息
MyNotifier::Notify direct:类型 1 的 RECEIVED 事件,编号为 314132345
由于 << 运算符日志也失败了,我认为 ostream 对象的构造函数有问题和/或它与 streambuf 绑定。这对我来说是相当复杂的事情。一些帮助将不胜感激。
[编辑]在与 Stephen M. Webb 讨论后,我专注于寻找一个基于 std::streambuf 的类示例,其中包含额外的缓冲。我发现以下代码有望为后续步骤奠定良好的基础:
//p. 837 The C++ Standard Library Second Edition, Nicolai M. Josuttis
class Outbuf_buffered : public std::streambuf {
protected:
static const int bufferSize = 10; // size of data buffer
char buffer[bufferSize]; // data buffer
public:
// constructor
// - initialize data buffer
// - one character less to let the bufferSizeth character cause a call of overflow()
Outbuf_buffered() {
setp (buffer, buffer+(bufferSize-1));
}
// destructor
// - flush data buffer
virtual ~Outbuf_buffered() {
sync();
}
protected:
// flush the characters in the buffer
int flushBuffer () {
int num = pptr()-pbase();
if (write (1, buffer, num) != num) {
return EOF;
}
pbump (-num); // reset put pointer accordingly
return num;
}
// buffer full
// - write c and all previous characters
virtual int_type overflow (int_type c) {
if (c != EOF) {
// insert character into the buffer
*pptr() = c;
pbump(1);
}
// flush the buffer
if (flushBuffer() == EOF) {
// ERROR
return EOF;
}
return c;
}
// synchronize data with file/destination
// - flush the data in the buffer
virtual int sync () {
if (flushBuffer() == EOF) {
// ERROR
return -1;
}
return 0;
}
}; //Outbuf_buffered
答:
您根本不想触摸 ostream(格式层)。您应该在 streambuf(传输层)中执行所有操作。如果需要,可以使用操纵器通过通用 ostream 接口设置或更改基础传输层的状态。
评论
sputn()
overflow()
esp_mqtt_client_publish()
通过使用派生自 std::streambuf 的类,该类包含额外的缓冲区成员,我可以做到这一点。主 ostream 输出流只是 std::ostream。我从 C++ 标准库第二版中找到了一个示例,Nicolai M. Josuttis,第 837 页。
请参阅我原始帖子中的编辑。 感谢 @Stephen M. Webb 坚持不懈地引导我找到答案!
评论