Custom Manipulator for C++ iOStream

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.

Custom manipulator for C++ iostream

It's particularly difficult to add a manipulator to a C++ stream, as one has no control of how the manipulator is used. One can imbue a new locale into a stream, which has a facet installed that controls how numbers are printed - but not how strings are output. And then the problem would still be how to store the quoting state safely into the stream.

Strings are output using an operator defined in the std namespace. If you want to change the way those are printed, yet keeping the look of manipulators, you can create a proxy class:

namespace quoting {
struct quoting_proxy {
explicit quoting_proxy(std::ostream & os):os(os){}

template<typename Rhs>
friend std::ostream & operator<<(quoting_proxy const& q,
Rhs const& rhs) {
return q.os << rhs;
}

friend std::ostream & operator<<(quoting_proxy const& q,
std::string const& rhs) {
return q.os << "'" << rhs << "'";
}

friend std::ostream & operator<<(quoting_proxy const& q,
char const* rhs) {
return q.os << "'" << rhs << "'";
}
private:
std::ostream & os;
};

struct quoting_creator { } quote;
quoting_proxy operator<<(std::ostream & os, quoting_creator) {
return quoting_proxy(os);
}
}

int main() {
std::cout << quoting::quote << "hello" << std::endl;
}

Which would be suitable to be used for ostream. If you want to generalize, you can make it a template too and also accept basic_stream instead of plain string. It has different behaviors to standard manipulators in some cases. Because it works by returning the proxy object, it will not work for cases like

std::cout << quoting::quote; 
std::cout << "hello";

How to implement custom sticky manipulator that automatically adds separators?

The issue with the solution you posted is that it relies on customizing the way integers get formatted using facets. Unfortunately, I do not think there is a corresponding facility which would work for arbitrary types.

There is. You can use utilize the underlying buffer of the stream to get what you want. The buffer is where the character sequence is eventually gathered for maintenance. The following code makes a stream buffer that holds a reference to the object whose character sequence you wish to use. We set the std::ios_base::unitbuf format flag so that the stream is flushed on each output operation (so we can add the separator to the end).

By extension, it also allows you to uninstall the separator and makes sure that no memory is leaked in the process:

#include <iostream>
#include <string>

namespace custom
{
struct sep_impl
{
sep_impl(std::string const& separator);
std::string separator;
};

sep_impl sep(std::string const& str)
{
return sep_impl(str);
}

std::ostream& nosep(std::ostream& os);
}

int separatorEnabled()
{ static int idx = std::ios_base::xalloc(); return idx; }
int getSeparator() { static int idx = std::ios_base::xalloc(); return idx; }

struct custom_separator : std::streambuf
{
public:
custom_separator(std::ostream& _stream) : stream(_stream)
{ }

int_type overflow(int_type c)
{
return stream.rdbuf()->sputc(c);
}

int sync()
{
if (stream.iword(separatorEnabled()))
{
void*& p = stream.pword(getSeparator());
stream << *static_cast<std::string*>(p);
return 0;
}
return stream.rdbuf()->pubsync();
}
private:
std::ostream& stream;
};

void cleanup(std::ios_base::event evt, std::ios_base& str, int idx)
{
if (str.iword(separatorEnabled()) && evt == std::ios_base::erase_event)
{
void*& p = str.pword(idx);
delete static_cast<std::string*>(p);
str.iword(separatorEnabled()) = false;
}
}

std::ostream& set_separator(std::ostream& os, const custom::sep_impl& manip)
{
if (!os.bad())
{
os.pword(getSeparator()) = new std::string(manip.separator);
os.register_callback(cleanup, getSeparator());
}

return os;
}

std::ostream& operator<<(std::ostream& os, const custom::sep_impl& manip)
{
std::ostream* p = os.tie();
if (p && !p->iword(separatorEnabled()))
{
set_separator(*p, manip);
p->iword(separatorEnabled()) = true;
}

return os << std::unitbuf;
}

namespace custom
{
sep_impl::sep_impl(std::string const& _sep) : separator(_sep) { }

std::ostream& nosep(std::ostream& os)
{
cleanup(std::ios_base::erase_event, *os.tie(), getSeparator());
os.tie(nullptr);
return os << std::nounitbuf;
}

void install_separator(std::ostream& o1, std::ostream& o2)
{
static custom_separator csep(o2);
o1.rdbuf(&csep);
o1.tie(&o2);
}
}

int main()
{
std::ostream os(nullptr);
custom::install_separator(os, std::cout);

os << custom::sep(", ") << 4 << 2 << custom::nosep;
}

I'm sure there is also room for improvement, so if anyone has any suggestions they are very much appreciated.

Live Example

Custom manipulator for class

You can use pword array for this.
Every iostream in C++ has two arrays associated with it.

ios_base::iword - array of ints
ios_base::pword - array of void* pointers

You can store you own data in it. To obtain an index, that refers to an empty element in all iword and pword arrays you should use function std::ios_base::xalloc(). It returns int that you can use as an unique index in *word.
You should obtain that index once on the start-up, and than use it for all operations with *word.

Then programming your own manip will look like:

Manipulator function, that receives reference to ios_base object and pointer to the format string, simply stores that pointer in pword

iosObject.pword(index_from_xalloc) = formatString

Then overloaded operator << (>>) obtains format string from the iostream object in the same way. After that you just make a conversion referencing to the format.

Custom stream manipulator that passes a character to operator overload

Minor improvements of the current design

What you are doing is possible and can't be simplified much further. If you want to stick to your current implementation, I recommend fixing the following issues:

Unnecessary copy of entire string

Use

return std::move(out).str();

to prevent copying the entire string in the stringstream at the end (since C++20).

Signed/unsigned comparison

for (int i = 1; i < data.size(); ...)

results in a comparison between signed and unsigned, which results in compiler warnings at a sufficient warning level.
Prefer std::size_t i.

Unconstrained stream insertion operator

operator<<(const T&)

is not constrained and would accept any type, even though it only works for vectors.
Prefer:

operator<<(const std::vector<T>&)

Using std::string instead of std::string_view

const std::string& sequence = ","

results in the creation of an unnecessary string, possibly involving heap allocation, even when we use a string literal such as ",".
Normally we would prefer std::string_view, but here, you are storing this sequence in the class. This can create problems because std::string_view has no ownership over the string, so the lifetime of the string could expire before you use the view.
This problem is inherent to your design and can not be easily resolved.

Alternative design

Calling formatted a stream manipulator would be inaccurate, because stream manipulators are functions which accept and return a basic_ios (or derived type). Your formatted type is just some type with an operator overload for <<, but you could just as well do:

std::cout << vector_to_string(data, "/");

where "/" would default to "," if no argument is provided.
The problem is that we are still not using the stream for outputting the characters, but we first have to create a string, write the vector contents to it, and then write the string into the stream.

Instead, we can make a function that accepts the stream as its first parameter and writes directly to it.
The advantages include:

  • dropping #include <sstream>, potentially just including <iosfwd>
  • performance improvement
  • possibly no heap allocations
  • no unnecessary string copy (before C++20)
  • overall shorter code

This design as a regular function has precedent in the standard library as well. A classic examples is std::getline with the signature:

template < class CharT, ...>
std::basic_istream<CharT, ...>& getline( std::basic_istream<CharT, ...>& input,
std::basic_string<CharT, ...>& str,
CharT delim );

With this design, we can create much more concise code, which also resolves the issues of:

  • being unable to use std::string_view
  • having to use a std::string to store the whole printed vector
#include <string>
#include <vector>
#include <iostream>

template < typename T >
std::ostream& print(std::ostream& ostream,
const std::vector< T >& data,
std::string_view delimiter = ",")
{
if (not data.empty()) {
ostream << data.front();
for (std::size_t i = 1; i < data.size(); i++) {
ostream << delimiter << data[i];
}
}
return ostream;
}

int main(int argc, char const *argv[])
{
std::vector< int > data(10, 5);

print(std::cout, data) << std::endl;
print(std::cout, data, "/") << std::endl;
}

Further generalization for any range

We can generalize this code so that it works for any range that provides an input iterator, not just std::vector.
This means that we can use it for types such as std::array, std::string, std::vector, std::list, etc.

template <typename Range>
std::ostream& print(std::ostream& ostream,
const Range& range,
std::string_view delimiter = ",")
{
auto begin = range.begin();
auto end = range.end();

if (begin != end) {
ostream << *begin;
while (++begin != end) {
ostream << delimiter << *begin;
}
}
return ostream;
}

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;
}

Making a custom istream manipulator

That's a very nice question. I don't know if it's possible. But I implemented something different which gives you the same short syntax you wanted, by overloading the >> operator with a new class called Skip2. Here is the code (which I really enjoyed writing! :-) )

#include <iostream>
#include <string>
#include <istream>
#include <sstream>

using namespace std;

class Skip2 {
public:
string s;
};

istream &operator>>(istream &s, Skip2 &sk)
{
string str;
s >> str;

// build new string
ostringstream build;
int count = 0;
for (char ch : str) {
// a count "trick" to make skip every other 2 chars concise
if (count < 2) build << ch;
count = (count + 1) % 4;
}

// assign the built string to the var of the >> operator
sk.s = build.str();

// and of course, return this istream
return s;
}

int main()
{
istringstream s("1122334455");
Skip2 skip;

s >> skip;
cout << skip.s << endl;

return 0;
}

Parametrized custom stream manipulators - why overload operator?

The overload you're looking for is only defined for function pointers.

basic_ostream& operator<<(
std::basic_ostream<CharT,Traits>& (*func)(std::basic_ostream<CharT,Traits>&) );

Your print_my_data class is a callable object (a functor, in C++ terms). But it is not a function pointer. On the other hand, endl is a function, and hence has a function pointer (in fact, it's one of the few functions in the C++ standard library which do have an address)

A not-unreasonable argument could be made that the overload should look like

basic_ostream& operator<<(
std::function<std::basic_ostream<CharT,Traits>&(std::basic_ostream<CharT,Traits>&)> func);

But alas, std::function wasn't around when the I/O manipulation operators were written. Neither were concepts, for that matter. And using SFINAE to declare

template <typename F>
basic_ostream& operator<<(
F func);

would have just opened an entire Pandora's box worth of messy details that the standards committee didn't want to deal with.

Custom stream manipulator for streaming integers in any base

You can do something like the following. I have commented the code to explain what each part is doing, but essentially its this:

  • Create a "manipulator" struct which stores some data in the stream using xalloc and iword.
  • Create a custom num_put facet which looks for your manipulator and applies the manipulation.

Here is the code...

Edit: Note that im not sure I handled the std::ios_base::internal flag correctly here - as I dont actually know what its for.

Edit 2: I found out what std::ios_base::internal is for, and updated the code to handle it.

Edit 3: Added a call to std::locacle::global to show how to make all the standard stream classes support the new stream manipulator by default, rather than having to imbue them.

#include <algorithm>
#include <cassert>
#include <climits>
#include <iomanip>
#include <iostream>
#include <locale>

namespace StreamManip {

// Define a base manipulator type, its what the built in stream manipulators
// do when they take parameters, only they return an opaque type.
struct BaseManip
{
int mBase;

BaseManip(int base) : mBase(base)
{
assert(base >= 2);
assert(base <= 36);
}

static int getIWord()
{
// call xalloc once to get an index at which we can store data for this
// manipulator.
static int iw = std::ios_base::xalloc();
return iw;
}

void apply(std::ostream& os) const
{
// store the base value in the manipulator.
os.iword(getIWord()) = mBase;
}
};

// We need this so we can apply our custom stream manipulator to the stream.
std::ostream& operator<<(std::ostream& os, const BaseManip& bm)
{
bm.apply(os);
return os;
}

// convience function, so we can do std::cout << base(16) << 100;
BaseManip base(int b)
{
return BaseManip(b);
}

// A custom number output facet. These are used by the std::locale code in
// streams. The num_put facet handles the output of numberic values as characters
// in the stream. Here we create one that knows about our custom manipulator.
struct BaseNumPut : std::num_put<char>
{
// These absVal functions are needed as std::abs doesnt support
// unsigned types, but the templated doPutHelper works on signed and
// unsigned types.
unsigned long int absVal(unsigned long int a) const
{
return a;
}

unsigned long long int absVal(unsigned long long int a) const
{
return a;
}

template <class NumType>
NumType absVal(NumType a) const
{
return std::abs(a);
}

template <class NumType>
iter_type doPutHelper(iter_type out, std::ios_base& str, char_type fill, NumType val) const
{
// Read the value stored in our xalloc location.
const int base = str.iword(BaseManip::getIWord());

// we only want this manipulator to affect the next numeric value, so
// reset its value.
str.iword(BaseManip::getIWord()) = 0;

// normal number output, use the built in putter.
if (base == 0 || base == 10)
{
return std::num_put<char>::do_put(out, str, fill, val);
}

// We want to conver the base, so do it and output.
// Base conversion code lifted from Nawaz's answer

int digits[CHAR_BIT * sizeof(NumType)];
int i = 0;
NumType tempVal = absVal(val);

while (tempVal != 0)
{
digits[i++] = tempVal % base;
tempVal /= base;
}

// Get the format flags.
const std::ios_base::fmtflags flags = str.flags();

// Add the padding if needs by (i.e. they have used std::setw).
// Only applies if we are right aligned, or none specified.
if (flags & std::ios_base::right ||
!(flags & std::ios_base::internal || flags & std::ios_base::left))
{
std::fill_n(out, str.width() - i, fill);
}

if (val < 0)
{
*out++ = '-';
}

// Handle the internal adjustment flag.
if (flags & std::ios_base::internal)
{
std::fill_n(out, str.width() - i, fill);
}

char digitCharLc[] = "0123456789abcdefghijklmnopqrstuvwxyz";
char digitCharUc[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

const char *digitChar = (str.flags() & std::ios_base::uppercase)
? digitCharUc
: digitCharLc;

while (i)
{
// out is an iterator that accepts characters
*out++ = digitChar[digits[--i]];
}

// Add the padding if needs by (i.e. they have used std::setw).
// Only applies if we are left aligned.
if (str.flags() & std::ios_base::left)
{
std::fill_n(out, str.width() - i, fill);
}

// clear the width
str.width(0);

return out;
}

// Overrides for the virtual do_put member functions.

iter_type do_put(iter_type out, std::ios_base& str, char_type fill, long val) const
{
return doPutHelper(out, str, fill, val);
}

iter_type do_put(iter_type out, std::ios_base& str, char_type fill, unsigned long val) const
{
return doPutHelper(out, str, fill, val);
}
};

} // namespace StreamManip

int main()
{
// Create a local the uses our custom num_put
std::locale myLocale(std::locale(), new StreamManip::BaseNumPut());

// Set our locacle to the global one used by default in all streams created
// from here on in. Any streams created in this app will now support the
// StreamManip::base modifier.
std::locale::global(myLocale);

// imbue std::cout, so it uses are custom local.
std::cout.imbue(myLocale);
std::cerr.imbue(myLocale);

// Output some stuff.
std::cout << std::setw(50) << StreamManip::base(2) << std::internal << -255 << std::endl;
std::cout << StreamManip::base(4) << 255 << std::endl;
std::cout << StreamManip::base(8) << 255 << std::endl;
std::cout << StreamManip::base(10) << 255 << std::endl;
std::cout << std::uppercase << StreamManip::base(16) << 255 << std::endl;

return 0;
}


Related Topics



Leave a reply



Submit