当遇到Nan和Inf时,提升序列化1.5.5崩溃

时间:2015-02-24 13:08:11

标签: c++ boost boost-serialization

似乎boost序列化无法从基于文本的档案中恢复值Nan和inf。

除非您在这种情况下处理archive_exception,否则该程序将终止,除此之外的任何解决方案?

4 个答案:

答案 0 :(得分:9)

图书馆has this to say的作者:

  

简单的事实是我从不考虑这一点。

     

当它最后一次出现时,我并没有像我一样真的这么想    参与了其他事情,我希望有兴趣的人可以参加    没有我不得不屈服于过度伸展的大脑的共识。

     

继续讨论解决方法

这似乎是正确的,在我的测试中只有二进制存档支持inf / nan。

Xml和文本存档确实支持所有精度,除了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>()));

}

完整列表

后人:

#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>()));

}

答案 1 :(得分:2)

首先,这不是特定于提升档案的问题,它是iostream的一个特征,即文本流。您可以流出NaN值,但无法读回。我甚至不确定是否“定义”NaN将如何打印。

如果你要“修补”你的升级库,那么这个地方就在

boost/archive/basic_text_iprimitive.hpp

template< class IStream >
class basic_text_iprimitive

方法

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

char,signed char和unsigned char有一个重载但不是double。

如果您不喜欢修改提升标题(我不喜欢触及那些标题),那么请利用它们是模板化的并且可以专门化的事实。

将此修补程序放在代码中的某个位置,并在读取存档之前将其包含在内。

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

从我自己的测试运行中,模板化类型为std::istream(或者更准确std::basic_istream< char, std::char_traits<char> >

如果您发现它不起作用,那么为其他输入流编写类似的重载(当然,这些重载都会转发到一个实现)。

在“修复”部分放什么? Well boost实际上创建了一个用于读取NaN的方面,您只需确保为其创建了区域设置并将其填充到存档中。

将实现放在C ++文件中,并确保只创建一次该语言环境:

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

然后,您可以在阅读之前将其灌输到您的信息流中:

is.imbue( infLocale )

(你也可以在你第一次加载和阅读XML的时候注意这一点,如果你只有一个用于读取XML的库,那就这样做,但如果你在代码的不同位置这样做,那么这样做不理想)。

当然,为了确保一致性,您需要使用类似的区域设置来编写NaN。 boost有一个nonfinite_num_put方面。您可以将它放在与nonfinite_num_get相同的语言环境中,然后在为XML创建文件句柄或专门化模板方法时将其添加到流中。

答案 2 :(得分:1)

查看this主题。 boost / math / special_functions中有一些方面(参见问题)以及一种应用它们的方法(见答案)。

答案 3 :(得分:1)

辅助模板函数或重载运算符(例如&lt;&lt;,&amp;)可用于调用流选择器 - 专门的静态模板类。然后,流选择器专精将根据被序列化的类型选择正确的流功能:

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

可以为boost::archive::xml_iarchive支持的任何类型定义常规流选择器静态模板类,如下所示:

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

然后,流选择器可以专门用于double来处理NaN,或者以更易于阅读的格式将浮点值输出到存档:

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

可以添加其他类似的流选择器专精来处理更多类型(例如float,long double)和更多特殊情况,例如infinities。

现场演示: Open In Coliru