From 5eecca84937e2df5eee95f430ce518baa854f1f7 Mon Sep 17 00:00:00 2001 From: Matthew Whitlock Date: Thu, 30 May 2024 11:19:41 -0700 Subject: [PATCH] #344 Implement user traits mechanisms Also adds is_footprinter_v --- ...checkpoint_example_to_file_nonintrusive.cc | 11 +- examples/checkpoint_example_user_traits.cc | 60 ++++++ examples/checkpoint_example_user_traits.hpp | 66 ++++++ src/checkpoint/checkpoint_api.h | 26 +-- src/checkpoint/checkpoint_api.impl.h | 54 ++--- src/checkpoint/container/atomic_serialize.h | 2 +- src/checkpoint/container/function_serialize.h | 4 +- .../kokkos_unordered_map_serialize.h | 4 +- src/checkpoint/container/list_serialize.h | 6 +- src/checkpoint/container/map_serialize.h | 4 +- src/checkpoint/container/queue_serialize.h | 2 +- src/checkpoint/container/raw_ptr_serialize.h | 4 +- .../container/shared_ptr_serialize.h | 2 +- src/checkpoint/container/thread_serialize.h | 2 +- src/checkpoint/container/vector_serialize.h | 10 +- src/checkpoint/dispatch/dispatch.h | 21 +- src/checkpoint/dispatch/dispatch.impl.h | 46 ++-- .../dispatch/dispatch_serializer_nonbyte.h | 2 +- .../dispatch/vrt/serializer_registry.h | 2 +- .../dispatch/vrt/virtual_serialize.h | 8 +- src/checkpoint/serializers/footprinter.h | 6 + src/checkpoint/serializers/serializer_ref.h | 173 +++++++++++++++ .../serializers/serializers_headers.h | 21 +- src/checkpoint/traits/serializable_traits.h | 2 +- src/checkpoint/traits/user_traits.h | 196 +++++++++++++++++ tests/unit/test_user_traits.cc | 204 ++++++++++++++++++ 26 files changed, 824 insertions(+), 114 deletions(-) create mode 100644 examples/checkpoint_example_user_traits.cc create mode 100644 examples/checkpoint_example_user_traits.hpp create mode 100644 src/checkpoint/serializers/serializer_ref.h create mode 100644 src/checkpoint/traits/user_traits.h create mode 100644 tests/unit/test_user_traits.cc diff --git a/examples/checkpoint_example_to_file_nonintrusive.cc b/examples/checkpoint_example_to_file_nonintrusive.cc index e369b5be..b3a99869 100644 --- a/examples/checkpoint_example_to_file_nonintrusive.cc +++ b/examples/checkpoint_example_to_file_nonintrusive.cc @@ -140,18 +140,19 @@ int main(int, char**) { // Call the serialization routine for the variable `my_test_inst` // The output is a unique pointer: `std::unique_ptr` // (defined in `src/checkpoint_api.h`) - magistrate::serializeToFile(my_test_inst, "hello.txt"); + std::string my_filename = "CheckpointExampleToFileNoninstrusive.txt"; + magistrate::serializeToFile(my_test_inst, my_filename); // // De-serializes from the file an object of type 'MyTestType' // out will be an object of type 'std::unique_ptr' // - auto out = magistrate::deserializeFromFile("hello.txt"); + auto out = magistrate::deserializeFromFile(my_filename); if (my_test_inst == *out) { std::cout << " Serialization / Deserialization from file worked. \n"; } else { - std::cout << " Serialization / Deserialization from file failed. \n"; + std::cout << " Serialization / Deserialization from file failed. \n" << std::flush; assert(false); } @@ -164,7 +165,7 @@ int main(int, char**) { // Here 'out_2' will contain an empty vector and an integer 'len_' set to 0. // - magistrate::deserializeInPlaceFromFile("hello.txt", &out_2); + magistrate::deserializeInPlaceFromFile(my_filename, &out_2); // // Now 'out_2' will contain: @@ -175,7 +176,7 @@ int main(int, char**) { if (my_test_inst == out_2) { std::cout << " Deserialization in-place from file worked. \n"; } else { - std::cout << " Deserialization in-place from file failed. \n"; + std::cout << " Deserialization in-place from file failed. \n" << std::flush; assert(false); } diff --git a/examples/checkpoint_example_user_traits.cc b/examples/checkpoint_example_user_traits.cc new file mode 100644 index 00000000..350df995 --- /dev/null +++ b/examples/checkpoint_example_user_traits.cc @@ -0,0 +1,60 @@ +/* +//@HEADER +// ***************************************************************************** +// +// checkpoint_example_user_traits.cc +// DARMA/magistrate => Serialization Library +// +// Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. +// +// 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 the copyright holder 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. +// +// Questions? Contact darma@sandia.gov +// +// ***************************************************************************** +//@HEADER +*/ +#include "checkpoint/checkpoint.h" + +#include "checkpoint_example_user_traits.hpp" + +int main(int, char**){ + test::TestObj obj; + + //Each invocation will be handled based on the traits attached + auto s_info_a = checkpoint::serialize(obj); + auto s_info_b = checkpoint::serialize(obj); + auto s_info_c = checkpoint::serialize(obj); + auto s_info_d = checkpoint::serialize(obj); + auto s_info_e = checkpoint::serialize(obj); + auto s_info_f = checkpoint::serialize(obj); + auto s_info_g = checkpoint::serialize(obj); + auto s_info_h = checkpoint::serialize(obj); + auto s_info_i = checkpoint::serialize(obj); +} diff --git a/examples/checkpoint_example_user_traits.hpp b/examples/checkpoint_example_user_traits.hpp new file mode 100644 index 00000000..6f48c264 --- /dev/null +++ b/examples/checkpoint_example_user_traits.hpp @@ -0,0 +1,66 @@ +#include "checkpoint/checkpoint.h" + +struct checkpoint_trait {} CheckpointTrait; +struct shallow_trait {} ShallowTrait; + +namespace test { + struct random_trait {} RandomTrait; + + struct TestObj { + int a = 1; + + TestObj() {} + + template::type = nullptr> + void serialize(SerT& s){ + if constexpr(SerT::template has_traits::value){ + if(s.isSizing()) printf("Customizing serialization for checkpoint\n"); + s | a; + } else { + if(s.isSizing()) printf("Default serializing testObj\n"); + } + + static_assert(SerT::template has_not_traits::value, "ShallowTrait should have been removed!\n"); + } + }; +} + +namespace test { + template::type = nullptr> + void serialize(SerT& s, TestObj& myObj){ + if(s.isSizing()) printf("Inserting random extra object serialization step! "); + myObj.serialize(s); + } + + template::type = nullptr> + void serialize(SerT& s, TestObj& myObj){ + if(s.isSizing()) printf("Removing shallow trait before passing along!\n"); + auto newS = s.template withoutTraits(); + myObj.serialize(newS); + } +} + +namespace misc { + template::type = nullptr> + void serialize(SerT& s, test::TestObj& myObj){ + if(s.isSizing()) printf("Serializers in other namespaces don't usually get found "); + myObj.serialize(s); + } + + + const struct namespace_trait {} NamespaceTrait; + template::type = nullptr> + void serialize(SerT& s, test::TestObj& myObj){ + if(s.isSizing()) printf("A misc:: trait means we can serialize from misc:: too: "); + myObj.serialize(s); + } + + + const struct hook_all_trait {} HookAllTrait; + template::type = nullptr> + void serialize(SerT& s, T& myObj){ + if(s.isSizing()) printf("We can even add on a generic pre-serialize hook: "); + auto newS = s.template withoutTraits(); + myObj.serialize(newS); + } +} diff --git a/src/checkpoint/checkpoint_api.h b/src/checkpoint/checkpoint_api.h index cd123cf4..2dbb50cf 100644 --- a/src/checkpoint/checkpoint_api.h +++ b/src/checkpoint/checkpoint_api.h @@ -78,7 +78,7 @@ using SerializedReturnType = std::unique_ptr; * \return a \c std::unique_ptr to a \c SerializedInfo containing the buffer * with serialized data and the size of the buffer */ -template +template SerializedReturnType serialize(T& target, BufferCallbackType fn = nullptr); /** @@ -101,7 +101,7 @@ SerializedReturnType serialize(T& target, BufferCallbackType fn = nullptr); * * \return a pointer to the newly reified \c T based on bytes in \c buf */ -template +template T* deserialize(char* buf, char* object_buf); /** @@ -118,7 +118,7 @@ T* deserialize(char* buf, char* object_buf); * * \return a unique pointer to the newly reified \c T based on bytes in \c buf */ -template +template std::unique_ptr deserialize(char* buf); /** @@ -132,7 +132,7 @@ std::unique_ptr deserialize(char* buf); * \param[in] t a valid pointer to a \c T that has been user-allocated and * constructed */ -template +template void deserializeInPlace(char* buf, T* t); /** @@ -143,7 +143,7 @@ void deserializeInPlace(char* buf, T* t); * * \return a unique pointer to \c T that must be deallocated */ -template +template std::unique_ptr deserialize(SerializedReturnType&& in); /** @@ -153,7 +153,7 @@ std::unique_ptr deserialize(SerializedReturnType&& in); * * \return number of bytes for the \c target */ -template +template std::size_t getSize(T& target); /** @@ -170,7 +170,7 @@ std::size_t getSize(T& target); * * \return memory footprint of the \c target */ -template +template std::size_t getMemoryFootprint(T& target, std::size_t size_offset = 0); /** @@ -184,7 +184,7 @@ std::size_t getMemoryFootprint(T& target, std::size_t size_offset = 0); * \param[in] target the \c T to serialize * \param[in] file name of the file to create */ -template +template void serializeToFile(T& target, std::string const& file); /** @@ -200,7 +200,7 @@ void serializeToFile(T& target, std::string const& file); * * \return unique pointer to the new object \c T */ -template +template std::unique_ptr deserializeFromFile(std::string const& file); /** @@ -214,7 +214,7 @@ std::unique_ptr deserializeFromFile(std::string const& file); * \param[in] file the filename to read with bytes for \c T * \param[in] t a valid, constructed \c T to deserialize into */ -template +template void deserializeInPlaceFromFile(std::string const& file, T* buf); /** @@ -227,7 +227,7 @@ void deserializeInPlaceFromFile(std::string const& file, T* buf); * \param[in] target the \c T to serialize * \param[in] stream to serialize into, with tellp and write functions. */ -template +template void serializeToStream(T& target, StreamT& stream); /** @@ -243,7 +243,7 @@ void serializeToStream(T& target, StreamT& stream); * * \return unique pointer to the new object \c T */ -template +template std::unique_ptr deserializeFromStream(StreamT& stream); /** @@ -257,7 +257,7 @@ std::unique_ptr deserializeFromStream(StreamT& stream); * \param[in] stream the stream to read with bytes for \c T, with tellg and read functions * \param[in] t a valid, constructed \c T to deserialize into */ -template +template void deserializeInPlaceFromStream(StreamT& stream, T* buf); diff --git a/src/checkpoint/checkpoint_api.impl.h b/src/checkpoint/checkpoint_api.impl.h index 863472e9..f04f4bff 100644 --- a/src/checkpoint/checkpoint_api.impl.h +++ b/src/checkpoint/checkpoint_api.impl.h @@ -53,9 +53,9 @@ namespace checkpoint { -template +template SerializedReturnType serialize(T& target, BufferCallbackType fn) { - auto ret = dispatch::serializeType(target, fn); + auto ret = dispatch::serializeType>(target, fn); auto& buf = std::get<0>(ret); std::unique_ptr base_ptr( static_cast(buf.release()) @@ -63,87 +63,87 @@ SerializedReturnType serialize(T& target, BufferCallbackType fn) { return base_ptr; } -template +template T* deserialize(char* buf, char* object_buf) { - return dispatch::deserializeType(buf, object_buf); + return dispatch::deserializeType>(buf, object_buf); } -template +template std::unique_ptr deserialize(char* buf) { - auto t = dispatch::deserializeType(buf); + auto t = dispatch::deserializeType>(buf); return std::unique_ptr(t); } -template +template std::unique_ptr deserialize(SerializedReturnType&& in) { - auto t = dispatch::deserializeType(in->getBuffer()); + auto t = dispatch::deserializeType>(in->getBuffer()); return std::unique_ptr(t); } -template +template void deserializeInPlace(char* buf, T* t) { - return dispatch::deserializeType(dispatch::InPlaceTag{}, buf, t); + return dispatch::deserializeType>(dispatch::InPlaceTag{}, buf, t); } -template +template std::size_t getSize(T& target) { - return dispatch::Standard::size(target); + return dispatch::Standard::size>(target); } -template +template std::size_t getMemoryFootprint(T& target, std::size_t size_offset) { return size_offset + std::max( - dispatch::Standard::footprint(target), + dispatch::Standard::footprint>(target), sizeof(target) ); } -template +template void serializeToFile(T& target, std::string const& file) { - auto len = getSize(target); - dispatch::Standard::pack>( + auto len = getSize(target); + dispatch::Standard::pack, UserTraitHolder>( target, len, buffer::IOBuffer::WriteToFileTag{}, len, file ); } -template +template std::unique_ptr deserializeFromFile(std::string const& file) { auto mem = dispatch::Standard::allocate(); T* t_buf = dispatch::Standard::construct(mem); - auto t = dispatch::Standard::unpack>( + auto t = dispatch::Standard::unpack, UserTraitHolder>( t_buf, buffer::IOBuffer::ReadFromFileTag{}, file ); return std::unique_ptr(t); } -template +template void deserializeInPlaceFromFile(std::string const& file, T* t) { - dispatch::Standard::unpack>( + dispatch::Standard::unpack, UserTraitHolder>( t, buffer::IOBuffer::ReadFromFileTag{}, file ); } -template +template void serializeToStream(T& target, StreamT& stream) { auto len = getSize(target); - dispatch::Standard::pack>( + dispatch::Standard::pack, UserTraitHolder>( target, len, stream ); } -template +template std::unique_ptr deserializeFromStream(StreamT& stream) { auto mem = dispatch::Standard::allocate(); T* t_buf = dispatch::Standard::construct(mem); - auto t = dispatch::Standard::unpack>( + auto t = dispatch::Standard::unpack, UserTraitHolder>( t_buf, stream ); return std::unique_ptr(t); } -template +template void deserializeInPlaceFromStream(StreamT& stream, T* t) { - dispatch::Standard::unpack>( + dispatch::Standard::unpack, UserTraitHolder>( t, stream ); } diff --git a/src/checkpoint/container/atomic_serialize.h b/src/checkpoint/container/atomic_serialize.h index f0783453..e1bc80e3 100644 --- a/src/checkpoint/container/atomic_serialize.h +++ b/src/checkpoint/container/atomic_serialize.h @@ -54,7 +54,7 @@ template < typename SerializerT, typename T, typename = std::enable_if_t< - checkpoint::is_footprinter::value + checkpoint::is_footprinter_v > > void serialize(SerializerT& s, const std::atomic& atomic) { diff --git a/src/checkpoint/container/function_serialize.h b/src/checkpoint/container/function_serialize.h index 4ab98f3e..6a192fee 100644 --- a/src/checkpoint/container/function_serialize.h +++ b/src/checkpoint/container/function_serialize.h @@ -68,9 +68,7 @@ template < typename Res, typename... ArgTypes, typename = std::enable_if_t< - checkpoint::is_footprinter< - SerializerT - >::value + checkpoint::is_footprinter_v > > void serializeFunction(SerializerT& s, std::function& fn) { diff --git a/src/checkpoint/container/kokkos_unordered_map_serialize.h b/src/checkpoint/container/kokkos_unordered_map_serialize.h index d07b0e6f..81183452 100644 --- a/src/checkpoint/container/kokkos_unordered_map_serialize.h +++ b/src/checkpoint/container/kokkos_unordered_map_serialize.h @@ -117,7 +117,7 @@ template < typename Hasher, typename EqualTo > typename std::enable_if_t< - not checkpoint::is_footprinter::value, void + not checkpoint::is_footprinter_v, void > serialize( SerializerT& s, Kokkos::UnorderedMap& map @@ -140,7 +140,7 @@ template < typename Hasher, typename EqualTo > typename std::enable_if_t< - checkpoint::is_footprinter::value, void + checkpoint::is_footprinter_v, void > serialize( SerializerT& s, Kokkos::UnorderedMap& map diff --git a/src/checkpoint/container/list_serialize.h b/src/checkpoint/container/list_serialize.h index 72f6d1b8..262a08d5 100644 --- a/src/checkpoint/container/list_serialize.h +++ b/src/checkpoint/container/list_serialize.h @@ -56,7 +56,7 @@ namespace checkpoint { template inline typename std::enable_if_t< - not checkpoint::is_footprinter::value, void + not checkpoint::is_footprinter_v, void > deserializeOrderedElems( Serializer& s, ContainerT& cont, typename ContainerT::size_type size, @@ -76,7 +76,7 @@ deserializeOrderedElems( template inline typename std::enable_if_t< - not checkpoint::is_footprinter::value, void + not checkpoint::is_footprinter_v, void > deserializeOrderedElems( Serializer& s, ContainerT& cont, typename ContainerT::size_type size, @@ -96,7 +96,7 @@ deserializeOrderedElems( template inline typename std::enable_if_t< - checkpoint::is_footprinter::value, void + checkpoint::is_footprinter_v, void > deserializeOrderedElems( Serializer&, ContainerT&, typename ContainerT::size_type diff --git a/src/checkpoint/container/map_serialize.h b/src/checkpoint/container/map_serialize.h index 39e1b6b8..a1a2a880 100644 --- a/src/checkpoint/container/map_serialize.h +++ b/src/checkpoint/container/map_serialize.h @@ -58,7 +58,7 @@ namespace checkpoint { template inline typename std::enable_if_t< - not checkpoint::is_footprinter::value, + not checkpoint::is_footprinter_v, void > deserializeEmplaceElems( Serializer& s, ContainerT& cont, typename ContainerT::size_type size @@ -77,7 +77,7 @@ inline typename std::enable_if_t< template inline typename std::enable_if_t< - checkpoint::is_footprinter::value, + checkpoint::is_footprinter_v, void > deserializeEmplaceElems( Serializer&, ContainerT&, typename ContainerT::size_type diff --git a/src/checkpoint/container/queue_serialize.h b/src/checkpoint/container/queue_serialize.h index 8da15f1c..485fbccc 100644 --- a/src/checkpoint/container/queue_serialize.h +++ b/src/checkpoint/container/queue_serialize.h @@ -108,7 +108,7 @@ template < typename SerializerT, typename Q, typename = std::enable_if_t< - checkpoint::is_footprinter::value + checkpoint::is_footprinter_v > > void serializeQueueLikeContainer(SerializerT& s, const Q& q) { diff --git a/src/checkpoint/container/raw_ptr_serialize.h b/src/checkpoint/container/raw_ptr_serialize.h index cb1df132..30814ad9 100644 --- a/src/checkpoint/container/raw_ptr_serialize.h +++ b/src/checkpoint/container/raw_ptr_serialize.h @@ -61,7 +61,7 @@ template < typename SerializerT, typename T, typename = std::enable_if_t< - checkpoint::is_footprinter::value + checkpoint::is_footprinter_v > > void serialize(SerializerT& s, T* ptr) { @@ -92,7 +92,7 @@ void serializeRawPtr(SerializerT& s, void* ptr) { #define CHECKPOINT_FOOTPRINT_PIMPL_WITH_SIZEOF_PTR(PIMPL_TYPE) \ template < \ typename SerializerT, \ - typename = std::enable_if_t< checkpoint::is_footprinter::value > \ + typename = std::enable_if_t< checkpoint::is_footprinter_v > \ > \ void serialize(SerializerT &s, PIMPL_TYPE *t) { \ s.countBytes(t); \ diff --git a/src/checkpoint/container/shared_ptr_serialize.h b/src/checkpoint/container/shared_ptr_serialize.h index f880aef0..db854b52 100644 --- a/src/checkpoint/container/shared_ptr_serialize.h +++ b/src/checkpoint/container/shared_ptr_serialize.h @@ -52,7 +52,7 @@ template < typename SerializerT, typename T, typename = std::enable_if_t< - checkpoint::is_footprinter::value + checkpoint::is_footprinter_v > > void serialize(SerializerT& s, std::shared_ptr& ptr) { diff --git a/src/checkpoint/container/thread_serialize.h b/src/checkpoint/container/thread_serialize.h index d755ce39..d2596a18 100644 --- a/src/checkpoint/container/thread_serialize.h +++ b/src/checkpoint/container/thread_serialize.h @@ -53,7 +53,7 @@ namespace checkpoint { template < typename SerializerT, typename = std::enable_if_t< - checkpoint::is_footprinter::value + checkpoint::is_footprinter_v > > void serialize(SerializerT& s, const std::thread& t) { diff --git a/src/checkpoint/container/vector_serialize.h b/src/checkpoint/container/vector_serialize.h index ede47ad0..ed4478c6 100644 --- a/src/checkpoint/container/vector_serialize.h +++ b/src/checkpoint/container/vector_serialize.h @@ -56,7 +56,7 @@ namespace checkpoint { template typename std::enable_if_t< - not checkpoint::is_footprinter::value, SerialSizeType + not checkpoint::is_footprinter_v, SerialSizeType > serializeVectorMeta(SerializerT& s, std::vector& vec) { SerialSizeType vec_capacity = vec.capacity(); @@ -108,7 +108,7 @@ void constructVectorData( template typename std::enable_if_t< - not checkpoint::is_footprinter::value, void + not checkpoint::is_footprinter_v, void > serialize(SerializerT& s, std::vector& vec) { auto const vec_size = serializeVectorMeta(s, vec); @@ -122,7 +122,7 @@ serialize(SerializerT& s, std::vector& vec) { template typename std::enable_if_t< - not checkpoint::is_footprinter::value, void + not checkpoint::is_footprinter_v, void > serialize(SerializerT& s, std::vector& vec) { auto const vec_size = serializeVectorMeta(s, vec); @@ -146,7 +146,7 @@ serialize(SerializerT& s, std::vector& vec) { template typename std::enable_if_t< - checkpoint::is_footprinter::value, void + checkpoint::is_footprinter_v, void > serialize(SerializerT& s, std::vector& vec) { s.countBytes(vec); @@ -156,7 +156,7 @@ serialize(SerializerT& s, std::vector& vec) { template typename std::enable_if_t< - checkpoint::is_footprinter::value, void + checkpoint::is_footprinter_v, void > serialize(SerializerT& s, std::vector& vec) { s.countBytes(vec); diff --git a/src/checkpoint/dispatch/dispatch.h b/src/checkpoint/dispatch/dispatch.h index e5c6da1b..b5cefcaf 100644 --- a/src/checkpoint/dispatch/dispatch.h +++ b/src/checkpoint/dispatch/dispatch.h @@ -98,7 +98,7 @@ struct Traverse { * * \return the constructed traverser after traversal is complete */ - template + template , typename... Args> static TraverserT with(T& target, Args&&... args); /** @@ -131,7 +131,7 @@ struct Standard { * * \return the number of bytes */ - template + template static SerialSizeType size(T& target, Args&&... args); /** @@ -142,20 +142,19 @@ struct Standard { * * \return memory footprint of \c T */ - template + template static SerialSizeType footprint(T& target, Args&&... args); /** * \brief Pack \c target that requires \c size number of bytes. * * \param[in] target the target to pack - * \param[in] size the number of bytes for \c target * \param[in] args arguments to the packer's constructor * * \return the packer after packing */ - template - static PackerT pack(T& target, SerialSizeType const& size, Args&&... args); + template + static PackerT pack(T& target, Args&&... args); /** * \brief Unpack \c T from packed byte-buffer \c mem @@ -166,7 +165,7 @@ struct Standard { * * \return a pointer to an unpacked \c T */ - template + template static T* unpack(T* mem, Args&&... args); /** @@ -188,7 +187,7 @@ struct Standard { static SerialByteType* allocate(); }; -template +template buffer::ImplReturnType packBuffer( T& target, SerialSizeType size, BufferObtainFnType fn ); @@ -196,13 +195,13 @@ buffer::ImplReturnType packBuffer( template inline void serializeArray(Serializer& s, T* array, SerialSizeType const len); -template +template buffer::ImplReturnType serializeType(T& target, BufferObtainFnType fn = nullptr); -template +template T* deserializeType(SerialByteType* data, SerialByteType* allocBuf = nullptr); -template +template void deserializeType(InPlaceTag, SerialByteType* data, T* t); template diff --git a/src/checkpoint/dispatch/dispatch.impl.h b/src/checkpoint/dispatch/dispatch.impl.h index 44aac6a2..20eb65c2 100644 --- a/src/checkpoint/dispatch/dispatch.impl.h +++ b/src/checkpoint/dispatch/dispatch.impl.h @@ -50,6 +50,7 @@ #include #include +#include namespace checkpoint { @@ -166,13 +167,14 @@ TraverserT& Traverse::with(T& target, TraverserT& t, SerialSizeType len) { return t; } -template +template TraverserT Traverse::with(T& target, Args&&... args) { #if !defined(SERIALIZATION_ERROR_CHECKING) using CleanT = typename CleanType::CleanT; #endif - TraverserT t(std::forward(args)...); + TraverserT t_base(std::forward(args)...); + auto t = SerializerRef(&t_base, Traits{}); #if !defined(SERIALIZATION_ERROR_CHECKING) withTypeIdx(t); @@ -184,7 +186,7 @@ TraverserT Traverse::with(T& target, Args&&... args) { withMemUsed(t, 1); #endif - return t; + return t_base; } template @@ -192,22 +194,22 @@ T* Traverse::reconstruct(SerialByteType* mem) { return Reconstructor::CleanT>::construct(mem); } -template +template SerialSizeType Standard::size(T& target, Args&&... args) { - auto sizer = Traverse::with(target, std::forward(args)...); + auto sizer = Traverse::with(target, std::forward(args)...); return sizer.getSize(); } -template +template SerialSizeType Standard::footprint(T& target, Args&&... args) { auto footprinter = - Traverse::with(target, std::forward(args)...); + Traverse::with(target, std::forward(args)...); return footprinter.getMemoryFootprint(); } -template -PackerT Standard::pack(T& target, SerialSizeType const& size, Args&&... args) { - return Traverse::with(target, size, std::forward(args)...); +template +PackerT Standard::pack(T& target, Args&&... args) { + return Traverse::with(target, std::forward(args)...); } template @@ -215,9 +217,9 @@ SerialByteType* Standard::allocate() { return reinterpret_cast(std::allocator{}.allocate(1)); } -template +template T* Standard::unpack(T* t_buf, Args&&... args) { - Traverse::with(*t_buf, std::forward(args)...); + Traverse::with(*t_buf, std::forward(args)...); return t_buf; } @@ -247,17 +249,17 @@ validatePackerBufferSize(PackerT const& p, SerialSizeType bufferSize) { } } -template +template buffer::ImplReturnType packBuffer(T& target, SerialSizeType size, BufferObtainFnType fn) { SerialByteType* user_buf = fn ? fn(size) : nullptr; if (user_buf == nullptr) { auto p = - Standard::pack>(target, size); + Standard::pack, UserTraits>(target, size); validatePackerBufferSize(p, size); return std::make_tuple(std::move(p.extractPackedBuffer()), size); } else { - auto p = Standard::pack>( + auto p = Standard::pack, UserTraits>( target, size, std::make_unique(user_buf, size) ); validatePackerBufferSize(p, size); @@ -265,26 +267,26 @@ packBuffer(T& target, SerialSizeType size, BufferObtainFnType fn) { } } -template +template buffer::ImplReturnType serializeType(T& target, BufferObtainFnType fn) { - auto len = Standard::size(target); + auto len = Standard::size(target); debug_checkpoint("serializeType: len=%ld\n", len); - return packBuffer(target, len, fn); + return packBuffer(target, len, fn); } -template +template T* deserializeType(SerialByteType* data, SerialByteType* allocBuf) { auto mem = allocBuf ? allocBuf : Standard::allocate(); auto t_buf = std::unique_ptr(Standard::construct(mem)); T* traverser = - Standard::unpack>(t_buf.get(), data); + Standard::unpack, UserTraits>(t_buf.get(), data); t_buf.release(); return traverser; } -template +template void deserializeType(InPlaceTag, SerialByteType* data, T* t) { - Standard::unpack>(t, data); + Standard::unpack, UserTraits>(t, data); } }} /* end namespace checkpoint::dispatch */ diff --git a/src/checkpoint/dispatch/dispatch_serializer_nonbyte.h b/src/checkpoint/dispatch/dispatch_serializer_nonbyte.h index 9bc2fd9a..cae6b4f1 100644 --- a/src/checkpoint/dispatch/dispatch_serializer_nonbyte.h +++ b/src/checkpoint/dispatch/dispatch_serializer_nonbyte.h @@ -112,7 +112,7 @@ struct SerializerDispatchNonByte { template using justFootprint = typename std::enable_if< - checkpoint::is_footprinter::value and + checkpoint::is_footprinter_v and not SerializableTraits::is_traversable and not std::is_enum::value, T diff --git a/src/checkpoint/dispatch/vrt/serializer_registry.h b/src/checkpoint/dispatch/vrt/serializer_registry.h index 9ec3860c..81e70669 100644 --- a/src/checkpoint/dispatch/vrt/serializer_registry.h +++ b/src/checkpoint/dispatch/vrt/serializer_registry.h @@ -94,7 +94,7 @@ inline RegistryType& getRegistry() { template inline TypeIdx makeObjIdx() { - return Type::idx; + return Type::idx; } template diff --git a/src/checkpoint/dispatch/vrt/virtual_serialize.h b/src/checkpoint/dispatch/vrt/virtual_serialize.h index 52f8ef03..a2e66b4c 100644 --- a/src/checkpoint/dispatch/vrt/virtual_serialize.h +++ b/src/checkpoint/dispatch/vrt/virtual_serialize.h @@ -59,9 +59,13 @@ namespace checkpoint { namespace dispatch { namespace vrt { */ template void virtualSerialize(T*& base, SerializerT& s) { + //We can't support traited serializing with virtual types. + static_assert(std::is_same_v, "User Traits are incompatible with virtual serialization"); + // Get the real base in case this is called on a derived type using BaseT = ::checkpoint::dispatch::vrt::checkpoint_base_type_t; auto serializer_idx = serializer_registry::makeObjIdx(); + base->_checkpointDynamicSerialize(&s, serializer_idx, no_type_idx); } @@ -146,7 +150,7 @@ struct ReconstructAsVirtualIfNeeded< SerializerT, typename std::enable_if_t< dispatch::vrt::VirtualSerializeTraits::has_not_virtual_serialize and - not checkpoint::is_footprinter::value + not checkpoint::is_footprinter_v > > { static T* apply(SerializerT&, dispatch::vrt::TypeIdx) { @@ -162,7 +166,7 @@ struct ReconstructAsVirtualIfNeeded< SerializerT, typename std::enable_if_t< dispatch::vrt::VirtualSerializeTraits::has_not_virtual_serialize and - checkpoint::is_footprinter::value + checkpoint::is_footprinter_v > > { static T* apply(SerializerT&, dispatch::vrt::TypeIdx) { return nullptr; } diff --git a/src/checkpoint/serializers/footprinter.h b/src/checkpoint/serializers/footprinter.h index 1818410b..7d38d44b 100644 --- a/src/checkpoint/serializers/footprinter.h +++ b/src/checkpoint/serializers/footprinter.h @@ -76,6 +76,9 @@ namespace { template struct is_footprinter_impl : public std::false_type {}; + template + struct is_footprinter_impl> : std::true_type {}; + template <> struct is_footprinter_impl : std::true_type {}; } @@ -83,6 +86,9 @@ namespace { template using is_footprinter = is_footprinter_impl>; +template +static constexpr const bool is_footprinter_v = is_footprinter::value; + } /* end namespace checkpoint */ #endif /*INCLUDED_SRC_CHECKPOINT_SERIALIZERS_FOOTPRINTER_H*/ diff --git a/src/checkpoint/serializers/serializer_ref.h b/src/checkpoint/serializers/serializer_ref.h new file mode 100644 index 00000000..eb973b9b --- /dev/null +++ b/src/checkpoint/serializers/serializer_ref.h @@ -0,0 +1,173 @@ +/* +//@HEADER +// ***************************************************************************** +// +// serializer_ref.h +// DARMA/magistrate => Serialization Library +// +// Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. +// +// 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 the copyright holder 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. +// +// Questions? Contact darma@sandia.gov +// +// ***************************************************************************** +//@HEADER +*/ + +#if !defined INCLUDED_SRC_CHECKPOINT_SERIALIZERS_SERIALIZER_REF_H +#define INCLUDED_SRC_CHECKPOINT_SERIALIZERS_SERIALIZER_REF_H + +#include "checkpoint/common.h" +#include "checkpoint/serializers/base_serializer.h" +#include "checkpoint/traits/user_traits.h" + +#include +#include +#include + +namespace checkpoint { + +namespace { + // Cuda does not play nicely with templated static constexpr + // member variables in the SerializerRef class, so we make a + // helper struct to hold non-templated static constexpr members. + template + struct bool_enable_if { + static constexpr bool value = false; + }; + + template<> + struct bool_enable_if { + static constexpr bool value = true; + using type = void*; + }; +} + +template> +struct SerializerRef +{ + using TraitHolder = typename UserTraits::BaseTraits; + + //Passing initialization to the serializer implementation + explicit SerializerRef(SerT* m_impl, const UserTraits& = {}) : + impl(m_impl) + { } + + //Copy constructor, possibly changing the traits + template + explicit SerializerRef( + const SerializerRef& other + ) : impl(other.impl) + { } + + //Now forward along Serializer function calls and types + using ModeType = eSerializationMode; + template + using DispatcherType = typename SerT::template DispatcherType; + ModeType getMode() const { return impl->getMode(); } + bool isSizing() const { return impl->isSizing(); } + bool isPacking() const { return impl->isPacking(); } + bool isUnpacking() const { return impl->isUnpacking(); } + bool isFootprinting() const { return impl->isFootprinting(); } + template + void countBytes(const T& t) { impl->countBytes(t); } + void addBytes(std::size_t s) { impl->addBytes(s); } + void contiguousBytes(void* ptr, SerialSizeType size, SerialSizeType num_elms) { + impl->contiguousBytes(ptr, size, num_elms); + } + SerialSizeType usedBufferSize() const { return impl->usedBufferSize(); } + template + void contiguousTyped(SerializerT& serdes, T* ptr, SerialSizeType num_elms) { + serdes.contiguousBytes(static_cast(ptr), sizeof(T), num_elms); + } + template + void skip(Args&&... args){ + impl->skip( std::forward(args)... ); + } + SerialByteType* getBuffer() const { return impl->getBuffer(); } + SerialByteType* getSpotIncrement(const SerialSizeType inc) { return impl->getSpotIncrement(inc); } + bool isVirtualDisabled() const { return impl->isVirtualDisabled(); } + void setVirtualDisabled(bool val) { impl->setVirtualDisabled(val); } + + + + SerT* operator->(){ return impl; } + + template + operator SerializerRef() const { + return SerializerRef(*this); + } + + + //Big block of helpers for conveniently checking traits in different contexts. + template + using has_traits = bool_enable_if::value>; + template + using has_any_traits = bool_enable_if::value>; + + template + using has_not_traits = bool_enable_if::value)>; + template + using has_not_any_traits = bool_enable_if::value)>; + + //Helpers for converting between traits + using TraitlessT = SerializerRef; + + //Returns a new reference with traits in addition to this reference's traits. + template + auto withTraits(UserTraitHolder = {}){ + using NewTraitHolder = typename TraitHolder::template with; + return SerializerRef(*this); + } + + //Returns a new reference with traits removed (if present) from this reference's traits. + template + auto withoutTraits(UserTraitHolder = {}){ + using NewTraitHolder = typename TraitHolder::template without; + return SerializerRef(*this); + } + + //Returns a new reference with traits set to the inputs, regardless of this reference's traits. + template + SerializerRef> setTraits( + const UserTraitHolder& = {} + ){ + return SerializerRef>(*this); + } + +protected: + SerT *const impl; + + template + friend struct SerializerRef; +}; + +} +#endif /*INCLUDED_SRC_CHECKPOINT_SERIALIZERS_SERIALIZER_REF_H*/ diff --git a/src/checkpoint/serializers/serializers_headers.h b/src/checkpoint/serializers/serializers_headers.h index 395a063d..e9b4c674 100644 --- a/src/checkpoint/serializers/serializers_headers.h +++ b/src/checkpoint/serializers/serializers_headers.h @@ -46,21 +46,22 @@ #include "checkpoint/common.h" #include "checkpoint/serializers/base_serializer.h" +#include "checkpoint/serializers/serializer_ref.h" #include "checkpoint/serializers/footprinter.h" #include "checkpoint/serializers/sizer.h" #include "checkpoint/serializers/packer.h" #include "checkpoint/serializers/unpacker.h" #include "checkpoint/serializers/stream_serializer.h" -#define checkpoint_serializer_variadic_args() \ - checkpoint::Footprinter, \ - checkpoint::Packer, \ - checkpoint::PackerUserBuf, \ - checkpoint::PackerIO, \ - checkpoint::Unpacker, \ - checkpoint::UnpackerIO, \ - checkpoint::Sizer, \ - checkpoint::StreamPacker<>, \ - checkpoint::StreamUnpacker<> \ +#define checkpoint_serializer_variadic_args() \ + checkpoint::SerializerRef, \ + checkpoint::SerializerRef, \ + checkpoint::SerializerRef, \ + checkpoint::SerializerRef, \ + checkpoint::SerializerRef, \ + checkpoint::SerializerRef, \ + checkpoint::SerializerRef, \ + checkpoint::SerializerRef>, \ + checkpoint::SerializerRef> \ #endif /*INCLUDED_SRC_CHECKPOINT_SERIALIZERS_SERIALIZERS_HEADERS_H*/ diff --git a/src/checkpoint/traits/serializable_traits.h b/src/checkpoint/traits/serializable_traits.h index 3dc8b0bb..fef2e7cb 100644 --- a/src/checkpoint/traits/serializable_traits.h +++ b/src/checkpoint/traits/serializable_traits.h @@ -86,7 +86,7 @@ struct isByteCopyableImpl { template struct isByteCopyable : detail::isByteCopyableImpl::has_byteCopyTraitTrue {}; -template +template > struct SerializableTraits { /** * Start with detection of "serialize" overloads, intrusive and non-intrusive. diff --git a/src/checkpoint/traits/user_traits.h b/src/checkpoint/traits/user_traits.h new file mode 100644 index 00000000..26f6704f --- /dev/null +++ b/src/checkpoint/traits/user_traits.h @@ -0,0 +1,196 @@ +/* +//@HEADER +// ***************************************************************************** +// +// user_traits.h +// DARMA/magistrate => Serialization Library +// +// Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. +// +// 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 the copyright holder 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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. +// +// Questions? Contact darma@sandia.gov +// +// ***************************************************************************** +//@HEADER +*/ + +#if !defined INCLUDED_SRC_CHECKPOINT_TRAITS_USER_TRAITS_H +#define INCLUDED_SRC_CHECKPOINT_TRAITS_USER_TRAITS_H + +namespace { + struct NoTrait; + + template + struct without_helper { + using type = typename without_helper, U...>::type; + }; + template + struct without_helper { + using type = typename Traits::template _without_trait; + }; +} + +namespace checkpoint { +namespace SerializerUserTraits { + + template + struct is_base_or_derived + : std::disjunction, std::is_base_of> {}; + + template + struct is_same_template + : std::false_type {}; + template