I'm working on a stream filter that can decode a custom file format. My goal is to use boost::iostreams::filtering_istream
to read the file and process it with my boost::iostreams::multichar_input_filter
subclass, so that I can load values using the <<
operator.
I also want the process to be terminated when my filter cannot decode the stream and throws an exception, which happens when I compile the code using gcc 5.4 on Windows Subsystem for Linux, but the exception gets swallowed before it reaches my code if I compile with VS2017.
I'm using Boost 1.68.0 on both Windows and WSL; I've built and installed it using b2 on both platforms, without any custom arguments or config. I've also tried 1.58.0 on WSL, which comes from the package manager.
The project uses CMake, and I haven't customised anything in CMakeSettings.json or in launch.vs.json.
I've created this simplified code that shows how I use the filter chain, the exception class, and how I try to catch a processing error:
#include <iostream>
#include <boost/iostreams/concepts.hpp> // multichar_input_filter
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/throw_exception.hpp>
using namespace std;
namespace io = boost::iostreams;
class TestFilterException : public BOOST_IOSTREAMS_FAILURE {
public:
explicit TestFilterException(const char* message) : BOOST_IOSTREAMS_FAILURE(message) {
}
};
class TestFilter : public io::multichar_input_filter {
public:
explicit TestFilter() {
}
template <typename Source> streamsize read(Source& src, char* output_buffer, streamsize requested_char_count) {
BOOST_THROW_EXCEPTION(TestFilterException("Something went wrong"));
}
template <typename Source> void close(Source&) {
}
};
int main(const int argc, const char *argv[]) {
char buffer[64] = {'x'};
io::array_source source = io::array_source(buffer);
io::filtering_istream in;
in.push(TestFilter());
in.push(source);
char c;
try {
in >> c;
cout << c;
} catch (boost::exception& e) {
cout << "Expected exception";
return 1;
}
return 0;
}
I would expect this code to write the 'Expected exception' message to the output and exit with return code 1
on all platforms. However, when I compile it with Visual Studio, it outputs some garbage and returns code 0
.
I think it is a bug in the older gcc. Newer gcc and VS correctly catch exception thrown and set bad bit flag instead of propagating exception through stream methods. Garbage is printed because c
is left uninitialized after a failed read attempt. You can make stream throw a bad bit exception by setting exception flags in the stream:
try
{
io::filtering_istream in;
in.exceptions(::std::ios_base::badbit | ::std::ios_base::failbit | ::std::ios_base::eofbit);
in.push(TestFilter());
in.push(source);
char c;
in >> c;
cout << c;
} catch (boost::exception& e) {
cout << "not expected boost exception";
return 1;
}
catch(::std::exception const & e)
{
cout << "Expected std exception";
return 2;
}
also see iostream Exceptions documentation