Custom Stream to Method in C++

Custom stream to method in C++?

If all that you need is directing certain log messages to files, have you considered std::ofstream?

Otherwise, I like to derive my logging class from std::ostream, so I get all of the stream goodness. The trick is to put all of your application-specific code in the associated streambuf class. Consider:

#include <iostream>
#include <sstream>

class CLogger : public std::ostream {
private:
class CLogBuf : public std::stringbuf {
private:
// or whatever you need for your application
std::string m_marker;
public:
CLogBuf(const std::string& marker) : m_marker(marker) { }
~CLogBuf() { pubsync(); }
int sync() {
std::cout << m_marker << ": " << str();
str("");
return std::cout?0:-1;
}

};

public:
// Other constructors could specify filename, etc
// just remember to pass whatever you need to CLogBuf
CLogger(const std::string& marker) : std::ostream(new CLogBuf(marker)) {}
~CLogger() { delete rdbuf(); }
};

int main()
{
CLogger hi("hello");
CLogger bye("goodbye");

hi << "hello, world" << std::endl;
hi << "Oops, forgot to flush.\n";
bye << "goodbye, cruel world\n" << std::flush;
bye << "Cough, cough.\n";
}

Notes:

  • The CLogger constructor can take whatever parameters you need to use -- a filename, an output language, a pointer to the underlying log data, whatever. Just pass the data onto the CLogBuf class.
  • The CLogBuf's sync() is automatically called during in response to std::flush.

How to write custom input stream in C++

The proper way to create a new stream in C++ is to derive from std::streambuf and to override the underflow() operation for reading and the overflow() and sync() operations for writing. For your purpose you'd create a filtering stream buffer which takes another stream buffer (and possibly a stream from which the stream buffer can be extracted using rdbuf()) as argument and implements its own operations in terms of this stream buffer.

The basic outline of a stream buffer would be something like this:

class compressbuf
: public std::streambuf {
std::streambuf* sbuf_;
char* buffer_;
// context for the compression
public:
compressbuf(std::streambuf* sbuf)
: sbuf_(sbuf), buffer_(new char[1024]) {
// initialize compression context
}
~compressbuf() { delete[] this->buffer_; }
int underflow() {
if (this->gptr() == this->egptr()) {
// decompress data into buffer_, obtaining its own input from
// this->sbuf_; if necessary resize buffer
// the next statement assumes "size" characters were produced (if
// no more characters are available, size == 0.
this->setg(this->buffer_, this->buffer_, this->buffer_ + size);
}
return this->gptr() == this->egptr()
? std::char_traits<char>::eof()
: std::char_traits<char>::to_int_type(*this->gptr());
}
};

How underflow() looks exactly depends on the compression library being used. Most libraries I have used keep an internal buffer which needs to be filled and which retains the bytes which are not yet consumed. Typically, it is fairly easy to hook the decompression into underflow().

Once the stream buffer is created, you can just initialize an std::istream object with the stream buffer:

std::ifstream fin("some.file");
compressbuf sbuf(fin.rdbuf());
std::istream in(&sbuf);

If you are going to use the stream buffer frequently, you might want to encapsulate the object construction into a class, e.g., icompressstream. Doing so is a bit tricky because the base class std::ios is a virtual base and is the actual location where the stream buffer is stored. To construct the stream buffer before passing a pointer to a std::ios thus requires jumping through a few hoops: It requires the use of a virtual base class. Here is how this could look roughly:

struct compressstream_base {
compressbuf sbuf_;
compressstream_base(std::streambuf* sbuf): sbuf_(sbuf) {}
};
class icompressstream
: virtual compressstream_base
, public std::istream {
public:
icompressstream(std::streambuf* sbuf)
: compressstream_base(sbuf)
, std::ios(&this->sbuf_)
, std::istream(&this->sbuf_) {
}
};

(I just typed this code without a simple way to test that it is reasonably correct; please expect typos but the overall approach should work as described)

Implement custom stream

The easiest custom stream a stream that "wraps" some other stream (similar to compression streams). Each method would simply redirect its implementation to internal stream.

class MyStream : Stream
{
Stream inner;
public MyStream(Stream inner)
{
this.inner = inner;
}

public override int Read(byte[] buffer, int offset, int count)
{
var result = inner.Read(buffer, offset, count);

/* HERE I COULD CALL A CUSTOM EVENT */
return result;
}
///
}

Usage sample: functionThatTakesStream(new MyStream(new MemoryStream());.

Real code will need to handle exceptions in operations on inners stream before/after fireing events and deal with IDisposable correctly.

C++ custom stream manipulator that changes next item on stream

First, you have to store some state into each stream. You can do that with the function iword and an index you pass to it, given by xalloc:

inline int geti() { 
static int i = ios_base::xalloc();
return i;
}

ostream& add_one(ostream& os) { os.iword(geti()) = 1; return os; }
ostream& add_none(ostream& os) { os.iword(geti()) = 0; return os; }

Having that in place, you can already retrieve some state in all streams. Now, you just have to hook into the respective output operation. Numeric output is done by a facet, because it potentially is locale dependent. So you can do

struct my_num_put : num_put<char> {
iter_type
do_put(iter_type s, ios_base& f, char_type fill, long v) const {
return num_put<char>::do_put(s, f, fill, v + f.iword(geti()));
}

iter_type
do_put(iter_type s, ios_base& f, char_type fill, unsigned long v) const {
return num_put<char>::do_put(s, f, fill, v + f.iword(geti()));
}
};

Now, you can test the stuff.

int main() {
// outputs: 11121011
cout.imbue(locale(locale(),new my_num_put));
cout << add_one << 10 << 11
<< add_none << 10 << 11;
}

If you want that only the next number is incremented, just set the word to 0 again after each call to do_put.

Is it possible to use functions that acts on FILE* on custom structures?

It depends on the C library you're using. Glibc, for example, supports custom streams through fopencookie (further documentation here). FreeBSD (and probably other BSDs as well, including OS X) have funopen. It doesn't look like Microsoft's C library supports such a feature.

custom FILE type in C/C++

If your "custom stream" isn't something you can represent with a file descriptor or file handle, then you're out of luck. The FILE type is implementation-defined, so there's no standard way to associate other things with one.

If you can get a C file descriptor for whatever it is you're trying to write to, then you can call fdopen on it to turn it into a FILE*. It's not standard C or C++, but it's provided by Posix. On Windows, it's spelled _fdopen.

If you're using Windows and you have a HANDLE, then you can use _open_osfhandle to associate a file descriptor with it, and then use _fdopen from there.

Are you really tied to fputs? If not, then replace it with use of a C++ IOStream. Then you can provide your own descendant of std::basic_streambuf, wrap it in a std::ostream, and use standard C++ I/O on it.

custom stream manipulator for class

To make it work you have to add overload of operator << for functions,
than call the function from it:

 class CAudit
{
//...other details here as in original question

CAudit& operator << (CAudit & (*func)(CAudit &))
{
return func(*this);
}
};

CAudit audit;
audit << "some text" << CAudit::write;

Writing a manipulator for a custom stream class

Your manipulator should be declared as a function which accepts just one argument of type ostream&. However, if you make it a member function, you know there is an implicit this argument being passed to the function as well.

Thus, you should rather declare your manipulator as a free, non-member function, making it friend of your class so that it can access its private member ib:

class IndentStream : public ostream {
public:
IndentStream(ostream &os) : ib(os.rdbuf()), ostream(&ib){};

ostream& indent(ostream& stream) {
ib.indent();
return stream;
}

friend ostream& deindent(ostream& stream);
// ^^^^^^

private:
indentbuf ib;
};

ostream& deindent(ostream& stream)
{
IndentStream* pIndentStream = dynamic_cast<IndentStream*>(&stream);
if (pIndentStream != nullptr)
{
pIndentStream->ib.deindent();
}

return stream;
}

int main()
{
IndentStream is(cout);
is << "31 hexadecimal: " << hex << 31 << endl;
is << "31 hexadecimal: " << hex << 31 << deindent << endl;
is << "31 hexadecimal: " << hex << 31 << endl;
return 0;
}

Alternatively, if you really want your function to be a member, you could make it static:

class IndentStream : public ostream {
public:
IndentStream(ostream &os) : ib(os.rdbuf()), ostream(&ib){};

ostream& indent(ostream& stream) {
ib.indent();
return stream;
}

static ostream& deindent(ostream& stream)
{
IndentStream* pIndentStream = dynamic_cast<IndentStream*>(&stream);
if (pIndentStream != nullptr)
{
pIndentStream->ib.deindent();
}

return stream;
}

private:
indentbuf ib;
};

However, this would force you to use a qualified name to refer to it:

int main()
{
IndentStream is(cout);
is << "31 hexadecimal: " << hex << 31 << endl;
is << "31 hexadecimal: " << hex << 31 << IndentStream::deindent << endl;
// ^^^^^^^^^^^^^^
is << "31 hexadecimal: " << hex << 31 << endl;
return 0;
}

Custom stream flush type

I believe all it would take is overriding ostream::put(char), but don't quote me on that:

template <typename Ch>
class autoflush_ostream : public basic_ostream<Ch> {
public:
typedef basic_ostream<Ch> Base;
autoflush_ostream& put(Ch c);
};

template <typename Ch>
autoflush_ostream<Ch>& autoflush_ostream<Ch>::put(Ch c) {
Base::put(c);
if (c == "\n") {
flush();
}
return *this;
}

You might have to override every method and function that takes a character or sequence of characters that's defined in the STL. They would all basically do the same thing: call the method/function defined on the super class, check if a newline was just printed and flush if so.

C++ custom stream manipulator that changes next string on stream

The linked answer shows the "proper" way to achieve customisation of stream formatting behaviour, but it requires the formatted output operation to be extensible - this might be true for a couple of reasons:

  • because you have written an operator<< for your own type and have complete control over the behaviour, or
  • because you are printing a type which the Standard Library explicitly gives you control over via locale (e.g. printing integers with different digit separators).

Neither of these is the case for std::string. The intention is that the std::string contains exactly the characters you want to print. That doesn't make it impossible, but the solution I show below is hacky as hell and I really don't recommend it! A string should already contain the formatted content, and it shouldn't be up to the printing operation to change that.

The trick is that we want to call our own operator<<, and we can't control the type of the string, so we control the type of the stream instead.

#include <algorithm>
#include <iostream>
#include <string>

// Not really a stream, but works enough like one for our purposes
struct UpperStream {
std::ostream& d_originalStream;
UpperStream(std::ostream& originalStream)
: d_originalStream(originalStream)
{}
};

// Trick to turn a normal stream into our magic type
struct UpperHelper {} makeNextStringUpper;
UpperStream operator<<(std::ostream& os, UpperHelper)
{ return os; }

// Special printing for strings
std::ostream& operator<<(UpperStream const& us, std::string s)
{
auto l = us.d_originalStream.getloc();
std::transform(std::cbegin(s),
std::cend(s),
std::begin(s),
[=](char c){return std::toupper(c, l);});
return us.d_originalStream << s;
}

// Default to normal printing for everything else, and then go back to normal
// stream behaviour
template <typename T>
std::ostream& operator<<(UpperStream const& us, T&& t)
{ return us.d_originalStream << t; }

int main()
{
using namespace std::string_literals;
std::cout << makeNextStringUpper << "Hello, World!\n"s;
std::cout << makeNextStringUpper << 123 << " Hello, World!\n"s;
}

This prints:

HELLO, WORLD!
123 Hello, World!

To be honest, the whole business with trying to make this look like a stream manipulator is pretty odd. What's wrong with working on the string itself?

std::string toUpper(std::string s)
{
std::locale l;
std::transform(std::cbegin(s), std::cend(s), std::begin(s),
[=](char c){return std::toupper(c, l);});
return s;
}

int main()
{
std::cout << toUpper("Hello, World!\n"s);
}


Related Topics



Leave a reply



Submit