7
\$\begingroup\$

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.

200_success
146k22 gold badges190 silver badges479 bronze badges
asked Jan 19, 2018 at 15:23
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

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";
}
answered Jan 19, 2018 at 18:42
\$\endgroup\$
1
  • \$\begingroup\$ Later never happened πŸ˜‰ ... still +1. \$\endgroup\$ Commented Jul 14, 2020 at 10:17

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.