Skip to content

Commit

Permalink
initial deferment implementation
Browse files Browse the repository at this point in the history
relates USCiLab#185
  • Loading branch information
AzothAmmo authored and jrmadsen committed Jul 7, 2019
1 parent eddfb30 commit 3ee1826
Show file tree
Hide file tree
Showing 7 changed files with 393 additions and 4 deletions.
25 changes: 25 additions & 0 deletions include/cereal/archives/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,31 @@ namespace cereal
void epilogue( JSONInputArchive &, NameValuePair<T> const & )
{ }

// ######################################################################
//! Prologue for deferred data for JSON archives
/*! Do nothing for the defer wrapper */
template <class T> inline
void prologue( JSONOutputArchive &, DeferredData<T> const & )
{ }

//! Prologue for deferred data for JSON archives
template <class T> inline
void prologue( JSONInputArchive &, DeferredData<T> const & )
{ }

// ######################################################################
//! Epilogue for deferred for JSON archives
/*! NVPs do not start or finish nodes - they just set up the names */
template <class T> inline
void epilogue( JSONOutputArchive &, DeferredData<T> const & )
{ }

//! Epilogue for deferred for JSON archives
/*! Do nothing for the defer wrapper */
template <class T> inline
void epilogue( JSONInputArchive &, DeferredData<T> const & )
{ }

// ######################################################################
//! Prologue for SizeTags for JSON archives
/*! SizeTags are strictly ignored for JSON, they just indicate
Expand Down
25 changes: 25 additions & 0 deletions include/cereal/archives/xml.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,31 @@ namespace cereal
void epilogue( XMLInputArchive &, NameValuePair<T> const & )
{ }

// ######################################################################
//! Prologue for deferred data for XML archives
/*! Do nothing for the defer wrapper */
template <class T> inline
void prologue( XMLOutputArchive &, DeferredData<T> const & )
{ }

//! Prologue for deferred data for XML archives
template <class T> inline
void prologue( XMLInputArchive &, DeferredData<T> const & )
{ }

// ######################################################################
//! Epilogue for deferred for XML archives
/*! NVPs do not start or finish nodes - they just set up the names */
template <class T> inline
void epilogue( XMLOutputArchive &, DeferredData<T> const & )
{ }

//! Epilogue for deferred for XML archives
/*! Do nothing for the defer wrapper */
template <class T> inline
void epilogue( XMLInputArchive &, DeferredData<T> const & )
{ }

// ######################################################################
//! Prologue for SizeTags for XML output archives
/*! SizeTags do not start or finish nodes */
Expand Down
53 changes: 49 additions & 4 deletions include/cereal/cereal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <type_traits>
#include <string>
#include <memory>
#include <functional>
#include <unordered_map>
#include <unordered_set>
#include <vector>
Expand Down Expand Up @@ -97,6 +98,16 @@ namespace cereal
return {std::forward<T>(sz)};
}

// ######################################################################
//! Marks data for deferred serialization
/*! @relates DeferredData
@ingroup Utility */
template <class T> inline
DeferredData<T> defer( T && value )
{
return {std::forward<T>(value)};
}

// ######################################################################
//! Called before a type is serialized to set up any special archive state
//! for processing some type
Expand Down Expand Up @@ -266,14 +277,14 @@ namespace cereal
//! Indicates this archive is not intended for loading
/*! This ensures compatibility with boost archive types. If you are transitioning
from boost, you can check this value within a member or external serialize function
(i.e., Archive::is_loading::value) to disable behavior specific to loading, until
(i.e., Archive::is_loading::value) to disable behavior specific to loading, until
you can transition to split save/load or save_minimal/load_minimal functions */
using is_loading = std::false_type;

//! Indicates this archive is intended for saving
/*! This ensures compatibility with boost archive types. If you are transitioning
from boost, you can check this value within a member or external serialize function
(i.e., Archive::is_saving::value) to enable behavior specific to loading, until
(i.e., Archive::is_saving::value) to enable behavior specific to loading, until
you can transition to split save/load or save_minimal/load_minimal functions */
using is_saving = std::true_type;

Expand Down Expand Up @@ -346,6 +357,12 @@ namespace cereal
return id->second;
}

void serializeDeferments()
{
for( auto & deferment : itsDeferments )
deferment();
}

private:
//! Serializes data after calling prologue, then calls epilogue
template <class T> inline
Expand Down Expand Up @@ -387,6 +404,17 @@ namespace cereal
return *self;
}

std::vector<std::function<void(void)>> itsDeferments;

template <class T> inline
ArchiveType & processImpl(DeferredData<T> const & d)
{
std::function<void(void)> deferment( [=](){ self->process( d.value ); } );
itsDeferments.emplace_back( std::move(deferment) );

return *self;
}

//! Helper macro that expands the requirements for activating an overload
/*! Requirements:
Has the requested serialization function
Expand Down Expand Up @@ -635,14 +663,14 @@ namespace cereal
//! Indicates this archive is intended for loading
/*! This ensures compatibility with boost archive types. If you are transitioning
from boost, you can check this value within a member or external serialize function
(i.e., Archive::is_loading::value) to enable behavior specific to loading, until
(i.e., Archive::is_loading::value) to enable behavior specific to loading, until
you can transition to split save/load or save_minimal/load_minimal functions */
using is_loading = std::true_type;

//! Indicates this archive is not intended for saving
/*! This ensures compatibility with boost archive types. If you are transitioning
from boost, you can check this value within a member or external serialize function
(i.e., Archive::is_saving::value) to disable behavior specific to loading, until
(i.e., Archive::is_saving::value) to disable behavior specific to loading, until
you can transition to split save/load or save_minimal/load_minimal functions */
using is_saving = std::false_type;

Expand Down Expand Up @@ -728,6 +756,12 @@ namespace cereal
itsPolymorphicTypeMap.insert( {stripped_id, name} );
}

void serializeDeferments()
{
for( auto & deferment : itsDeferments )
deferment();
}

private:
//! Serializes data after calling prologue, then calls epilogue
template <class T> inline
Expand Down Expand Up @@ -769,6 +803,17 @@ namespace cereal
return *self;
}

std::vector<std::function<void(void)>> itsDeferments;

template <class T> inline
ArchiveType & processImpl(DeferredData<T> const & d)
{
std::function<void(void)> deferment( [=](){ self->process( d.value ); } );
itsDeferments.emplace_back( std::move(deferment) );

return *self;
}

//! Helper macro that expands the requirements for activating an overload
/*! Requirements:
Has the requested serialization function
Expand Down
34 changes: 34 additions & 0 deletions include/cereal/details/helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,10 @@ namespace cereal
namespace detail
{
struct NameValuePairCore {}; //!< Traits struct for NVPs
struct DeferredDataCore {}; //!< Traits struct for DeferredData
}

// ######################################################################
//! For holding name value pairs
/*! This pairs a name (some string) with some value such that an archive
can potentially take advantage of the pairing.
Expand Down Expand Up @@ -220,6 +222,38 @@ namespace cereal
uint64_t size; //!< size in bytes
};

// ######################################################################
template <class T>
class DeferredData : detail::DeferredDataCore
{
private:
// If we get passed an array, keep the type as is, otherwise store
// a reference if we were passed an l value reference, else copy the value
using Type = typename std::conditional<std::is_array<typename std::remove_reference<T>::type>::value,
typename std::remove_cv<T>::type,
typename std::conditional<std::is_lvalue_reference<T>::value,
T,
typename std::decay<T>::type>::type>::type;

// prevent nested nvps
static_assert( !std::is_base_of<detail::DeferredDataCore, T>::value,
"Cannot defer DeferredData" );

DeferredData & operator=( DeferredData const & ) = delete;

public:
//! Constructs a new NameValuePair
/*! @param v The value to defer. Ideally this should be an l-value reference so that
the value can be both loaded and saved to. If you pass an r-value reference,
the DeferredData will store a copy of it instead of a reference. Thus you should
only pass r-values in cases where this makes sense, such as the result of some
size() call.
@internal */
DeferredData( T && v ) : value(std::forward<T>(v)) {}

Type value;
};

// ######################################################################
namespace detail
{
Expand Down
19 changes: 19 additions & 0 deletions unittests/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,20 @@ void check_collection( T const & a, T const & b )
CHECK_EQ( *aIter, *bIter );
}

template <class T> inline
void check_ptr_collection( T const & a, T const & b )
{
auto aIter = std::begin(a);
auto aEnd = std::end(a);
auto bIter = std::begin(b);
auto bEnd = std::end(b);

CHECK_EQ( std::distance(aIter, aEnd), std::distance(bIter, bEnd) );

for( ; aIter != aEnd; ++aIter, ++bIter )
CHECK_EQ( **aIter, **bIter );
}

// Random Number Generation ===============================================
template<class T> inline
typename std::enable_if<std::is_floating_point<T>::value, T>::type
Expand All @@ -110,6 +124,11 @@ random_value(std::mt19937 & gen)
return s;
}

size_t random_index( size_t min, size_t max, std::mt19937 & gen )
{
return std::uniform_int_distribution<size_t>( min, max )(gen);
}

template<class C> inline
std::basic_string<C> random_basic_string(std::mt19937 & gen)
{
Expand Down
52 changes: 52 additions & 0 deletions unittests/defer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Copyright (c) 2014, Randolph Voorhies, Shane Grant
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of cereal nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "defer.hpp"

TEST_SUITE("defer");

TEST_CASE("binary_defer")
{
test_defer<cereal::BinaryInputArchive, cereal::BinaryOutputArchive>();
}

TEST_CASE("portable_binary_defer")
{
test_defer<cereal::PortableBinaryInputArchive, cereal::PortableBinaryOutputArchive>();
}

TEST_CASE("xml_defer")
{
test_defer<cereal::XMLInputArchive, cereal::XMLOutputArchive>();
}

TEST_CASE("json_defer")
{
test_defer<cereal::JSONInputArchive, cereal::JSONOutputArchive>();
}

TEST_SUITE_END();
Loading

0 comments on commit 3ee1826

Please sign in to comment.