Increase serialization 1.5.5 crash when meeting with Nan and Inf

It seems that increasing serialization cannot restore the value of Nan and inf from text archives.

The program will end if you do not handle archive_exception in this case, any solutions for this?

+6
source share
4 answers

The author of the library has this to say :

The simple truth is that I never consider it.

When he last appeared, I really didn’t think about it as much as I did other things, and I hoped that the interested parties [d] could reach a consensus without my desire to bend my over-stretched brain.

(further workarounds are discussed)

This seems correct, in my test only binary archives support inf / nan.

Xml and text archives support the entire range of precision except nan / inf:

Live on coliru

 using BIA = boost::archive::binary_iarchive; using BOA = boost::archive::binary_oarchive; using TIA = boost::archive::text_iarchive; using TOA = boost::archive::text_oarchive; using XIA = boost::archive::xml_iarchive; using XOA = boost::archive::xml_oarchive; int main() { // supported: assert((perform_test<BIA, BOA, use_nan, use_inf, use_range>())); assert((perform_test<XIA, XOA, no_nan, no_inf, use_range>())); assert((perform_test<TIA, TOA, no_nan, no_inf, use_range>())); // not supported: assert(!(perform_test<XIA, XOA, no_nan, use_inf>())); assert(!(perform_test<TIA, TOA, no_nan, use_inf>())); assert(!(perform_test<XIA, XOA, use_nan, no_inf>())); assert(!(perform_test<TIA, TOA, use_nan, no_inf>())); } 

Full list

For posterity:

 #include <boost/archive/xml_oarchive.hpp> #include <boost/archive/xml_iarchive.hpp> #include <boost/archive/binary_oarchive.hpp> #include <boost/archive/binary_iarchive.hpp> #include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_iarchive.hpp> #include <boost/serialization/vector.hpp> #include <sstream> using namespace boost::archive; static bool equal_or_nan(double a, double b) { return (std::isnan(a) && std::isnan(b)) || a==b; } template <typename IA, typename OA, bool withNan = true, bool withInf = true, bool withRange = true> bool perform_test() { std::vector<double> const v { withRange? std::numeric_limits<double>::min() : 0, withRange? std::numeric_limits<double>::max() : 0, withRange? std::numeric_limits<double>::epsilon() : 0, withNan? std::numeric_limits<double>::quiet_NaN() : 0, withInf? std::numeric_limits<double>::infinity() : 0, withInf? - std::numeric_limits<double>::infinity() : 0, }; std::stringstream ss; { OA oa(ss); oa << boost::serialization::make_nvp("element", v); } try { IA ia(ss); std::vector<double> w; ia >> boost::serialization::make_nvp("element", w); return std::equal(v.begin(), v.end(), w.begin(), equal_or_nan); } catch(...) { return false; } } static constexpr bool use_inf = true, use_nan = true, use_range = true; static constexpr bool no_inf = false, no_nan = false, no_range = false; using BIA = boost::archive::binary_iarchive; using BOA = boost::archive::binary_oarchive; using TIA = boost::archive::text_iarchive; using TOA = boost::archive::text_oarchive; using XIA = boost::archive::xml_iarchive; using XOA = boost::archive::xml_oarchive; int main() { // supported: assert((perform_test<BIA, BOA, use_nan, use_inf, use_range>())); assert((perform_test<XIA, XOA, no_nan, no_inf, use_range>())); assert((perform_test<TIA, TOA, no_nan, no_inf, use_range>())); // not supported: assert(!(perform_test<XIA, XOA, no_nan, use_inf>())); assert(!(perform_test<TIA, TOA, no_nan, use_inf>())); assert(!(perform_test<XIA, XOA, use_nan, no_inf>())); assert(!(perform_test<TIA, TOA, use_nan, no_inf>())); } 
+9
source

Check out this thread. There are facets in boost / math / special_functions (see Question) and how to apply them (see Answer).

+1
source

Helper template functions or overloaded operators (for example, <&) can be used to invoke the stream selector, a specialized static template class. The stream selector specialization then selects the correct streaming functions based on a serialized type:

 template <typename T> boost::archive::xml_iarchive& stream(boost::archive::xml_iarchive& ia, const boost::serialization::nvp<T>& nvp) { return StreamSelector<T>::stream(ia, nvp); } template <typename T> boost::archive::xml_oarchive& stream(boost::archive::xml_oarchive& oa, const boost::serialization::nvp<T>& nvp) { return StreamSelector<T>::stream(oa, nvp); } 

A static thread selector pattern template can be defined for any type supported by boost::archive::xml_iarchive as follows:

 template <typename T> class StreamSelector { public: static boost::archive::xml_iarchive& stream(boost::archive::xml_iarchive& ia, const boost::serialization::nvp<T>& nvp) { ia.operator >>(nvp); return ia; } static boost::archive::xml_oarchive& stream(boost::archive::xml_oarchive& oa, const boost::serialization::nvp<T>& nvp) { oa.operator <<(nvp); return oa; } }; 

Then the stream selector can be specialized for double for processing NaN or for outputting floating point values ​​to a more readable format in the archive:

 template <> class StreamSelector<double> { public: constexpr static double nan = std::numeric_limits<double>::quiet_NaN(); constexpr static const char* nanCStr = "nan"; static boost::archive::xml_iarchive& stream(boost::archive::xml_iarchive& ia, const boost::serialization::nvp<double>& nvp) { std::string iStr; ia >> boost::serialization::make_nvp(nvp.name(), iStr); if(iStr == nanCStr) nvp.value() = nan; else nvp.value() = std::stod(iStr); return ia; } static boost::archive::xml_oarchive& stream(boost::archive::xml_oarchive& oa, const boost::serialization::nvp<double>& nvp) { if(std::isnan(nvp.value())) { std::string nanStr = nanCStr; oa << boost::serialization::make_nvp(nvp.name(), nanStr); } else { std::stringstream oStrm; oStrm << std::setprecision(std::numeric_limits<double>::digits10 + 1) << nvp.value(); std::string oStr = oStrm.str(); oa << boost::serialization::make_nvp(nvp.name(), oStr); } return oa; } }; 

Other similar stream selector specializations can be added to handle more types (e.g. float, long double) and more special cases such as infinities.

Live demo: Open at Coliru

+1
source

Firstly, this is not a problem related to the increase in archives, it is an iostream function, that is, streaming text. You can pass the value of NaN, but you cannot read it. I'm not even sure that it is “defined” as NaN prints.

If you are planning on “planning” your boost library, the place to do this is in

 boost/archive/basic_text_iprimitive.hpp template< class IStream > class basic_text_iprimitive 

Method

  template<class T> void load(T & t) 

There is an overload for char signed by char and unsigned char, but not double.

If you don't like modifying the boost header (and I don't like touching them), take advantage of the fact that they are templates and can be specialized.

Put this patch in your code in the header and include it before reading the archive.

 namespace boost { namespace archive { template<> template<> void basic_text_iprimitive< std::istream >::load<double>( double& t ) { // fix to handle NaNs } } } // close namespaces 

From my own test run, the std::istream template type (or, rather, std::basic_istream< char, std::char_traits<char> > )

If you find that this does not work, write similar overloads for other input streams (and, of course, do them all forward for one implementation).

What needs to be added to the Fix section? Reinforcing a well actually creates a facet for reading NaN, you just need to make sure that you create a locale for it and put it in the archive.

Put the implementation in a C ++ file and make sure that this locale is created only once:

 std::locale infLocale( std::locale(), new boost::math::nonfinite_num_get<char>)); 

You can then infiltrate your stream before reading:

 is.imbue( infLocale ) 

(You can also do this imbue the moment you first download and read the XML, and if you only have one library to read the XML, do it this way, but if you do it in different places in your code, this is not ideal).

Of course, to ensure consistency, you will want to use a similar language standard for writing NaN. For this, boost has a nonfinite_num_put phase. You can put this in the same locale as nonfinite_num_get, and then embed it in the stream by creating a file descriptor for XML or specializing a template method.

+1
source

Source: https://habr.com/ru/post/982944/


All Articles