как десериализовать QNan и SNan в boost :: serialization [duplicate]

Это очень распространенная проблема, с которой мы сталкиваемся, борясь с «таинствами» JavaScript.

Давайте начнем с простой функции JavaScript:

function foo(){
// do something 
 return 'wohoo';
}

let bar = foo(); // bar is 'wohoo' here

Это простой синхронный вызов функции (где каждая строка кода выполняется одна за другой в последовательность), и результат будет таким же, как ожидалось.

Теперь добавим немного завихрения, введя небольшую задержку в нашей функции, чтобы все строки кода не выполнялись последовательно. Таким образом, он будет эмулировать асинхронное поведение функции:

function foo(){
 setTimeout( ()=>{
   return 'wohoo';
  }, 1000 )
}

let bar = foo() // bar is undefined here

Итак, вы идете, эта задержка просто сломала функциональность, которую мы ожидали! Но что именно произошло? Ну, на самом деле это довольно логично, если вы посмотрите на код. функция foo() после выполнения ничего не возвращает (таким образом, возвращаемое значение равно undefined), но оно запускает таймер, который выполняет функцию после 1s, чтобы вернуть «wohoo». Но, как вы можете видеть, значение, присвоенное бару, является немедленно возвращенным материалом из foo (), а не что-либо еще, что приходит позже.

Итак, как мы решаем эту проблему?

Давайте попросим нашу функцию для ОБЕЩАНИЯ. Обещание действительно о том, что это означает: это означает, что функция гарантирует, что вы предоставите любой результат, который он получит в будущем. поэтому давайте посмотрим на это в нашей маленькой проблеме выше:

function foo(){
   return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
    setTimeout ( function(){ 
      // promise is RESOLVED , when exececution reaches this line of code
       resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
    }, 1000 )
  })
}

let bar ; 
foo().then( res => {
 bar = res;
 console.log(bar) // will print 'wohoo'
});

Таким образом, резюме - для решения асинхронных функций, таких как вызовы на основе ajax и т. д., вы можете использовать обещание resolve значение (которое вы намерены вернуть). Таким образом, короче говоря, вы разрешаете значение вместо возврата в асинхронных функциях.

6
задан sehe 24 February 2015 в 18:04
поделиться

4 ответа

Во-первых, это не проблема для повышения архивов, это функция iostream, то есть потоковая передача текста. Вы можете передать значение NaN, но вы не можете его прочитать. Я даже не уверен, что он «определен», как печатает NaN.

Если вы собираетесь «исправлять» вашу библиотеку boost, то место, которое нужно сделать, это в

boost/archive/basic_text_iprimitive.hpp

template< class IStream >
class basic_text_iprimitive

method

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

перегрузка для char, подписанный символ char и unsigned, но не двойной.

Если вам не нравится изменять заголовок boost (и мне не нравится касаться их), воспользуйтесь тем, что они шаблоны и могут быть специализированными.

Put это патч где-то в вашем коде в заголовке и включить его перед чтением архива.

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> >),

Если вы обнаружите, что это не сработает, напишите аналогичные перегрузки для других входных потоков (и, конечно же, сделайте их все первыми для одной реализации).

Что помещать в " исправить "? Усиление скважины фактически создает грань для чтения 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, либо в специализированном методе шаблона.

1
ответ дан CashCow 28 August 2018 в 05:30
поделиться

Посмотрите на эту тему . Есть фасетки в boost / math / special_functions (см. Вопрос) и способ их применения (см. Ответ).

1
ответ дан Community 28 August 2018 в 05:30
поделиться

Автор библиотеки имеет это, чтобы сказать :

Простая истина заключается в том, что я никогда не рассматриваю это.

Когда он появился в последний раз, я не думал об этом очень сильно, так как я был вовлечен в другие вещи, и я надеялся, что заинтересованные стороны [d] могут прийти к консенсусу без моего изгиба

( продолжает обсуждать обходные пути )

Это кажется правильным, в моем тестировании поддерживаются только двоичные архивы инф / нан.

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

}
9
ответ дан sehe 28 August 2018 в 05:30
поделиться

Функции шаблона-помощника или перегруженные операторы (например, & 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) и более специальных случаев, таких как бесконечности.

Live demo: Open In Coliru

1
ответ дан Victor Rybynok 28 August 2018 в 05:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: