I am writing a plugin in C++ that uses C API that gives me a single logging function with this signature:
typedef void(*println_t)(const char* text);
I want to wrap this type of a function in a nice ostream
to use all C++ formatting features. Having read it is best to use streambuf
for this, I made this code:
//println_buf.h
#include <string>
#include <sstream>
class println_buf : public std::streambuf
{
typedef void(*println_t)(const char* text);
println_t logfunc_;
std::string buffer;
public:
println_buf(println_t logfunc) : logfunc_(logfunc)
{
}
println_buf(const println_buf &) = delete;
println_buf(println_buf &&);
println_buf& operator=(const println_buf &) = delete;
println_buf& operator=(println_buf &&);
virtual std::streamsize xsputn(const char* s, std::streamsize n) override;
virtual int overflow(int c) override;
virtual int sync() override;
};
//println_buf.cpp
#include "println_buf.h"
using namespace std;
println_buf::println_buf(println_buf &&o) : logfunc_(o.logfunc_), buffer(std::move(o.buffer))
{
}
println_buf& println_buf::operator=(println_buf &&o)
{
logfunc_ = o.logfunc_;
buffer = std::move(o.buffer);
return *this;
}
streamsize println_buf::xsputn(const char* s, streamsize n)
{
streamsize start = 0;
for (std::streamsize i = 0; i < n; i++)
{
if (s[i] == '\n')
{
buffer.append(s + start, s + i);
sync();
start = i + 1;
}
}
buffer.append(s + start, s + n);
return n;
}
int println_buf::overflow(int c)
{
char ch = traits_type::to_char_type(c);
if (ch == '\n')
{
sync();
} else {
buffer.append(1, c);
}
return c;
}
int println_buf::sync()
{
logfunc_(buffer.c_str());
buffer.clear();
return 0;
}
Usage:
println_t logfunc = ...;
println_buf buffer(logfunc);
std::ostream stream(&buffer);
stream << "Hello world" << std::endl;
It should be noted that println_t
includes a newline automatically in the actual output, but I want to have to specify the line separator in C++.
My concern is that the function should not be called with a string containing \n
, but the string must be passed to the function only if it originally ended with \n
, and be buffered otherwise. Also endl
should cause the function to be called immediately.
1 Answer 1
I'll review more later but a basic comment first.
Rather than write just the println_buf
class I would also write the println_stream
class that internally has a println_buf
.
class println_stream: std::ostream
{
// Make this a private class.
// You don't need to leak implementation details.
// As far as anybody else is convered it is just std::streambuf
class println_buf { /* STUFF */ };
println_buf buf;
std::streambuf* oldBuf = nullptr;
public:
println_stream()
// You can't pass the buf to the ostream constructor
// here as it has not been constructed yet.
// There are ways but lets keep it simple for now.
{
// I did not do the work with logfunc
// Have not read that bit yet.
// But you get the idea I hope.
oldBuf = rdbuf(&buf);
}
~println_stream()
{
// Can't remember if it is required.
// But I would always set back the original buffer.
rdbuf(oldBuf);
}
};
int main()
{
println_stream stream;
stream << "Hello world\n";
}
-
\$\begingroup\$ Later never happened π ... still +1. \$\endgroup\$0xC0000022L– 0xC0000022L2020εΉ΄07ζ14ζ₯ 10:17:23 +00:00Commented Jul 14, 2020 at 10:17