2016-06-19 21 views
2

Ich möchte, dass die Deserialisierung fehlschlägt, wenn die Größe des Arrays, zu dem ich deserialisieren möchte, nicht mit der Größe des ursprünglich serialisierten Arrays übereinstimmt.Boost - Deserialisierung eines Arrays nur, wenn die Größen übereinstimmen

Bisher ist nur es schlägt fehl, wenn arr1_size > arr2_size und ich möchte es arr1_size != arr2_size sein:

#include <iostream> 
#include <sstream> 
#include <array> 

#include <boost/serialization/serialization.hpp> 
#include <boost/serialization/array.hpp> 
#include <boost/archive/text_oarchive.hpp> 
#include <boost/archive/text_iarchive.hpp> 

int main() 
{ 
    const size_t arr1_size = 4, arr2_size = 3; 
    std::stringstream ss; 

    // save 
    std::array<int, arr1_size> arr1; 
    boost::archive::text_oarchive oar(ss, boost::archive::no_header); 
    oar & arr1; 

    // load 
    std::array<int, arr2_size> arr2; 
    boost::archive::text_iarchive iar(ss, boost::archive::no_header); 
    iar & arr2; // throw on size inequality, please 
} 

Live on Coliru


ich dachte:

  • Serialisierung durch std::vector s und das selbst zu handhaben, aber das könnte zu Leistungsverlust führen

  • Überprüfung arr2 danach (wenn es nicht auf arr1_size > arr2_size werfen nicht) für Hinterstandard konstruierte Klassentyp Elemente oder anderweitig besondere Werte) zu handhaben arr1_size < arr2_size

Gibt es etwas einfacher, vorzugsweise durch Boost, die ich verpasst habe?

+1

Ich denke, Sie müssen entweder die Größe des Arrays vor dem Array selbst serialisieren und dann auf Deserialisierung überprüfen oder zu einem Vektor wechseln (wie viel Leistungsverlust sprechen wir hier wirklich?) –

+0

@RichardHodges Thanks für den dritten Weg wären die Elemente vom Typ abgeleitet von "boost :: bimap" - nicht beweglich, so scheint es. – LogicStuff

Antwort

2

Here ist der Boost-Code, den Sie, speziell dieser Test unzureichend umgehen möchten:

if(static_cast<std::size_t>(count) > current_count) 
     boost::serialization::throw_exception(
      archive::archive_exception(
       boost::archive::archive_exception::array_size_too_short 
      ) 
     ); 

Eine Abhilfe ist, ersetzen Sie Ihre eigene Serialisierung für std::array. Dies ist am einfachsten, wenn Sie vermeiden können, dass die Kopfzeile boost/serialization/array.hpp für jede Übersetzungseinheit, die Sie serialisieren, enthält. Es ist immer noch möglich, wenn Sie diese Header-Datei benötigen (zB gewöhnlichen Arrays serialisiert) - den Trick, um die Boost templated function passend zu vermeiden:

template <class Archive, class T, std::size_t N> 
void serialize(Archive& ar, std::array<T,N>& a, const unsigned int /* version */) 
... 

Eine Möglichkeit, dies zu tun, ist explizit Ihren Elementtyp angeben:

typedef int MyArrayElementType; 

namespace std { 
    template<class Archive, size_t N> 
    void serialize(Archive& ar, std::array<MyArrayElementType, N>& a, const unsigned int version) 
    ... 

Hier ist eine Anpassung Ihrer MCVE:

#include <iostream> 
#include <sstream> 
#include <array> 

#include <boost/serialization/serialization.hpp> 
#include <boost/serialization/split_free.hpp> 
#include <boost/serialization/array.hpp> 
#include <boost/archive/text_oarchive.hpp> 
#include <boost/archive/text_iarchive.hpp> 

// Supply your element type here. 
typedef int MyArrayElementType; 

namespace std { 
    template<class Archive, size_t N> 
    void serialize(Archive& ar, std::array<MyArrayElementType, N>& a, const unsigned int version) { 
     boost::serialization::split_free(ar, a, version); 
    } 

    template<class Archive, size_t N> 
    void save(Archive& ar, const std::array<MyArrayElementType, N>& a, const unsigned int version) { 
     // Adapted code from oserializer.hpp save_array_type::invoke(). 
     boost::serialization::collection_size_type count(N); 
     ar << BOOST_SERIALIZATION_NVP(count); 
     ar << boost::serialization::make_array(static_cast<MyArrayElementType const*>(&a[0]), count); 
    } 

    template<class Archive, size_t N> 
    void load(Archive& ar, std::array<MyArrayElementType, N>& a, const unsigned int version) { 
     // Adapted code from iserializer.hpp load_array_type::invoke(). 
     boost::serialization::collection_size_type count; 
     ar >> BOOST_SERIALIZATION_NVP(count); 
     if(static_cast<std::size_t>(count) != N) 
     boost::serialization::throw_exception(
      std::runtime_error("std::array size mismatch") 
      ); 
     ar >> boost::serialization::make_array(static_cast<MyArrayElementType*>(&a[0]), count); 
    } 
} 

int main() 
{ 
    const size_t arr1_size = 3, arr2_size = 4; 
    std::stringstream ss; 

    // save 
    std::array<int, arr1_size> arr1; 
    boost::archive::text_oarchive oar(ss, boost::archive::no_header); 
    oar & arr1; 

    // load 
    std::array<int, arr2_size> arr2; 
    boost::archive::text_iarchive iar(ss, boost::archive::no_header); 
    iar & arr2; // throw on size inequality, please 
} 

Live on CoLiRu

Dies verwendet dieselbe Array-Serialisierungsmaschinerie wie die integrierte Serialisierung, daher sollte sie genau die gleiche Leistung haben. Wenn Sie boost/serialization/array.hpp entfernen können, können Sie stattdessen MyArrayElementType in ein Vorlagenargument ändern.

+0

Cool, aber fügt diese Funktionsvorlagen 'std' Namespace UB nicht hinzu? Ich könnte leben, ohne "Operator" zu verwenden und expliziter zu sein. – LogicStuff

+1

Sie haben Recht, dass das Hinzufügen von 'std'-Funktionen ein offiziell undefiniertes Verhalten ist, obwohl ich denke, dass dies weit verbreitet ist. Wenn Sie möchten, können Sie 'load()' und 'save()' explizit mit einem Namespace Ihrer Wahl aufrufen. – rhashimoto