Deserialization of non-finite floating-point numbers fails even with appropriate facets - c++

I need to use Boost.Serialization to serialize floating-point numbers. Since NaN and infinites cannot natively be read from an input stream, I am trying to use the facets in boost/math/special_functions. I have tested them on my platform using code similar to the examples we can find here: http://www.boost.org/doc/libs/1_50_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/fp_facets/intro.html
However, the following code still fails to properly unserialize non-finite floating point values (an exception is thrown with description "input stream error").
#include <limits>
#include <locale>
#include <sstream>
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/math/special_functions/nonfinite_num_facets.hpp>
#include <boost/serialization/nvp.hpp>
struct Data {
float f;
Data() : f(std::numeric_limits<float>::quiet_NaN()) {}
template <class Archive>
void serialize(Archive & ar, unsigned const)
{
ar & BOOST_SERIALIZATION_NVP(f);
}
};
void test()
{
using namespace boost::archive;
Data d;
std::ostringstream oss;
xml_oarchive oar(oss);
oar << BOOST_SERIALIZATION_NVP(d);
//std::cout << oss.str() << std::endl;
std::istringstream iss(oss.str());
std::locale const new_loc(iss.getloc(), new boost::math::nonfinite_num_get<char>);
iss.imbue(new_loc);
xml_iarchive iar(iss);
iar >> BOOST_SERIALIZATION_NVP(d);
std::cout << d.f << std::endl;
}
Am I doing something wrong? Is there a problem with my Boost version or my platform? Is there a better solution? Any help would be greatly appreciated.

I have found the solution by reading the following implementation note:
http://www.boost.org/doc/libs/1_55_0/libs/serialization/doc/implementation.html#charencoding
When constructing an archive with the default flag, the stream's locale is changed to address character encoding issues, but this mechanism can be disabled using flag boost::archive::no_codecvt.
If I replace the line
xml_iarchive iar(iss);
with
xml_iarchive iar(iss, no_codecvt);
then it works.

Related

boost asio with little endian

I am integrating a library that requires little endian for length. It's formatted with little endian and then a custom serialized object. How do I convert 4 byte char into a int? The little endian tells me the size of the serialized object to read.
so if I receive "\x00\x00\x00H\x00" I would like to be able to get the decimal value out.
my library looks like
char buffer_size[size_desc]
m_socket->receive(boost::asio::buffer(buffer, size_desc));
int converted_int = some_function(buffer); <-- not sure what to do here
char buffer_obj[converted_int];
m_socket->receive(boost::asio::buffer(buffer, size_desc));
For a simple solution you could do couple of tricks,
Reverse with a cast:
// #include <stdafx.h>
#include <cassert>
#include <iomanip>
#include <iostream>
#include <algorithm>
#include <string>
int main()
{
char buff[4] = {3,2,1,0};
std::cout << (*reinterpret_cast<int*>(&buff[0])) << "\n";
std::reverse(buff, buff+4);
std::cout << (*reinterpret_cast<int*>(&buff[0]));
return 0;
};
Boost also comes with an endianness library:
https://www.boost.org/doc/libs/1_74_0/libs/endian/doc/html/endian.html#buffers
You can use the built in types, like:
big_int32_t
little_int16_t

Using Boost::iostreams dual-use filters

I was attempting to follow the example of Finite State Filters in the Boost::iostreams documentation. However when I went to use the filter I got an error stating the ::imbue was not accessible because 'boost::iostreams::detail::finite_state_filter_impl' uses 'protected' to inherit from 'my_fsm'.
Frustrated I copied my code into the tests used to in the boost examples. The tests compile and pass. My conculsion is that I am probably mis-using the dual use filter defined by:
typedef io::finite_state_filter my_fsm_filter;
I feel that just pushing it onto a filtered_stream may not be proper, but I could not find a missing step. I am sure there must be a need to wrap the filter but I can find no example (though I am sure if I dug deep enough into the code used to test the boost code it has to be there somewhere).
here is a bit of example code:
#include <boost/mpl/vector.hpp>
#include <libs/iostreams/example/finite_state_filter.hpp>
namespace io = boost::iostreams;
struct my_fsm : io::finite_state_machine<my_fsm> {
BOOST_IOSTREAMS_FSM(my_fsm) // define skip and push.
typedef my_fsm self;
static const int beginline = 0;
static const int skipline = 1;
static const int dataline = 2;
typedef boost::mpl::vector <
row<beginline, is<'C'>, skipline, &self::skip>,
row<beginline, is_any, dataline, &self::push>,
row<skipline, is<'\n'>, beginline, &self::skip>,
row<skipline, is_any, skipline, &self::skip>,
row<dataline, is<'\n'>, beginline, &self::push>,
row<dataline, is_any, dataline, &self::push>
> transition_table;
};
typedef io::finite_state_filter<my_fsm> my_fsm_filter;
#include <iostream>
#include <string>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/stream.hpp>
namespace io = boost::iostreams;
int main() {
io::stream<io::file_sink> out(io::file_sink("outputfile.txt"));
io::filtering_istream in;
my_fsm_filter infsm;
in.push(my_fsm_filter());
in.push(io::file_source("inputdata.txt"));
while (in) {
std::string line;
if(std::getline(in, line)) {
//std::cout << line << std::endl;
out << line << std::endl;
}
}
return 0;
}
I personally feel that there is a bug in the sample header with respect to this imbue call.
However, you can work around it by changing the typedef to
struct my_fsm_filter : io::finite_state_filter<my_fsm> {
using io::finite_state_filter<my_fsm>::imbue;
};
This explicitly exposes the imbue method as public on the derived type. I haven't looked at the sample program that you reported to be working (because you didn't link to it). But it's possible they used a similar hack.
In my tests, a similar edit to finite_state_filte.hpp L278 to add
using base_type::imbue;
to class finite_state_filter has the same effect.

GCC, std::ctype specialisation & streams

I've written my own specialisation of each virtual member function of std::ctype<char16_t>, so that this now works:
#include <string>
#include <locale>
#include "char16_facets.h" // Header containing my ctype specialisation
#include <sstream>
#include <iostream>
// Implemented elsewhere using iconv
std::string Convert(std::basic_string<char16_t>);
int main() {
std::basic_string<char16_t> s("Hello, world.");
std::basic_stringstream<char16_t> ss(s);
ss.imbue(std::locale(ss.getloc(), new std::ctype<char16_t>()));
std::basic_string<char16_t> t;
ss >> t;
std::cout << Convert(t) << " ";
ss >> t;
std::cout << Convert(t) << std::endl;
}
Is there a way to make streams use the new ctype specialisation by default, so I don't have to imbue each stream with a new locale?
I haven't written a new class, just provided
template<>
inline bool std::ctype<char16_t>::do_is (std::ctype_base::mask m, char16_t c) const {
etc. I'd sort of hoped it would be picked up automatically, so long as it was declared before I #include <sstream> but it isn't.
Most of the work for the above was done using G++ and libstdc++ 4.8, but I get the same result with them built from SVN trunk.
Edit - Update This question originally asked about how to get number extraction working. However, given a stream imbued with correct ctype and numpunct implementations, then no specialisation of num_get is necessary; simply
ss.imbue(std::locale(ss.getloc(), new std::num_get<char16_t>()));
and it will work, with either gcc version.
Again, is there some way to get the streams to pick this up automatically, rather than having to imbue every stream with it?
Use std::locale::global():
std::locale::global(std::locale(std::locale(), new std::ctype<char16_t>()));

ostream::write not writing entire struct?

I'm trying to export various values, such as ints and simple structs, to a binary file. Here's some code:
#include &ltiostream&gt
#include &ltfstream&gt
#include &ltcstdint&gt
using namespace std;
template&ltclass T&gt void writeToStream(ostream& o, T& val)
{
o.write((char*)&val, sizeof(T));
cout &lt&lt o.tellp() &lt&lt endl; //always outputs 4
}
struct foo {
uint16_t a, b;
};
int main()
{
foo myFoo = {42, 42};
ofstream test("test.txt", ios::binary);
writeToStream(test, myFoo);
test.close();
}
The program should generate an output file 4 bytes long. But when I open it, it's only 2 bytes long. If I change myFoo.a and myFoo.b to contain values of 256 or more (requires more than 1 byte to store), then the file becomes 4 bytes long. I'm using the Visual Studio 11 Developer Preview on Win7; I haven't checked to see if the same happens on other systems or compilers. How can I make it output correctly for values of a or b under 256?
A file can only be read back by a program that understands the format in which it was stored. Notepad++ has no understanding of the format in which your file was stored, so it has no ability to read it back and render it sensibly. Either write the file in a format Notepad++ understands, such as ASCII text, or only read the file with a program that understand the format you wrote it in.
I have cleaned up your code as follows. Though I do not know why the old code output two bytes, the new code does output four.
#include <iostream>
#include <fstream>
#include <cstdint>
using std::cout;
using std::endl;
using std::uint16_t;
using std::ostream;
using std::ofstream;
using std::ios;
template <class T> void writeToStream(ostream& o, T& val)
{
o.write(reinterpret_cast<char *>(&val), sizeof(T));
cout << o.tellp() << endl; //always outputs 4
}
struct foo {
uint16_t a, b;
};
int main()
{
foo myFoo = {42, 42};
ofstream test("test.txt", ios::binary);
writeToStream(test, myFoo);
// Just let the stream "test" pass out of scope.
// It closes automatically.
//test.close();
return 0;
}
(My standard library lacks cstdint, so I used short rather than uint16_t, but I doubt that this matters.)
The std::ofstream type is derived from std::ostream. The writeToStream() function is happier, or at least more regular and more general, if passed a plain std::ostream. Also, for information: to issue using namespace std; is almost never recommended in C++.
Good luck.

Formatting the string YYYYMMDD as YYYY.MM.DD using Boost

I have a std::string such as 20040531, I want to format this as 2004.05.31.
Apart from the straight forward way of doing an std::insert at respective locations, is there a better way to do this using Boost?
PS. I cannot use other Boost calls to get date/time as this string is returned via a custom API. So this question is reduced to basic string formatting which may not sound exciting, but I am trying to learn Boost.
You could use boost::format...
#include <string>
#include "boost/format.hpp"
#include <iostream>
int main()
{
std::string a("20040531");
std::cout << boost::format("%1%.%2%.%3%")
% a.substr(0,4) % a.substr(4,2) % a.substr(6,2);
}
You specifically asked about doing this using Boost, but if you wanted to do this in C++ without introducing a dependency on Boost then you could just use a stream to achieve the same thing:
#include <sstream>
#include <string>
#include <iostream>
int main()
{
std::stringstream s;
std::string a("20040531");
s << a.substr(0,4) << '.' << a.substr(4,2) << '.' << a.substr(6,2);
std::cout << s.str();
}