[Boost-users] Iostreams: how can I control my (ostream) filter?

81 views
Skip to first unread message

Akim Demaille

unread,
Nov 4, 2014, 7:56:36 AM11/4/14
to Boost Users List
Hi!

I'm trying to use Boost.Iostream to create a filter that
will escape some characters (typically backslash must be
backslashed). This is to conform with the syntax of an
output format that requires some portions to be escaped
this way. But other place must be left untouched.

My problem is I don't find a means to "talk" to my filter.

My first attempt used two streams: the "real" ostream
and the filtered one that wraps it. Of course it failed:
both streams are not synchronized, and buffers are
flushed at different moments resulting in scrambled
output. I don't want to flush too often (I got to
keep my throughput high), so that's ruled out.

My second attempt was to introduce an xalloc-based custom
iomanip to enable/disable the processing in my filter.
But my problem is that in my filter, I don't know how to
reach the final ostream and fetch it pword, so I failed
to have it work. From the syntax point of view (from
the caller viewpoint) that was my favorite approach.

In my third attempt, I have to boolean variable living
in the caller frame, taken as a bool& by my filter's
actor, and checked in its "write" function to decide
whether to escape or not. This looks worrying (bool&
members don't look nice), and anyway does not work nicely,
again, with buffers: the asynchrony between setting/unsetting
the boolean and using it breaks everything.

So, what are the good options? I think I really needed
something like an iomanipulator which ensures that "sequentiality"
is preserved, but how should I do that?

Thanks in advance!

PS/ One not-good-option would be to have my filter try
to "parse" what it is outputting to decide what's the
current state, and whether it should escape or not.
First, things become uselessly complex, and second, anyway
that's not right; for instance when I'm in a string,
double-quotes should be escaped, or mark the end of the string.
The filter cannot know which kind of double-quote it just
saw, but I know, so _I_ should change the state at that time,
not it.
_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users

Sylro

unread,
Nov 15, 2014, 1:03:06 PM11/15/14
to boost...@lists.boost.org
I have the same problem trying to escape XML : I want the filter to be
enabled when writing contents and attribute values, and disabled when
writing the markup.

I tried the same solutions as Akim, but ended up embedding "control
sequences" within the data to enable/disable the filter. From outside it's
not that bad since the control sequences are wrapped into manipulators, but
the implementation is simply horrible.

Is there any suggestion to solve our problem ? How can we control a filter
without having to sync ?
Thanks !



--
View this message in context: http://boost.2283326.n4.nabble.com/Iostreams-how-can-I-control-my-ostream-filter-tp4668716p4669093.html
Sent from the Boost - Users mailing list archive at Nabble.com.

Akim Demaille

unread,
Nov 16, 2014, 12:20:44 PM11/16/14
to Boost Users List

Le 15 nov. 2014 à 18:53, Sylro <syrmo...@hotmail.com> a écrit :

> I have the same problem trying to escape XML : I want the filter to be
> enabled when writing contents and attribute values, and disabled when
> writing the markup.
>
> I tried the same solutions as Akim, but ended up embedding "control
> sequences" within the data to enable/disable the filter. From outside it's
> not that bad since the control sequences are wrapped into manipulators, but
> the implementation is simply horrible.
>
> Is there any suggestion to solve our problem ? How can we control a filter
> without having to sync ?
> Thanks !

Hi Sylro,

FWIW, here is what I ended up doing. First, the
filter itself:

#pragma once

#include <iostream>

#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/char_traits.hpp> // EOF, WOULD_BLOCK
#include <boost/iostreams/concepts.hpp> // multichar_output_filter
#include <boost/iostreams/operations.hpp> // get

namespace vcsn
{
namespace detail
{
namespace io = boost::iostreams;

/// Backslash backslashes.
class backslashify_output_filter
: public io::multichar_output_filter
{
public:
explicit backslashify_output_filter()
{}

void enable()
{
enabled_ = true;
}

void disable()
{
enabled_ = false;
}

template <typename Sink>
std::streamsize
write(Sink& dest, const char* s, std::streamsize n)
{
std::streamsize z;
for (z = 0; z < n; ++z)
{
char c = s[z];
if (enabled_ && c == '\\')
if (!io::put(dest, '\\'))
// FIXME: probably lost a char here.
break;
if (!io::put(dest, c))
break;
}
return z;
}

bool enabled_ = false;
};
}
}

then the caller:

bos_ << " [label = \"";
enable_();
aut_->print_state_name(s, bos_, "text");
disable_();
boss << "\", shape = box";

where bos_ is the stream:

detail::io::filtering_ostream bos_;

initialized like this:

dotter(const automaton_t& aut, std::ostream& out,
bool dot2tex = false)
: super_t(aut, out)
, dot2tex_(dot2tex)
{
bos_.push(detail::backslashify_output_filter());
bos_.push(out);
}

Then, the enable/disable sequence is as follows:

/// Enable the escaping of backslashes.
void enable_()
{
boost::iostreams::flush(bos_);
bos_.component<detail::backslashify_output_filter>(0)->enable();
}

/// Disable the escaping of backslashes.
void disable_()
{
boost::iostreams::flush(bos_);
bos_.component<detail::backslashify_output_filter>(0)->disable();
}

where the calls to flush are extremely important. I have
_not_ implement flush in my filter, to avoid flushing
downstream and kill the buffers: only the buffers before
the filter and the filter itself are flushed, so that the
effect happens when it should.

Cheers!

Sylro

unread,
Nov 16, 2014, 3:12:41 PM11/16/14
to boost...@lists.boost.org
Hi Akim.

I see what you're trying to do : sync up to the filter, but not beyond,
would be a simple solution.
However boost::iostreams::flush() will always flush everything, up to the
final device.

For the demonstration, if you use this dummy_sink :

class dummy_sink
{
public:
typedef char char_type;
struct category : public boost::iostreams::sink_tag, public
boost::iostreams::flushable_tag {};

std::streamsize write(const char* s, std::streamsize n)
{
std::cout << "{" << std::string(s, s + n) << "}";
return n;
}

bool flush()
{
std::cout << " FLUSH ";
std::cout.flush();
return true;
}
};

With this example :
{
bos_ << " [label = \"";
enable_();
bos_ << "bla\\abla" << 1 << 2 << 3 << "blabla";
disable_();
bos_ << "\", shape = box";
}

Then the output looks like:
{ [label = "}
FLUSH
{bla\\abla123blabla}
FLUSH
{", shape = box}
FLUSH
FLUSH
FLUSH
FLUSH
FLUSH
FLUSH
FLUSH




--
View this message in context: http://boost.2283326.n4.nabble.com/Iostreams-how-can-I-control-my-ostream-filter-tp4668716p4669102.html
Sent from the Boost - Users mailing list archive at Nabble.com.

Akim Demaille

unread,
Nov 16, 2014, 3:21:47 PM11/16/14
to Boost Users List

Le 16 nov. 2014 à 21:06, Sylro <syrmo...@hotmail.com> a écrit :

> Hi Akim.
>
> I see what you're trying to do : sync up to the filter, but not beyond,
> would be a simple solution.
> However boost::iostreams::flush() will always flush everything, up to the
> final device.

Thanks for the counter-example! I have absolutely no desire
to design a protocol, as you did, but then, at this point, it
seems to be the only efficient solution :(
Reply all
Reply to author
Forward
0 new messages