Repository: boostorg/serialization
Branch: develop
Commit: 097a6c63a137
Files: 540
Total size: 2.1 MB
Directory structure:
gitextract_ba__d2tt/
├── .drone/
│ └── drone.sh
├── .drone.star
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── posix.yml
│ └── windows.yml
├── .gitignore
├── .travis.yml
├── CMake/
│ └── CMakeLists.txt
├── CMakeLists.txt
├── appveyor.yml
├── build/
│ └── Jamfile.v2
├── build.jam
├── doc/
│ ├── acknowledgments.html
│ ├── archive_reference.html
│ ├── archives.html
│ ├── bibliography.html
│ ├── class_diagram.html
│ ├── codecvt.html
│ ├── configuration.html
│ ├── contents.html
│ ├── dataflow.html
│ ├── definitions.html
│ ├── derivation.html
│ ├── exception_safety.html
│ ├── exceptions.html
│ ├── extended_type_info.html
│ ├── faq.html
│ ├── headers.html
│ ├── history.html
│ ├── implementation.html
│ ├── index.html
│ ├── new_case_studies.html
│ ├── overview.html
│ ├── performance_status.html
│ ├── pimpl.html
│ ├── private_base.html
│ ├── profile1.txt
│ ├── profile2.txt
│ ├── profile3.txt
│ ├── rationale.html
│ ├── reference.html
│ ├── release.html
│ ├── serialization.html
│ ├── shared_ptr.html
│ ├── shared_ptr2.html
│ ├── simple_log.html
│ ├── singleton.html
│ ├── smart_cast.html
│ ├── special.html
│ ├── state_saver.html
│ ├── static_warning.html
│ ├── strong_typedef.html
│ ├── style.css
│ ├── todo.html
│ ├── traits.html
│ ├── tutorial.html
│ ├── void_cast.html
│ └── wrappers.html
├── example/
│ ├── Jamfile.v2
│ ├── demo.cpp
│ ├── demo_auto_ptr.cpp
│ ├── demo_dll_a.hpp
│ ├── demo_dll_a.ipp
│ ├── demo_dll_b.hpp
│ ├── demo_dll_b.ipp
│ ├── demo_exception.cpp
│ ├── demo_fast_archive.cpp
│ ├── demo_gps.hpp
│ ├── demo_log.cpp
│ ├── demo_output.txt
│ ├── demo_pimpl.cpp
│ ├── demo_pimpl_A.cpp
│ ├── demo_pimpl_A.hpp
│ ├── demo_polymorphic.cpp
│ ├── demo_polymorphic_A.cpp
│ ├── demo_polymorphic_A.hpp
│ ├── demo_portable_archive.cpp
│ ├── demo_save.xml
│ ├── demo_shared_ptr.cpp
│ ├── demo_simple_log.cpp
│ ├── demo_trivial_archive.cpp
│ ├── demo_xml.cpp
│ ├── demo_xml.hpp
│ ├── demo_xml_load.cpp
│ ├── demo_xml_save.cpp
│ ├── demofile.txt
│ ├── fix_six.cpp
│ ├── log_archive.cpp
│ ├── log_archive.hpp
│ ├── polymorphic_portable_binary_iarchive.cpp
│ ├── polymorphic_portable_binary_iarchive.hpp
│ ├── polymorphic_portable_binary_oarchive.cpp
│ ├── polymorphic_portable_binary_oarchive.hpp
│ ├── portable_binary_archive.hpp
│ ├── portable_binary_iarchive.cpp
│ ├── portable_binary_iarchive.hpp
│ ├── portable_binary_oarchive.cpp
│ ├── portable_binary_oarchive.hpp
│ └── simple_log_archive.hpp
├── include/
│ └── boost/
│ ├── archive/
│ │ ├── archive_exception.hpp
│ │ ├── basic_archive.hpp
│ │ ├── basic_binary_iarchive.hpp
│ │ ├── basic_binary_iprimitive.hpp
│ │ ├── basic_binary_oarchive.hpp
│ │ ├── basic_binary_oprimitive.hpp
│ │ ├── basic_streambuf_locale_saver.hpp
│ │ ├── basic_text_iarchive.hpp
│ │ ├── basic_text_iprimitive.hpp
│ │ ├── basic_text_oarchive.hpp
│ │ ├── basic_text_oprimitive.hpp
│ │ ├── basic_xml_archive.hpp
│ │ ├── basic_xml_iarchive.hpp
│ │ ├── basic_xml_oarchive.hpp
│ │ ├── binary_iarchive.hpp
│ │ ├── binary_iarchive_impl.hpp
│ │ ├── binary_oarchive.hpp
│ │ ├── binary_oarchive_impl.hpp
│ │ ├── binary_wiarchive.hpp
│ │ ├── binary_woarchive.hpp
│ │ ├── codecvt_null.hpp
│ │ ├── detail/
│ │ │ ├── abi_prefix.hpp
│ │ │ ├── abi_suffix.hpp
│ │ │ ├── archive_serializer_map.hpp
│ │ │ ├── auto_link_archive.hpp
│ │ │ ├── auto_link_warchive.hpp
│ │ │ ├── basic_iarchive.hpp
│ │ │ ├── basic_iserializer.hpp
│ │ │ ├── basic_oarchive.hpp
│ │ │ ├── basic_oserializer.hpp
│ │ │ ├── basic_pointer_iserializer.hpp
│ │ │ ├── basic_pointer_oserializer.hpp
│ │ │ ├── basic_serializer.hpp
│ │ │ ├── basic_serializer_map.hpp
│ │ │ ├── check.hpp
│ │ │ ├── common_iarchive.hpp
│ │ │ ├── common_oarchive.hpp
│ │ │ ├── decl.hpp
│ │ │ ├── helper_collection.hpp
│ │ │ ├── interface_iarchive.hpp
│ │ │ ├── interface_oarchive.hpp
│ │ │ ├── iserializer.hpp
│ │ │ ├── oserializer.hpp
│ │ │ ├── polymorphic_iarchive_route.hpp
│ │ │ ├── polymorphic_oarchive_route.hpp
│ │ │ ├── register_archive.hpp
│ │ │ └── utf8_codecvt_facet.hpp
│ │ ├── dinkumware.hpp
│ │ ├── impl/
│ │ │ ├── archive_serializer_map.ipp
│ │ │ ├── basic_binary_iarchive.ipp
│ │ │ ├── basic_binary_iprimitive.ipp
│ │ │ ├── basic_binary_oarchive.ipp
│ │ │ ├── basic_binary_oprimitive.ipp
│ │ │ ├── basic_text_iarchive.ipp
│ │ │ ├── basic_text_iprimitive.ipp
│ │ │ ├── basic_text_oarchive.ipp
│ │ │ ├── basic_text_oprimitive.ipp
│ │ │ ├── basic_xml_grammar.hpp
│ │ │ ├── basic_xml_iarchive.ipp
│ │ │ ├── basic_xml_oarchive.ipp
│ │ │ ├── text_iarchive_impl.ipp
│ │ │ ├── text_oarchive_impl.ipp
│ │ │ ├── text_wiarchive_impl.ipp
│ │ │ ├── text_woarchive_impl.ipp
│ │ │ ├── xml_iarchive_impl.ipp
│ │ │ ├── xml_oarchive_impl.ipp
│ │ │ ├── xml_wiarchive_impl.ipp
│ │ │ └── xml_woarchive_impl.ipp
│ │ ├── iterators/
│ │ │ ├── base64_exception.hpp
│ │ │ ├── base64_from_binary.hpp
│ │ │ ├── binary_from_base64.hpp
│ │ │ ├── dataflow.hpp
│ │ │ ├── dataflow_exception.hpp
│ │ │ ├── escape.hpp
│ │ │ ├── insert_linebreaks.hpp
│ │ │ ├── istream_iterator.hpp
│ │ │ ├── mb_from_wchar.hpp
│ │ │ ├── ostream_iterator.hpp
│ │ │ ├── remove_whitespace.hpp
│ │ │ ├── transform_width.hpp
│ │ │ ├── unescape.hpp
│ │ │ ├── wchar_from_mb.hpp
│ │ │ ├── xml_escape.hpp
│ │ │ ├── xml_unescape.hpp
│ │ │ └── xml_unescape_exception.hpp
│ │ ├── polymorphic_binary_iarchive.hpp
│ │ ├── polymorphic_binary_oarchive.hpp
│ │ ├── polymorphic_iarchive.hpp
│ │ ├── polymorphic_oarchive.hpp
│ │ ├── polymorphic_text_iarchive.hpp
│ │ ├── polymorphic_text_oarchive.hpp
│ │ ├── polymorphic_text_wiarchive.hpp
│ │ ├── polymorphic_text_woarchive.hpp
│ │ ├── polymorphic_xml_iarchive.hpp
│ │ ├── polymorphic_xml_oarchive.hpp
│ │ ├── polymorphic_xml_wiarchive.hpp
│ │ ├── polymorphic_xml_woarchive.hpp
│ │ ├── text_iarchive.hpp
│ │ ├── text_oarchive.hpp
│ │ ├── text_wiarchive.hpp
│ │ ├── text_woarchive.hpp
│ │ ├── tmpdir.hpp
│ │ ├── wcslen.hpp
│ │ ├── xml_archive_exception.hpp
│ │ ├── xml_iarchive.hpp
│ │ ├── xml_oarchive.hpp
│ │ ├── xml_wiarchive.hpp
│ │ └── xml_woarchive.hpp
│ └── serialization/
│ ├── access.hpp
│ ├── archive_input_unordered_map.hpp
│ ├── archive_input_unordered_set.hpp
│ ├── array.hpp
│ ├── array_optimization.hpp
│ ├── array_wrapper.hpp
│ ├── assume_abstract.hpp
│ ├── base_object.hpp
│ ├── binary_object.hpp
│ ├── bitset.hpp
│ ├── boost_array.hpp
│ ├── boost_unordered_map.hpp
│ ├── boost_unordered_set.hpp
│ ├── collection_size_type.hpp
│ ├── collection_traits.hpp
│ ├── collections_load_imp.hpp
│ ├── collections_save_imp.hpp
│ ├── complex.hpp
│ ├── config.hpp
│ ├── deque.hpp
│ ├── detail/
│ │ ├── is_default_constructible.hpp
│ │ ├── shared_count_132.hpp
│ │ ├── shared_ptr_132.hpp
│ │ ├── shared_ptr_nmt_132.hpp
│ │ └── stack_constructor.hpp
│ ├── export.hpp
│ ├── extended_type_info.hpp
│ ├── extended_type_info_no_rtti.hpp
│ ├── extended_type_info_typeid.hpp
│ ├── factory.hpp
│ ├── force_include.hpp
│ ├── forward_list.hpp
│ ├── hash_collections_load_imp.hpp
│ ├── hash_collections_save_imp.hpp
│ ├── hash_map.hpp
│ ├── hash_set.hpp
│ ├── is_bitwise_serializable.hpp
│ ├── item_version_type.hpp
│ ├── level.hpp
│ ├── level_enum.hpp
│ ├── library_version_type.hpp
│ ├── list.hpp
│ ├── map.hpp
│ ├── nvp.hpp
│ ├── optional.hpp
│ ├── priority_queue.hpp
│ ├── queue.hpp
│ ├── scoped_ptr.hpp
│ ├── serialization.hpp
│ ├── set.hpp
│ ├── shared_ptr.hpp
│ ├── shared_ptr_132.hpp
│ ├── shared_ptr_helper.hpp
│ ├── singleton.hpp
│ ├── slist.hpp
│ ├── smart_cast.hpp
│ ├── split_free.hpp
│ ├── split_member.hpp
│ ├── stack.hpp
│ ├── state_saver.hpp
│ ├── static_warning.hpp
│ ├── std_variant.hpp
│ ├── string.hpp
│ ├── strong_typedef.hpp
│ ├── throw_exception.hpp
│ ├── tracking.hpp
│ ├── tracking_enum.hpp
│ ├── traits.hpp
│ ├── type_info_implementation.hpp
│ ├── unique_ptr.hpp
│ ├── unordered_collections_load_imp.hpp
│ ├── unordered_collections_save_imp.hpp
│ ├── unordered_map.hpp
│ ├── unordered_set.hpp
│ ├── utility.hpp
│ ├── valarray.hpp
│ ├── variant.hpp
│ ├── variant2.hpp
│ ├── vector.hpp
│ ├── vector_135.hpp
│ ├── version.hpp
│ ├── void_cast.hpp
│ ├── void_cast_fwd.hpp
│ ├── weak_ptr.hpp
│ └── wrapper.hpp
├── index.html
├── meta/
│ └── libraries.json
├── performance/
│ ├── Jamfile.v2
│ ├── binary_archive.hpp
│ ├── binary_warchive.hpp
│ ├── peformance_array.cpp
│ ├── performance_binary.cpp
│ ├── performance_codecvt_null.cpp
│ ├── performance_iterators.cpp
│ ├── performance_iterators_base64.cpp
│ ├── performance_no_rtti.cpp
│ ├── performance_polymorphic.cpp
│ ├── performance_simple_class.cpp
│ ├── performance_utf8_codecvt.cpp
│ ├── performance_vector.cpp
│ ├── polymorphic_array_binary_archive.hpp
│ ├── polymorphic_binary_archive.hpp
│ ├── polymorphic_text_archive.hpp
│ ├── polymorphic_text_warchive.hpp
│ ├── polymorphic_xml_archive.hpp
│ ├── polymorphic_xml_warchive.hpp
│ ├── portable_binary_archive.hpp
│ ├── profile.sh
│ ├── text_archive.hpp
│ ├── text_warchive.hpp
│ ├── xml/
│ │ ├── Jamfile.v2
│ │ ├── harness.hpp
│ │ ├── high_resolution_timer.hpp
│ │ ├── int16_results.xml
│ │ ├── int16_test.cpp
│ │ ├── int256_results.xml
│ │ ├── int256_test.cpp
│ │ ├── int4_results.xml
│ │ ├── int4_test.cpp
│ │ ├── int64_results.xml
│ │ ├── int64_test.cpp
│ │ ├── macro.hpp
│ │ ├── node.hpp
│ │ ├── string16_results.xml
│ │ ├── string16_test.cpp
│ │ ├── string256_results.xml
│ │ ├── string256_test.cpp
│ │ ├── string4_results.xml
│ │ ├── string4_test.cpp
│ │ ├── string64_results.xml
│ │ └── string64_test.cpp
│ ├── xml_archive.hpp
│ └── xml_warchive.hpp
├── src/
│ ├── archive_exception.cpp
│ ├── basic_archive.cpp
│ ├── basic_iarchive.cpp
│ ├── basic_iserializer.cpp
│ ├── basic_oarchive.cpp
│ ├── basic_oserializer.cpp
│ ├── basic_pointer_iserializer.cpp
│ ├── basic_pointer_oserializer.cpp
│ ├── basic_serializer_map.cpp
│ ├── basic_text_iprimitive.cpp
│ ├── basic_text_oprimitive.cpp
│ ├── basic_text_wiprimitive.cpp
│ ├── basic_text_woprimitive.cpp
│ ├── basic_xml_archive.cpp
│ ├── basic_xml_grammar.ipp
│ ├── binary_iarchive.cpp
│ ├── binary_oarchive.cpp
│ ├── binary_wiarchive.cpp
│ ├── binary_woarchive.cpp
│ ├── codecvt_null.cpp
│ ├── extended_type_info.cpp
│ ├── extended_type_info_no_rtti.cpp
│ ├── extended_type_info_typeid.cpp
│ ├── polymorphic_binary_iarchive.cpp
│ ├── polymorphic_binary_oarchive.cpp
│ ├── polymorphic_iarchive.cpp
│ ├── polymorphic_oarchive.cpp
│ ├── polymorphic_text_iarchive.cpp
│ ├── polymorphic_text_oarchive.cpp
│ ├── polymorphic_text_wiarchive.cpp
│ ├── polymorphic_text_woarchive.cpp
│ ├── polymorphic_xml_iarchive.cpp
│ ├── polymorphic_xml_oarchive.cpp
│ ├── polymorphic_xml_wiarchive.cpp
│ ├── polymorphic_xml_woarchive.cpp
│ ├── stl_port.cpp
│ ├── text_iarchive.cpp
│ ├── text_oarchive.cpp
│ ├── text_wiarchive.cpp
│ ├── text_woarchive.cpp
│ ├── utf8_codecvt_facet.cpp
│ ├── void_cast.cpp
│ ├── xml_archive_exception.cpp
│ ├── xml_grammar.cpp
│ ├── xml_iarchive.cpp
│ ├── xml_oarchive.cpp
│ ├── xml_wgrammar.cpp
│ ├── xml_wiarchive.cpp
│ └── xml_woarchive.cpp
├── test/
│ ├── A.cpp
│ ├── A.hpp
│ ├── A.ipp
│ ├── B.hpp
│ ├── C.hpp
│ ├── D.hpp
│ ├── J.hpp
│ ├── Jamfile.v2
│ ├── base.hpp
│ ├── binary_archive.hpp
│ ├── binary_warchive.hpp
│ ├── config_test.cpp
│ ├── derived2.hpp
│ ├── dll_a.cpp
│ ├── dll_base.cpp
│ ├── dll_derived2.cpp
│ ├── dll_polymorphic_base.cpp
│ ├── dll_polymorphic_derived2.cpp
│ ├── multi_shared1.cpp
│ ├── multi_shared2.cpp
│ ├── polymorphic_array_binary_archive.hpp
│ ├── polymorphic_base.cpp
│ ├── polymorphic_base.hpp
│ ├── polymorphic_binary_archive.hpp
│ ├── polymorphic_derived1.cpp
│ ├── polymorphic_derived1.hpp
│ ├── polymorphic_derived2.cpp
│ ├── polymorphic_derived2.hpp
│ ├── polymorphic_text_archive.hpp
│ ├── polymorphic_text_warchive.hpp
│ ├── polymorphic_xml_archive.hpp
│ ├── polymorphic_xml_warchive.hpp
│ ├── portable_binary_archive.hpp
│ ├── test__helper.cpp
│ ├── test_array.cpp
│ ├── test_binary.cpp
│ ├── test_bitset.cpp
│ ├── test_boost_array.cpp
│ ├── test_check.cpp
│ ├── test_class_info_load.cpp
│ ├── test_class_info_save.cpp
│ ├── test_codecvt_null.cpp
│ ├── test_complex.cpp
│ ├── test_const_load_fail1.cpp
│ ├── test_const_load_fail1_nvp.cpp
│ ├── test_const_load_fail2.cpp
│ ├── test_const_load_fail2_nvp.cpp
│ ├── test_const_load_fail3.cpp
│ ├── test_const_load_fail3_nvp.cpp
│ ├── test_const_pass.cpp
│ ├── test_const_save_warn1.cpp
│ ├── test_const_save_warn1_nvp.cpp
│ ├── test_const_save_warn2.cpp
│ ├── test_const_save_warn2_nvp.cpp
│ ├── test_const_save_warn3.cpp
│ ├── test_const_save_warn3_nvp.cpp
│ ├── test_const_save_warn4.cpp
│ ├── test_const_save_warn4_nvp.cpp
│ ├── test_contained_class.cpp
│ ├── test_cyclic_ptrs.cpp
│ ├── test_delete_pointer.cpp
│ ├── test_deque.cpp
│ ├── test_derived.cpp
│ ├── test_derived_class.cpp
│ ├── test_derived_class_ptr.cpp
│ ├── test_diamond.cpp
│ ├── test_diamond_complex.cpp
│ ├── test_dll_exported.cpp
│ ├── test_dll_plugin.cpp
│ ├── test_dll_simple.cpp
│ ├── test_enable_shared_from_this.cpp
│ ├── test_exported.cpp
│ ├── test_forward_list.cpp
│ ├── test_forward_list_ptrs.cpp
│ ├── test_helper_support.cpp
│ ├── test_inclusion.cpp
│ ├── test_inclusion2.cpp
│ ├── test_interators.cpp
│ ├── test_interrupts.cpp
│ ├── test_iterators.cpp
│ ├── test_iterators_base64.cpp
│ ├── test_list.cpp
│ ├── test_list_ptrs.cpp
│ ├── test_map.cpp
│ ├── test_map_hashed.cpp
│ ├── test_mi.cpp
│ ├── test_mult_archive_types.cpp
│ ├── test_multi_shared_lib.cpp
│ ├── test_multiple_inheritance.cpp
│ ├── test_multiple_ptrs.cpp
│ ├── test_native_array.cpp
│ ├── test_new_operator.cpp
│ ├── test_no_rtti.cpp
│ ├── test_non_default_ctor.cpp
│ ├── test_non_default_ctor2.cpp
│ ├── test_non_intrusive.cpp
│ ├── test_not_serializable.cpp
│ ├── test_null_ptr.cpp
│ ├── test_nvp.cpp
│ ├── test_object.cpp
│ ├── test_optional.cpp
│ ├── test_p_helper.cpp
│ ├── test_pimpl.cpp
│ ├── test_polymorphic.cpp
│ ├── test_polymorphic2.cpp
│ ├── test_polymorphic2.hpp
│ ├── test_polymorphic2imp.cpp
│ ├── test_polymorphic_A.cpp
│ ├── test_polymorphic_A.hpp
│ ├── test_primitive.cpp
│ ├── test_priority_queue.cpp
│ ├── test_private_base.cpp
│ ├── test_private_base2.cpp
│ ├── test_private_ctor.cpp
│ ├── test_queue.cpp
│ ├── test_recursion.cpp
│ ├── test_registered.cpp
│ ├── test_reset_object_address.cpp
│ ├── test_set.cpp
│ ├── test_set_hashed.cpp
│ ├── test_shared_ptr.cpp
│ ├── test_shared_ptr_132.cpp
│ ├── test_shared_ptr_multi_base.cpp
│ ├── test_simple_class.cpp
│ ├── test_simple_class_ptr.cpp
│ ├── test_singleton.cpp
│ ├── test_singleton_inherited.cpp
│ ├── test_singleton_plain.cpp
│ ├── test_slist.cpp
│ ├── test_slist_ptrs.cpp
│ ├── test_smart_cast.cpp
│ ├── test_split.cpp
│ ├── test_stack.cpp
│ ├── test_static_warning.cpp
│ ├── test_strong_typedef.cpp
│ ├── test_tools.hpp
│ ├── test_tracking.cpp
│ ├── test_traits_fail.cpp
│ ├── test_traits_pass.cpp
│ ├── test_unique_ptr.cpp
│ ├── test_unregistered.cpp
│ ├── test_valarray.cpp
│ ├── test_variant.cpp
│ ├── test_vector.cpp
│ ├── test_void_cast.cpp
│ ├── test_z.cpp
│ ├── text_archive.hpp
│ ├── text_warchive.hpp
│ ├── xml_archive.hpp
│ └── xml_warchive.hpp
└── util/
└── test.jam
================================================
FILE CONTENTS
================================================
================================================
FILE: .drone/drone.sh
================================================
#!/bin/bash
# Copyright 2020 Rene Rivera, Sam Darwin
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at http://boost.org/LICENSE_1_0.txt)
set -e
export TRAVIS_BUILD_DIR=$(pwd)
export DRONE_BUILD_DIR=$(pwd)
export TRAVIS_BRANCH=$DRONE_BRANCH
export VCS_COMMIT_ID=$DRONE_COMMIT
export GIT_COMMIT=$DRONE_COMMIT
export REPO_NAME=$DRONE_REPO
export PATH=~/.local/bin:/usr/local/bin:$PATH
if [ "$DRONE_JOB_BUILDTYPE" == "boost" ]; then
echo '==================================> INSTALL'
BOOST_BRANCH=develop && [ "$TRAVIS_BRANCH" == "master" ] && BOOST_BRANCH=master || true
cd ..
git clone -b $BOOST_BRANCH https://github.com/boostorg/boost.git boost-root
cd boost-root
git submodule update --init tools/build
git submodule update --init libs/config
git submodule update --init tools/boostdep
cp -r $TRAVIS_BUILD_DIR/* libs/serialization
python tools/boostdep/depinst/depinst.py serialization
./bootstrap.sh
./b2 headers
echo '==================================> SCRIPT'
echo "using $TOOLSET : : $TRAVIS_COMPILER ;" > ~/user-config.jam
./b2 -j 3 libs/serialization/test toolset=$TOOLSET link=${LINK:-shared}
fi
================================================
FILE: .drone.star
================================================
# Use, modification, and distribution are
# subject to the Boost Software License, Version 1.0. (See accompanying
# file LICENSE.txt)
#
# Copyright Rene Rivera 2020.
# For Drone CI we use the Starlark scripting language to reduce duplication.
# As the yaml syntax for Drone CI is rather limited.
#
#
globalenv={}
linuxglobalimage="cppalliance/droneubuntu1604:1"
windowsglobalimage="cppalliance/dronevs2019"
def main(ctx):
return [
linux_cxx("TOOLSET=gcc LINK=static,shared Job 0", "g++", packages="", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'TOOLSET': 'gcc', 'LINK': 'static,shared', 'TRAVIS_COMPILER': 'g++', 'DRONE_JOB_UUID': 'b6589fc6ab'}, globalenv=globalenv),
linux_cxx("TOOLSET=gcc LINK=static,shared Job 1", "g++", packages="g++-5", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'TOOLSET': 'gcc', 'LINK': 'static,shared', 'TRAVIS_COMPILER': 'g++-5', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv),
linux_cxx("TOOLSET=gcc LINK=static,shared Job 2", "g++", packages="g++-6", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'TOOLSET': 'gcc', 'LINK': 'static,shared', 'TRAVIS_COMPILER': 'g++-6', 'DRONE_JOB_UUID': 'da4b9237ba'}, globalenv=globalenv),
linux_cxx("TOOLSET=gcc LINK=static,shared Job 3", "g++", packages="g++-7", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'TOOLSET': 'gcc', 'LINK': 'static,shared', 'TRAVIS_COMPILER': 'g++-7', 'DRONE_JOB_UUID': '77de68daec'}, globalenv=globalenv),
linux_cxx("TOOLSET=clang LINK=static,shared Job 4", "clang++", packages="", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'TOOLSET': 'clang', 'LINK': 'static,shared', 'TRAVIS_COMPILER': 'clang++', 'DRONE_JOB_UUID': '1b64538924'}, globalenv=globalenv),
linux_cxx("TOOLSET=clang LINK=static,shared Job 5", "clang++", packages="libc++-dev", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'TOOLSET': 'clang', 'LINK': 'static,shared', 'TRAVIS_COMPILER': 'clang++-libc++', 'DRONE_JOB_UUID': 'ac3478d69a'}, globalenv=globalenv),
osx_cxx("TOOLSET=clang LINK=static,shared Job 6", "clang++", packages="", buildtype="boost", buildscript="drone", environment={'TOOLSET': 'clang', 'LINK': 'static,shared', 'TRAVIS_COMPILER': 'clang++', 'DRONE_JOB_UUID': 'c1dfd96eea'}, globalenv=globalenv),
osx_cxx("TOOLSET=clang LINK=static,shared Job 7", "clang++", packages="", buildtype="boost", buildscript="drone", xcode_version="10", environment={'TOOLSET': 'clang', 'LINK': 'static,shared', 'TRAVIS_COMPILER': 'clang++', 'DRONE_JOB_UUID': '902ba3cda1'}, globalenv=globalenv),
]
# from https://github.com/boostorg/boost-ci
load("@boost_ci//ci/drone/:functions.star", "linux_cxx","windows_cxx","osx_cxx","freebsd_cxx")
================================================
FILE: .gitattributes
================================================
* text=auto !eol svneol=native#text/plain
*.gitattributes text svneol=native#text/plain
# Scriptish formats
*.bat text svneol=native#text/plain
*.bsh text svneol=native#text/x-beanshell
*.cgi text svneol=native#text/plain
*.cmd text svneol=native#text/plain
*.js text svneol=native#text/javascript
*.php text svneol=native#text/x-php
*.pl text svneol=native#text/x-perl
*.pm text svneol=native#text/x-perl
*.py text svneol=native#text/x-python
*.sh eol=lf svneol=LF#text/x-sh
configure eol=lf svneol=LF#text/x-sh
# Image formats
*.bmp binary svneol=unset#image/bmp
*.gif binary svneol=unset#image/gif
*.ico binary svneol=unset#image/ico
*.jpeg binary svneol=unset#image/jpeg
*.jpg binary svneol=unset#image/jpeg
*.png binary svneol=unset#image/png
*.tif binary svneol=unset#image/tiff
*.tiff binary svneol=unset#image/tiff
*.svg text svneol=native#image/svg%2Bxml
# Data formats
*.pdf binary svneol=unset#application/pdf
*.avi binary svneol=unset#video/avi
*.doc binary svneol=unset#application/msword
*.dsp text svneol=crlf#text/plain
*.dsw text svneol=crlf#text/plain
*.eps binary svneol=unset#application/postscript
*.gz binary svneol=unset#application/gzip
*.mov binary svneol=unset#video/quicktime
*.mp3 binary svneol=unset#audio/mpeg
*.ppt binary svneol=unset#application/vnd.ms-powerpoint
*.ps binary svneol=unset#application/postscript
*.psd binary svneol=unset#application/photoshop
*.rdf binary svneol=unset#text/rdf
*.rss text svneol=unset#text/xml
*.rtf binary svneol=unset#text/rtf
*.sln text svneol=native#text/plain
*.swf binary svneol=unset#application/x-shockwave-flash
*.tgz binary svneol=unset#application/gzip
*.vcproj text svneol=native#text/xml
*.vcxproj text svneol=native#text/xml
*.vsprops text svneol=native#text/xml
*.wav binary svneol=unset#audio/wav
*.xls binary svneol=unset#application/vnd.ms-excel
*.zip binary svneol=unset#application/zip
# Text formats
.htaccess text svneol=native#text/plain
*.bbk text svneol=native#text/xml
*.cmake text svneol=native#text/plain
*.css text svneol=native#text/css
*.dtd text svneol=native#text/xml
*.htm text svneol=native#text/html
*.html text svneol=native#text/html
*.ini text svneol=native#text/plain
*.log text svneol=native#text/plain
*.mak text svneol=native#text/plain
*.qbk text svneol=native#text/plain
*.rst text svneol=native#text/plain
*.sql text svneol=native#text/x-sql
*.txt text svneol=native#text/plain
*.xhtml text svneol=native#text/xhtml%2Bxml
*.xml text svneol=native#text/xml
*.xsd text svneol=native#text/xml
*.xsl text svneol=native#text/xml
*.xslt text svneol=native#text/xml
*.xul text svneol=native#text/xul
*.yml text svneol=native#text/plain
boost-no-inspect text svneol=native#text/plain
CHANGES text svneol=native#text/plain
COPYING text svneol=native#text/plain
INSTALL text svneol=native#text/plain
Jamfile text svneol=native#text/plain
Jamroot text svneol=native#text/plain
Jamfile.v2 text svneol=native#text/plain
Jamrules text svneol=native#text/plain
Makefile* text svneol=native#text/plain
README text svneol=native#text/plain
TODO text svneol=native#text/plain
# Code formats
*.c text svneol=native#text/plain
*.cpp text svneol=native#text/plain
*.h text svneol=native#text/plain
*.hpp text svneol=native#text/plain
*.ipp text svneol=native#text/plain
*.tpp text svneol=native#text/plain
*.jam text svneol=native#text/plain
*.java text svneol=native#text/plain
================================================
FILE: .github/workflows/posix.yml
================================================
# Copyright 2020-2021 Peter Dimov
# Copyright 2021 Andrey Semashev
# Copyright 2021 Alexander Grund
# Copyright 2022 James E. King III
# Copyright 2023 Matt Borland
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
name: POSIX
on:
pull_request:
push:
branches:
- master
- develop
- feature/**
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
LIBRARY: serialization
UBSAN_OPTIONS: print_stacktrace=1
GIT_FETCH_JOBS: 8
NET_RETRY_COUNT: 5
DEFAULT_BUILD_VARIANT: release
jobs:
CI:
defaults:
run:
shell: bash
strategy:
fail-fast: false
matrix:
include:
- toolset: gcc-4.8
cxxstd: "11"
address_model: 64
os: ubuntu-latest
container: ubuntu:16.04
install:
- g++-4.8
sources:
- "ppa:ubuntu-toolchain-r/test"
- toolset: gcc-4.9
cxxstd: "11"
address_model: 64
os: ubuntu-latest
container: ubuntu:16.04
install:
- g++-4.9
sources:
- "ppa:ubuntu-toolchain-r/test"
- toolset: gcc-5
cxxstd: "11,14,1z"
address_model: 64
os: ubuntu-latest
container: ubuntu:16.04
install:
- g++-5-multilib
- toolset: gcc-5
cxxstd: "11-gnu,14-gnu,1z-gnu"
address_model: 64
os: ubuntu-latest
container: ubuntu:16.04
install:
- g++-5-multilib
- toolset: gcc-6
cxxstd: "11,14,1z"
address_model: 64
os: ubuntu-latest
container: ubuntu:18.04
install:
- g++-6-multilib
- toolset: gcc-7
cxxstd: "11,14,17"
address_model: 64
os: ubuntu-latest
container: ubuntu:18.04
install:
- g++-7-multilib
- toolset: gcc-8
cxxstd: "11,14,17,2a"
address_model: 64
os: ubuntu-latest
container: ubuntu:18.04
install:
- g++-8-multilib
- toolset: gcc-9
cxxstd: "11,14,17,2a"
address_model: 64
os: ubuntu-20.04
install:
- g++-9-multilib
- toolset: gcc-9
cxxstd: "11-gnu,14-gnu,17-gnu,2a-gnu"
address_model: 64
os: ubuntu-20.04
install:
- g++-9-multilib
- toolset: gcc-10
cxxstd: "11,14,17,20"
address_model: 64
os: ubuntu-20.04
install:
- g++-10-multilib
- toolset: gcc-11
cxxstd: "11,14,17,20,23"
address_model: 64
os: ubuntu-22.04
install:
- g++-11-multilib
- toolset: gcc-12
cxxstd: "11,14,17,20,23"
address_model: 64
os: ubuntu-22.04
install:
- g++-12-multilib
- toolset: gcc-12
cxxstd: "11-gnu,14-gnu,17-gnu,20-gnu,23-gnu"
address_model: 64
os: ubuntu-22.04
install:
- g++-12-multilib
- name: UBSAN
toolset: gcc-12
cxxstd: "11,20"
address_model: 64
ubsan: 1
os: ubuntu-22.04
install:
- g++-12-multilib
# Linux, clang
- toolset: clang
compiler: clang++-3.7
cxxstd: "11,14"
os: ubuntu-latest
container: ubuntu:16.04
install:
- clang-3.7
- toolset: clang
compiler: clang++-3.8
cxxstd: "11,14"
os: ubuntu-latest
container: ubuntu:16.04
install:
- clang-3.8
- toolset: clang
compiler: clang++-3.9
cxxstd: "11,14"
os: ubuntu-latest
container: ubuntu:18.04
install:
- clang-3.9
- toolset: clang
compiler: clang++-4.0
cxxstd: "11,14"
os: ubuntu-latest
container: ubuntu:18.04
install:
- clang-4.0
- toolset: clang
compiler: clang++-5.0
cxxstd: "11,14,1z"
os: ubuntu-latest
container: ubuntu:18.04
install:
- clang-5.0
- toolset: clang
compiler: clang++-6.0
cxxstd: "11,14,17"
os: ubuntu-latest
container: ubuntu:18.04
install:
- clang-6.0
- toolset: clang
compiler: clang++-7
cxxstd: "11,14,17"
os: ubuntu-latest
container: ubuntu:18.04
install:
- clang-7
# Note: clang-8 does not fully support C++20, so it is not compatible with libstdc++-8 in this mode
- toolset: clang
compiler: clang++-8
cxxstd: "11,14,17,2a"
os: ubuntu-latest
container: ubuntu:18.04
install:
- clang-8
- g++-7
gcc_toolchain: 7
- toolset: clang
compiler: clang++-9
cxxstd: "11,14,17,2a"
os: ubuntu-20.04
install:
- clang-9
- toolset: clang
compiler: clang++-10
cxxstd: "11,14,17,20"
os: ubuntu-20.04
install:
- clang-10
- toolset: clang
compiler: clang++-11
cxxstd: "11,14,17,20"
os: ubuntu-22.04
install:
- clang-11
- toolset: clang
compiler: clang++-12
cxxstd: "11,14,17,20,2b"
os: ubuntu-22.04
install:
- clang-12
- toolset: clang
compiler: clang++-13
cxxstd: "11,14,17,20,2b"
os: ubuntu-22.04
install:
- clang-13
- toolset: clang
compiler: clang++-14
cxxstd: "11,14,17,20,2b"
os: ubuntu-22.04
install:
- clang-14
- toolset: clang
compiler: clang++-14
cxxstd: "11-gnu,14-gnu,17-gnu,20-gnu,2b-gnu"
os: ubuntu-22.04
install:
- clang-14
- toolset: clang
compiler: clang++-15
cxxstd: "11,14,17,20,2b"
os: ubuntu-22.04
install:
- clang-15
sources:
- "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main"
source_keys:
- "https://apt.llvm.org/llvm-snapshot.gpg.key"
- toolset: clang
compiler: clang++-15
cxxstd: "11,14,17,20,2b"
os: ubuntu-22.04
install:
- clang-15
- libc++-15-dev
- libc++abi-15-dev
sources:
- "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main"
source_keys:
- "https://apt.llvm.org/llvm-snapshot.gpg.key"
cxxflags: -stdlib=libc++
linkflags: -stdlib=libc++
- toolset: clang
cxxstd: "11,14,17,2a"
os: macos-11
- toolset: clang
cxxstd: "11,14,17,20,2b"
os: macos-12
timeout-minutes: 360
runs-on: ${{matrix.os}}
container: ${{matrix.container}}
steps:
- name: Setup environment
run: |
if [ -f "/etc/debian_version" ]
then
echo "DEBIAN_FRONTEND=noninteractive" >> $GITHUB_ENV
export DEBIAN_FRONTEND=noninteractive
fi
if [ -n "${{matrix.container}}" ]
then
echo "GHA_CONTAINER=${{matrix.container}}" >> $GITHUB_ENV
if [ -f "/etc/debian_version" ]
then
apt-get -o Acquire::Retries=$NET_RETRY_COUNT update
if [ "$(apt-cache search "^python-is-python3$" | wc -l)" -ne 0 ]
then
PYTHON_PACKAGE="python-is-python3"
else
PYTHON_PACKAGE="python"
fi
apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y sudo software-properties-common tzdata wget curl apt-transport-https ca-certificates make build-essential g++ $PYTHON_PACKAGE python3 perl git cmake
fi
fi
git config --global pack.threads 0
- uses: actions/checkout@v3
- name: Install packages
if: matrix.install
run: |
declare -a SOURCE_KEYS SOURCES
if [ -n "${{join(matrix.source_keys, ' ')}}" ]
then
SOURCE_KEYS=("${{join(matrix.source_keys, '" "')}}")
fi
if [ -n "${{join(matrix.sources, ' ')}}" ]
then
SOURCES=("${{join(matrix.sources, '" "')}}")
fi
for key in "${SOURCE_KEYS[@]}"
do
for i in {1..$NET_RETRY_COUNT}
do
echo "Adding key: $key"
wget -O - "$key" | sudo apt-key add - && break || sleep 2
done
done
if [ ${#SOURCES[@]} -gt 0 ]
then
APT_ADD_REPO_COMMON_ARGS=("-y")
APT_ADD_REPO_SUPPORTED_ARGS="$(apt-add-repository --help | perl -ne 'if (/^\s*-n/) { print "n"; } elsif (/^\s*-P/) { print "P"; } elsif (/^\s*-S/) { print "S"; } elsif (/^\s*-U/) { print "U"; }')"
if [ -n "$APT_ADD_REPO_SUPPORTED_ARGS" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*n*}" ]
then
APT_ADD_REPO_COMMON_ARGS+=("-n")
fi
APT_ADD_REPO_HAS_SOURCE_ARGS="$([ -n "$APT_ADD_REPO_SUPPORTED_ARGS" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*P*}" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*S*}" -a -z "${APT_ADD_REPO_SUPPORTED_ARGS##*U*}" ] && echo 1 || echo 0)"
for source in "${SOURCES[@]}"
do
for i in {1..$NET_RETRY_COUNT}
do
APT_ADD_REPO_ARGS=("${APT_ADD_REPO_COMMON_ARGS[@]}")
if [ $APT_ADD_REPO_HAS_SOURCE_ARGS -ne 0 ]
then
case "$source" in
"ppa:"*)
APT_ADD_REPO_ARGS+=("-P")
;;
"deb "*)
APT_ADD_REPO_ARGS+=("-S")
;;
*)
APT_ADD_REPO_ARGS+=("-U")
;;
esac
fi
APT_ADD_REPO_ARGS+=("$source")
echo "apt-add-repository ${APT_ADD_REPO_ARGS[@]}"
sudo -E apt-add-repository "${APT_ADD_REPO_ARGS[@]}" && break || sleep 2
done
done
fi
sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT update
sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y ${{join(matrix.install, ' ')}}
- name: Setup GCC Toolchain
if: matrix.gcc_toolchain
run: |
GCC_TOOLCHAIN_ROOT="$HOME/gcc-toolchain"
echo "GCC_TOOLCHAIN_ROOT=\"$GCC_TOOLCHAIN_ROOT\"" >> $GITHUB_ENV
MULTIARCH_TRIPLET="$(dpkg-architecture -qDEB_HOST_MULTIARCH)"
mkdir -p "$GCC_TOOLCHAIN_ROOT"
ln -s /usr/include "$GCC_TOOLCHAIN_ROOT/include"
ln -s /usr/bin "$GCC_TOOLCHAIN_ROOT/bin"
mkdir -p "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET"
ln -s "/usr/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}" "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}"
- name: Setup Boost
run: |
echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY
LIBRARY=${GITHUB_REPOSITORY#*/}
echo LIBRARY: $LIBRARY
echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV
echo GITHUB_BASE_REF: $GITHUB_BASE_REF
echo GITHUB_REF: $GITHUB_REF
REF=${GITHUB_BASE_REF:-$GITHUB_REF}
REF=${REF#refs/heads/}
echo REF: $REF
BOOST_BRANCH=develop && [ "$REF" = "master" ] && BOOST_BRANCH=master || true
echo BOOST_BRANCH: $BOOST_BRANCH
BUILD_JOBS=$((nproc || sysctl -n hw.ncpu) 2> /dev/null)
echo "BUILD_JOBS=$BUILD_JOBS" >> $GITHUB_ENV
echo "CMAKE_BUILD_PARALLEL_LEVEL=$BUILD_JOBS" >> $GITHUB_ENV
DEPINST_ARGS=()
GIT_VERSION="$(git --version | sed -e 's/git version //')"
GIT_HAS_JOBS=1
if [ -f "/etc/debian_version" ]
then
if $(dpkg --compare-versions "$GIT_VERSION" lt 2.8.0)
then
GIT_HAS_JOBS=0
fi
else
declare -a GIT_VER=(${GIT_VERSION//./ })
declare -a GIT_MIN_VER=(2 8 0)
for ((i=0; i<${#GIT_VER[@]}; i++))
do
if [ -z "${GIT_MIN_VER[i]}" ]
then
GIT_MIN_VER[i]=0
fi
if [ "${GIT_VER[i]}" -lt "${GIT_MIN_VER[i]}" ]
then
GIT_HAS_JOBS=0
break
fi
done
fi
if [ "$GIT_HAS_JOBS" -ne 0 ]
then
DEPINST_ARGS+=("--git_args" "--jobs $GIT_FETCH_JOBS")
fi
cd ..
git clone -b "$BOOST_BRANCH" --depth 1 "https://github.com/boostorg/boost.git" "boost-root"
cd boost-root
mkdir -p libs/$LIBRARY
cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY
git submodule update --init tools/boostdep
DEPINST_ARGS+=("$LIBRARY")
python tools/boostdep/depinst/depinst.py "${DEPINST_ARGS[@]}"
if [ -z "${{matrix.cmake_tests}}" ]
then
./bootstrap.sh
./b2 headers
if [ -n "${{matrix.compiler}}" -o -n "$GCC_TOOLCHAIN_ROOT" ]
then
echo -n "using ${{matrix.toolset}} : : ${{matrix.compiler}}" > ~/user-config.jam
if [ -n "$GCC_TOOLCHAIN_ROOT" ]
then
echo -n " : © Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
This archive will compile and execute with any types which implement the
Serializable concept.
For an example see
Our archives have been factored into a tree of classes in order to minimize
repetition of code. This is shown in the accompanying
class diagram.
Any class which fulfills the following requirements will fit into
this hierarchy and implement all the features we require. Deriving from
the base class
common_oarchive.hpp provides all features we desire which
are missing from trivial_oarchive above.
All of these are associated with a default serialization defined in terms of primitive types
so it isn't a requirement to define
These are defined in
In real practice, we probably won't be quite done.
One or more of the following issues may need to be addressed:
A close examination of the archives included with the library illustrate
what it takes to make a portable archive that covers all data types.
In addition, there are 28 other tests which aren't related to any particular archive class.
The default
For each archive there is a header file in the test directory similar to the one below.
The name of this archive is passed to the test program by setting the
environmental variable
The accompanying demo program in files
As a convenience, small header files have been included which contain
a
Note that the concept of polymorphic archives is fundamentally incompatible with the
serialization of new types that are marked "primitive" by the user with:
The main utility of polymorphic archives will be to permit the building of class DLLs that will
include serialization code for all present and future archives with no redundant code.
© Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Returns a reference to
Returns a reference to
When an object is loaded to a temporary variable and later moved to another location,
this function must be called in order communicate this fact. This permits the
archive to properly implement object tracking. Object tracking is required in order
to correctly implement serialization of pointers to instances of derived classes.
The existence of the
In some cases, an archive may alter (and later restore)
the codecvt facet of the stream locale. To suppress this action,
include the
XML archives contain nested tags signifying the start and end of data fields.
These tags are normally checked for agreement with the object name when
data is loaded. If a mismatch occurs an exception is thrown. It's possible
that this may not be desired behavior. To suppress this checking of XML
tags, use
The
The normal character version ( © Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
© Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
This class implements the © Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
The C++ Standard IOStreams provides the std::codecvt
facet to handle specifically these cases. On reading from or
writing to a file, the std::basic_filebuf can call out to
the codecvt facet to convert data representations from external
format (ie. UTF-8) to internal format (ie. UCS-4) and
vice-versa. utf8_codecvt_facet is a specialization of
std::codecvt specifically designed to handle the case
of translating between UTF-8 and UCS-4.
© Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
source => 8 bit bytes => 6 bit integers => encode to base64 characters => insert line breaks => destination
We would prefer the solution that is:
Templated constructor have the form:
Iterators which fulfill the above requirements should be composable and the above sample
code should implement our binary to base64 conversion.
The standard stream iterators don't quite work for us. On systems which implement © Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
This derivation from the xml archive writes output in xml without the extra
information required to read the data back into the application. It might be
used to export one's data as simple xml for other applications or for logging
data while debugging.
To this end it is derived from the included xml archive and the save functions for
some types are specialized for this application.
The serialization library is
implemented using the Curiously Recurring Template
Pattern (CRTP). Also, all common code is factored out into
separate modules to minimize code repetition. This makes derivation from
an existing archive less straightforward than it would otherwise be.
This example illustrates several issues that have to be addressed when doing
something like this
© Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
The load function traps any exceptions that occur between the time an object
is created and its pointer is stored. Should an exception occur while
reading an archive, the created object is deleted and the de-serialized
pointer is set to NULL. This ensures that there are no memory leaks.
The fact that there are no other copies of this pointer ensures that
no pointers are left invalid. The object's destructor should
be able to delete any other existing objects in the normal manner
without problem.
test_delete_pointer.cpp
illustrates this case.
© Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Should it ever occur that an older program attempts to read newer archives whose
format has changed, this exception is thrown.
This includes
an attempt to read past the end of the file. Text files need a terminating
new line character at the end of the file which is appended when the
archive destructor is invoked. Be sure that an output archive on a stream
is destroyed before opening an input archive on that same stream. That is,
rather than using something like:
Another one is the passing of uninitialized data. In general, the behavior
of the serialization library when passed uninitialized data is undefined.
If it can be detected, it will invoke an assertion in debug builds.
Otherwise, depending on the type of archive, it may pass through without
incident or it may result in an archive with unexpected data in it.
This, in turn, can result in the throwing of this exception.
© Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Generally, there will be one and only one
The second argument is a const string which is the external
name of the type to which this record corresponds.
It may sometimes be referred to as a GUID - a Global Unique IDentifier.
It is passed through archives from one program invocation to
another to uniquely identify the types that the archive contains.
If the "export" facility is not going to be used,
this value may be NULL.
These functions are called by constructors and
destructors of classes which implement
© Copyright Robert Ramey 2005-2009.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
© Copyright Robert Ramey 2002-2009. Distributed
under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt
or copy at http://www.boost.org/LICENSE_1_0.txt)
By convention these header files are named:
boost/serialization/xxx.hpp
where xxx is the name of the header file which contains the type to be serialized.
For example, the declaration
The following discussion is based on the
class diagram.
The trade offs related to library implementation via pre-compiled code and templated
headers are well known. This library uses both. It uses templated headers
to generate code to serialize user and primitive types and it uses pre-compiled
library code for that part of the code which only depends upon the archive type.
Building of the library generates and compiles code for all archives implemented.
This is a ripe topic in itself. It's touched upon by the
boost iterator libraries,
View Template Library, and others.
The code for these iterators is really independent of this library. But since it
hasn't been and probably won't be reviewed outside of this context. I've left it in a directory
local to the serialization library:
boost/archive/iterators.
These iterators are described in
Dataflow Iterators.
© Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
© Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Note that the code conversion included for wide character text and XML
archives could alter
To produce wide character text output (i.e. 16 bit characters on Win32 systems),
do the following.
This compiler does not have RTTI or exception handling turned on by default. Although
they are not strictly necessary to use the serialization package, the example and test
programs presume that they are enabled. So be sure your command line or IDE settings
enable these features if you want to build and run these programs.
This compiler can treat Revised 1 November, 2004
© Copyright Robert Ramey 2002-2015.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
If a group of function objects were all derived from the
same polymorphic base class - perhaps via multiple inheritance,
then the function object effectively becomes a "variable" which
encapsulates code.
This case study would show how to do this.
This case study would show how to make a useful archive adaptor.
It would also be possible to make a "generic runtime helper"
which would effectively extend the API of the library. Previously
the library included such a helper class. It was removed
in favor of the current implementation. But this functionality
should be added back in with another adaptor which would
become part of the library.
Revised 1 November, 2008
© Copyright Robert Ramey 2002-2008.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Here, we use the term "serialization" to mean
the reversible deconstruction of an arbitrary set of C++ data structures
to a sequence of bytes. Such a system can be used to reconstitute
an equivalent structure in another program context. Depending on
the context, this might used implement object persistence, remote
parameter passing or other facility. In this system we use the term
"archive" to refer to a specific rendering of this
stream of bytes. This could be a file of binary data, text data,
XML, or some other created by the user of this library.
Revised 1 November, 2004
© Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
The downside of this is that one has to know which archives are going
to be used with hidden serializations. This is an effect of using template
driven code. One can invoke explicitly instantiation for all known templates and
presume that the linker will exclude unreferenced code. This should
work but is platform dependent.
© Copyright Robert Ramey 2002-2004.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
In order for this
code compiler the following alteration must be made:
If we made one of the functions of © Copyright Robert Ramey 2015.
Distributed under the Boost Software License, Version 1.0. (See
accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
I found that persistence is often used to refer
to something quite different. Examples are storage of class
instances (objects) in database schema [4]
This library will be useful in other contexts besides implementing persistence. The
most obvious case is that of marshalling data for transmission to another system.
Archive classes are NOT derived from
streams even though they have similar syntax rules.
Treating strings as STL vectors would result in minimal code size. This was
not done because:

Serialization
Acknowledgments
& syntax used to implement both save and
load in one function specification.

Serialization
Archive Class Reference
Trivial Archive
The Archive concept specifies the functions that a
class must implement in order to be used to serialize
Serializable types.
Our discussion will focus on archives used for saving as the hierarchy is exactly analogous
for archives used for loading data.
Minimum Requirements
The simplest class which will model the Archive concept specifies the functions that a
class will look like:
The simplest possible input archive class is analogous to the above.
In the following discussion, only output archives will be addressed.
Input archives are exactly symmetrical to output archives.
#include <cstddef> // std::size_t
//////////////////////////////////////////////////////////////
// class trivial_oarchive
class trivial_oarchive {
public:
//////////////////////////////////////////////////////////
// public interface used by programs that use the
// serialization library
typedef boost::mpl::bool_<true> is_saving;
typedef boost::mpl::bool_<false> is_loading;
template<class T> void register_type(){}
template<class T> trivial_oarchive & operator<<(const T & t){
return *this;
}
template<class T> trivial_oarchive & operator&(const T & t){
return *this << t;
}
void save_binary(void *address, std::size_t count){};
};
demo_trivial_archive.cpp.
Of course this program won't produce any output as it is. But it provides
the starting point for a simple class which can be used to log formatted
output. See the implementation of a simple
log archive to how this has been done.
More Useful Archive Classes
The above example is fine as far as it goes. But it doesn't implement
useful features such as serialization of pointers, class versioning
and others. This library implements a family of full featured archive
classes appropriate for a variety of purposes.
Given a suitable definitions of
#include <cstddef> // std::size_t
#include <boost/archive/detail/common_oarchive.hpp>
/////////////////////////////////////////////////////////////////////////
// class complete_oarchive
class complete_oarchive :
public boost::archive::detail::common_oarchive<complete_oarchive>
{
// permit serialization system privileged access to permit
// implementation of inline templates for maximum speed.
friend class boost::archive::save_access;
// member template for saving primitive types.
// Specialize for any types/templates that require special treatment
template<class T>
void save(T & t);
public:
//////////////////////////////////////////////////////////
// public interface used by programs that use the
// serialization library
// archives are expected to support this function
void save_binary(void *address, std::size_t count);
};
save
and save_binary,
any program using serialization with a conforming C++ compiler should compile
and run with this archive class.
Optional Overrides
The detail::common_oarchive class contains
a number of functions that are used by various parts of the serialization library
to help render the archive in a particular form.
void save_start(char const *)
Purpose:To inject/retrieve an object name into the archive. Used
by XML archive to inject "<name>" before data.
void save_end(char const *)
Purpose:To inject/retrieve an object name into the archive. Used
by XML archive to inject "</name>" after data.
void end_preamble()
Purpose:Called each time user data is saved.
It's not called when archive bookkeeping data is saved. This is used by XML archives
to determine when to inject a ">" character at the end of an XML header. XML output archives
keep their own internal flag indicating that data being written is header data. This
internal flag is reset when an object start tag is written. When
void end_preamble() is invoked and this internal flag is set
a ">" character is appended to the output and the internal flag is reset. The default
implementation for void end_preamble() is a no-op thereby permitting it
to be optimised away for archive classes that don't use it.
template<class T>
void save_override(T & t, int);
archive::save(Archive & ar, t)
This is the main entry into the serialization library.
Purpose:This can be specialized in cases where the data is to be written
to the archive in some special way. For example, XML archives implement special handling for
name-value pairs by overriding this function template for name-value pairs.
This replaces the default name-value pair handling, which is just to throw away the name,
with one appropriate for XML which writes out the start of an XML tag with the correct object name.
Types used by the serialization library
The serialization library injects bookkeeping data into the serialization archive.
This data includes things like object ids, version numbers, class names etc. Each
of these objects is included in a wrapper so that the archive class can override the
implementation of void save_override(T & t, int);.
For example, in the XML archive, the override for this type renders an object_id equal to 23 as
"object_id=_23". The following table lists the types defined in the
boost::archive namespace
used internally by the serialization library:
type default
serialized asversion_typeunsigned intobject_id_typeunsigned intobject_id_reference_typeunsigned intclass_id_typeintclass_id_optional_typenothingclass_id_reference_typeinttracking_typeboolclassname_typestringsave_override
for these types.
basic_archive.hpp.
All of these types have been assigned an
implementation level of
primitive and are convertible to types such as int, unsigned int, etc.
so that they have default implementations. This is illustrated by
basic_text_iarchive.hpp.
which relies upon the default. However, in some cases, overrides will have to be
explicitly provided for these types. For an example see
basic_xml_iarchive.hpp.
The attached class diagram
shows the relationships between classes used to implement the serialization library.
stream or
streambuf
as a template parameter rather than simple classes.
Combined with the above, even more issues arise with non-conforming compilers.
Usage
The newly created archive will usually be stored in its own header module. All
that is necessary is to include the header and construct an instance of the new archive.
EXCEPT for one special case.
To make this work, the following should be included after the archive
class definition.
BOOST_CLASS_EXPORT is used
to instantiate the serialization code for the included archives.
Failure to do this will not inhibit the program from compiling, linking
and executing properly - except in one case. If an instance of a derived
class is serialized through a pointer to its base class, the program
will throw an
BOOST_SERIALIZATION_REGISTER_ARCHIVE(Archive)
unregistered_class
exception.
Testing
Exhaustive testing of the library requires testing the different aspects of object
serialization with each archive. There are 46 different tests that can run with any archive.
There are 5 "standard archives" included with the system.
(3 in systems that don't support wide character i/o).
bjam testing setup will run all
the above described tests. This will result in as many as 46 archive tests * 5
standard archives + 28 general tests = 258 tests. Note that a complete test of the
library would include DLL vs static library, release vs debug so the actual total
would be closer to 1032 tests.
BOOST_ARCHIVE_TEST
to the name of the header. Here is the header file
test_archive.hpp . Test header files for
other archives are similar.
To test a new archive, for example, portable binary archives, with the gcc compiler,
make a header file
// text_archive test header
// include output archive header
#include <boost/archive/text_oarchive.hpp>
// set name of test output archive
typedef boost::archive::text_oarchive test_oarchive;
// set name of test output stream
typedef std::ofstream test_ostream;
// repeat the above for input archive
#include <boost/archive/text_iarchive.hpp>
typedef boost::archive::text_iarchive test_iarchive;
typedef std::ifstream test_istream;
// define open mode for streams
// binary archives should use std::ios_base::binary
#define TEST_STREAM_FLAGS (std::ios_base::openmode)0
portable_binary_archive.hpp
and invoke bjam with
This process in encapsulated in the shell or cmd script
-sBOOST_ARCHIVE_LIST=portable_binary_archive.hpp
library_test whose command line is
library_test --toolset=gcc -sBOOST_ARCHIVE_LIST=portable_binary_archive.hpp
Polymorphic Archives
Motivation
All archives described so far are implemented as templates. Code to save and load
data to archives is regenerated for each combination of archive class and data type.
Under these circumstances, a good optimizing compiler that can expand
inline functions to enough depth will generate fast code.
However:
Implementation
The solution is the pair polymorphic_oarchive
and polymorphic_iarchive. They present a common interface of virtual
functions - no templates - that is equivalent to the standard templated one.
This is shown in the accompanying
class diagram
demo_polymorphic.cpp,
demo_polymorphic_A.hpp, and
demo_polymorphic_A
show how polymorphic archives are to be used. Note the following:
As can be seen in the
class diagram
and the header files, this implementation is just a composition of the polymorphic
interface and the standard template driven implementation. This composition is
accomplished by the templates
demo_polymorphic_A.hpp and
demo_polymorphic_A.cpp
contain no templates and no reference to any specific archive implementation. That is, they will
only have to be compiled once for all archive implementations. This even applies to archives classes
created in the future.
demo_polymorphic.cpp
specifies a specific archive implementation.
polymorphic_iarchive_route.hpp
and
polymorphic_oarchive_route.hpp
which redirect calls to the polymorphic archives to the specific archive.
As these contain no code specific to the particular implementation archive, they can be used to create
a polymorphic archive implementation from any functioning templated archive implementation.
typedef for a polymorphic implementation for each corresponding
templated one. For example, the headers
polymorphic_text_iarchive.hpp
and
polymorphic_text_oarchive.hpp.
contain the typedef for the polymorphic implementation
of the standard text archive classes
text_iarchive.hpp
and
text_oarchive.hpp
respectively. All included polymorphic archives use the same naming scheme.
Usage
Polymorphic archives address the issues raised above regarding templated implementation.
That is, there is no replicated code, and no recompilation for new archives. This will
result in smaller executables for program which use more than one type of archive, and
smaller DLLS. There is a penalty for calling archive functions through a virtual function
dispatch table and there is no possibility for a compiler to inline
archive functions. This will result in a detectable degradation in performance for
saving and loading archives.
Code to implement serialization for these types is instantiated "on the fly" in the user's program.
But this conflicts with the whole purpose of the polymorphic archive. An attempt to
serialize such a primitive type will result in a compilation error since the common polymorphic
interface is static and cannot instantiate code for a new type.
BOOST_CLASS_IMPLEMENTATION(my_primitive_type, boost::serialization::primitive_type)

Serialization
Archive Concepts
Notation
In the following descriptions
SA is an type modeling the Saving Archive Concept.
sa is an instance of type SA.
LA is an type modeling the Loading Archive Concept.
la is an instance of type LA.
T is an Serializable Type.
x is an instance of type T Type.
u,v is a pointer to a an instance of type T.
count is an instance of a type that can be converted to std::size_t.
Saving Archive Concept
Associated Types
Intuitively, a type modeling this concept will generate a sequence of bytes
corresponding to an arbitrary set of C++ data structures. Each type modeling the
Saving Archive concept (SA) may be associated with another type modeling the
Loading Archive Concept(LA).
This associated type will perform the inverse operation.
That is, given a sequence of bytes generated by SA, it will generate a set of
C++ data structures that is equivalent to the original.
The notion of equivalence is defined by the implementations of the pair of archives and the
way the data are rendered serializable.
Valid Expressions
SA::is_saving
SA::is_loading
sa << x
sa & x
x along with other information to sa.
This other information is defined by the implementation of the archive.
Typically this information is that which is required by a corresponding
Loading Archive type to properly restore the value of x.
sa.
sa.save_binary(u, count)
size_t(count) bytes found at
u.
sa.register_type<T>()
sa.register_type(u)
sa is a template argument.
For more information, see Template Invocation syntax
sa.get_library_version()
sa.get_helper<Helper>(void * const helper_instance_id = 0)
la.get_helper<Helper>(void * const helper_instance_id = 0)
below.
Loading Archive Concept
Associated Types
Each model of this concept presumes the
existence of a corresponding type modeling the
Saving Archive Concept.
The purpose of an instance of this concept is to convert a sequence of bytes
generated by this corresponding type to a set of C++ data structures
equivalent to the original.
Valid Expressions
There are archives based on text, binary and XML file
formats but all have the above interface. Given that all archives present
the same public interface, specification of serialization is exactly the same
for all archives. Archive classes have other members not mentioned here.
However they are related to the internal functioning of the library and
are not meant to be called by users of an archive. Implementation of new
archives is discussed in
New Archives - Implementation.
LA::is_saving
LA::is_loading
la >> x
la & x
x to a value retrieved from la.
la.
la.load_binary(u, count)
la size_t(count) bytes and stores
them in memory starting at u.
la.register_type<T>()
la.register_type(u)
la is a template argument.
For more information, see Template Invocation syntax
la.get_library_version()
la.get_helper<Helper>(void * const helper_instance_id)
la.get_helper<Helper>(void * const helper_instance_id)
is invoked for a given helper_instance_id, Helper, a default-constructed
Helper object is created, attached to
la and a reference to it is returned. Subsequent
invocations of la.get_helper<Helper>(void * const helper_instance_id) with the same id value return
a reference to the formerly constructed object. All objects created in this manner are
destroyed upon la destruction time. The purpose
of helper objects is discussed in
Special Considerations - Helper Support.
la.reset_object_address(v, u)
la.delete_created_pointers()
<<
and >> suggests
a relationship between archives and C++ i/o streams. Archives are not
C++ i/o streams. All the archives included with this system take a stream
as an argument in the constructor and that stream is used for output or input.
However, this is not a requirement of the serialization functions or the
archive interface. It just turns out that the archives written so far have
found it useful to base their implementation on streams.
Archive Models
This library includes various implementations of the Archive concept.
An archive is defined by two complementary classes. One is for saving data while
the other is for loading it.
This library includes a number of archive implementations that are "ready to go" for the
most common requirements. These classes implement the archive concept for differing data formats.
They can be used "as is" or as a basis for developing one's own particular type of archive.
An archive is defined by two complementary classes. One is for saving data while the other is for loading it.
To invoke serialization using one of
these archives, one or more of the following header files must be
included in the code module containing the serialization code.
All of these archives implement the same interface. Hence, it should suffice to describe only one
of them in detail. For this purpose we will use the text archive.
// a portable text archive
boost::archive::text_oarchive // saving
boost::archive::text_iarchive // loading
// a portable text archive using a wide character stream
boost::archive::text_woarchive // saving
boost::archive::text_wiarchive // loading
// a portable XML archive
boost::archive::xml_oarchive // saving
boost::archive::xml_iarchive // loading
// a portable XML archive which uses wide characters - use for utf-8 output
boost::archive::xml_woarchive // saving
boost::archive::xml_wiarchive // loading
// a non-portable native binary archive
boost::archive::binary_oarchive // saving
boost::archive::binary_iarchive // loading
namespace boost {
namespace archive {
enum archive_flags {
no_header = 1, // suppress archive header info
no_codecvt = 2, // suppress alteration of codecvt facet
no_xml_tag_checking = 4 // suppress checking of xml tags - ignored on saving
};
} // archive
} // boost
namespace boost {
namespace archive {
class text_oarchive : ...
{
...
public:
... // implementation of the Saving Archive concept
text_oarchive(std::ostream & os, unsigned int flags = 0);
~text_oarchive();
};
} // archive
} // boost
text_oarchive(std::ostream & os, unsigned int flags = 0);
stream as
an argument and optional flags. For most applications there will be no need to use flags.
Flags are defined by enum archive_flags enumerator.
Multiple flags can be combined with the | operator.
By default, archives prepend
output with initial data which helps identify them as archives produced by this system.
This permits a more graceful handling of the case where an attempt is made to load an archive
from an invalid file format. In addition to this, each type of archive might have
its own information. For example, native binary archives include information about
sizes of native types and endianness to gracefully handle the case where it has been
erroneously assumed that such an archive is portable across platforms. In some cases,
where this extra overhead might be considered objectionable, it can be suppressed with the
no_header flag.
no_codecvt flag.
no_xml_tag_checking flag.
~text_oarchive();
namespace boost {
namespace archive {
class text_iarchive : ...
{
...
public:
... // implementation of the Loading Archive concept
text_iarchive(std::istream & is, unsigned int flags = 0);
~text_iarchive();
};
} //namespace archive
) //namespace boost
text_iarchive(std::istream & is, unsigned int flags = 0);
stream as
an argument and optional flags. If flags are used, they should be the same
as those used when the archive was created. Function and usage of flags is described
above.
~text_iarchive();
binary_oarchive and
binary_iarchive classes are
implemented in terms of the more basic
std::streambuf. So, in addition
to the common class interface described above, they include the following
constructors:
binary_oarchive(std::streambuf & bsb, unsigned int flags = 0);
binary_iarchive(std::streambuf & bsb, unsigned int flags = 0);
Exceptions
All of the archive classes included may throw exceptions. The list of exceptions that might
be thrown can be found in section Archive Exceptions
of this documentation.
Character Sets
This library includes two archive classes for XML. The wide character
version (xml_w?archive) renders its output as UTF-8 which can
handle any wide character without loss of information.
std::string data is converted from multi-byte format to wide
character format using the current
locale. Hence this version should give a fair rendering of all
C++ data for all cases. This could result in some unexpected behavior.
Suppose an std::string
is created with the locale character
set to hebrew characters. On output this is converted to wide characters.
On input however, there could be a problem if the locale is
not set the same as when the archive is created.
xml_?archive) renders
std::string output without any conversion. Though this may work
fine for serialization, it may create difficulties if the XML archive is used
for some other purpose.

Serialization
Bibliography

Serialization
Text Archive Class Diagram
This diagram shows the relationship between the various classes that implement saving (output
serialization) for text archives. The hierarchy and organization is similar for loading and for
other types of archives as well. In the diagram, classes written in blue
implement saving for a given archive type. (in this case it's text archives).
Users include classes in red to save their data from a particular
type of archive. Other classes whose names are in black implement the library and should
never change. They are in
basic_oarchive ->
|
|
| interface_oarchive<text_oarchive> ->
| /
| /
| _________/
| /
| /
| /
common_oarchive<text_oarchive> ->
|
|
basic_text_oarchive<text_oarchive> ->
|
|
| basic_text_oprimitive<basic_ostream> ->
| /
| /
| _________/ interface_oarchive<polymorphic_oarchive> ->
| / |
| / |
| / |
text_oarchive_impl<text_oarchive> -> polymorphic_oarchive_impl ->
| \ |
| \ |
| \_____________________________________ polymorphic_oarchive ->
| \ /
| \ /
| \ /
text_oarchive -> polymorphic_oarchive_route<text_oarchive_impl<text_oarchive> > ->
|
|
|
polymorphic_text_oarchive ->
namespace boost::archive::detail
basic_oarchive
interface_oarchive<text_oarchive>
polymorphic_oarchive
as well as for archive implementations.
common_oarchive<text_oarchive>
basic_oarchive and the template interface used by archive
class implementations.
basic_text_oarchive<text_oarchive>
basic_text_oprimitive<basic_ostream>
text_oarchive_impl<text_oarchive>
text_oarchive
text_oarchive_impl<text_oarchive> .
We can't use typedef because a
typedef can't refer to it self in its definition.
This is the class name that is used to serialize to a text archive.
interface_oarchive<polymorphic_oarchive>
common_oarchive uses.
polymorphic_oarchive
save(T &t)
for all primitive types T. This is the class that is used to do pre-compile serialization of classes
for all archives present and future.
polymorphic_oarchive_route<text_oarchive_impl<text_oarchive> >
polymorphic_oarchive in terms of a specific
concrete class. Virtual function calls are routed to the implementing class. In this example,
that implementing class would be text_oarchive_impl.
polymorphic_text_oarchive
polymorphic_text_archive rather than
polymorphic_oarchive_route<text_oarchive_impl<text_oarchive> >
utf8_codecvt_facet
template<
typename InternType = wchar_t,
typename ExternType = char
> utf8_codecvt_facet
Rationale
UTF-8 is a method of encoding Unicode text in environments
where data is stored as 8-bit characters and some ascii characters
are considered special (i.e. Unix filesystem filenames) and tend
to appear more commonly than other characters. While
UTF-8 is convenient and efficient for storing data on filesystems,
it was not meant to be manipulated in memory by
applications. While some applications (such as Unix's 'cat') can
simply ignore the encoding of data, others should convert
from UTF-8 to UCS-4 (the more canonical representation of Unicode)
on reading from file, and reversing the process on writing out to
file.
Template Parameters
Parameter Description Default
InternType
The internal type used to represent UCS-4 characters.
wchar_t
ExternType
The external type used to represent UTF-8 octets.
char_t
Requirements
utf8_codecvt_facet defaults to using char as
its external data type and wchar_t as its internal
datatype, but on some architectures wchar_t is
not large enough to hold UCS-4 characters. In order to use
another internal type.You must also specialize std::codecvt
to handle your internal and external types.
(std::codecvt<char,wchar_t,std::mbstate_t> is required to be
supplied by any standard-conforming compiler).
Example Use
The following is a simple example of using this facet:
//...
// My encoding type
typedef wchar_t ucs4_t;
std::locale old_locale;
std::locale utf8_locale(old_locale,new utf8_codecvt_facet<ucs4_t>);
// Set a New global locale
std::locale::global(utf8_locale);
// Send the UCS-4 data out, converting to UTF-8
{
std::wofstream ofs("data.ucd");
ofs.imbue(utf8_locale);
std::copy(ucs4_data.begin(),ucs4_data.end(),
std::ostream_iterator<ucs4_t,ucs4_t>(ofs));
}
// Read the UTF-8 data back in, converting to UCS-4 on the way in
std::vector<ucs4_t> from_file;
{
std::wifstream ifs("data.ucd");
ifs.imbue(utf8_locale);
ucs4_t item = 0;
while (ifs >> item) from_file.push_back(item);
}
//...
History
This code was originally written as an iterator adaptor over
containers for use with UTF-8 encoded strings in memory.
Dietmar Kuehl suggested that it would be better provided as a
codecvt facet.
Resources
Copyright © 2001
Ronald Garcia,
Indiana University
(garcia@osl.iu.edu)
Andrew Lumsdaine,
Indiana University
(lums@osl.iu.edu)Contents
================================================
FILE: doc/dataflow.html
================================================

Serialization
Dataflow Iterators
Motivation
Consider the problem of translating an arbitrary length sequence of 8 bit bytes
to base64 text. Such a process can be summarized as:
The approach that comes closest to meeting these requirements is that described
and implemented with Iterator Adaptors.
The fundamental feature of an Iterator Adaptor template that makes it interesting to
us is that it takes as a parameter a base iterator from which it derives its
input. This suggests that something like the following might be possible.
Indeed, this seems to be exactly the kind of problem that iterator adaptors are
intended to address. The Iterator Adaptor library already includes
modules which can be configured to implement some of the operations above. For example,
included is
transform_iterator, which can be used to implement 6 bit integer => base64 code.
typedef
insert_linebreaks< // insert line breaks every 76 characters
base64_from_binary< // convert binary values to base64 characters
transform_width< // retrieve 6 bit integers from a sequence of 8 bit bytes
const char *,
6,
8
>
>
,76
>
base64_text; // compose all the above operations in to a new iterator
std::copy(
base64_text(address),
base64_text(address + count),
ostream_iterator<CharType>(os)
);
Dataflow Iterators
Unfortunately, not all iterators which inherit from Iterator Adaptors are guaranteed
to meet the composability goals stated above. To accomplish this purpose, they have
to be written with some additional considerations in mind.
We define a Dataflow Iterator as an class inherited from iterator_adaptor which
fulfills a small set of additional requirements.
Templated Constructors
When these constructors are applied to our example of above, the following code is generated:
template<class T>
dataflow_iterator(T start) :
iterator_adaptor(Base(start))
{}
The recursive application of this template is what automatically generates the
constructor
std::copy(
insert_linebreaks(
base64_from_binary(
transform_width(
address
),
)
),
insert_linebreaks(
base64_from_binary(
transform_width(
address + count
)
)
)
ostream_iterator<char>(os)
);
base64_text(const char *) in our example above. The original
Iterator Adaptors include a make_xxx_iterator to fulfill this function.
However, I believe these are unwieldy to use compared to the above solution using
Templated constructors.
Dereferencing
Dereferencing some iterators can cause problems. For example, a natural
way to write a remove_whitespace iterator is to increment past the initial
whitespaces when the iterator is constructed. This will fail if the iterator passed to the
constructor "points" to the end of a string. The
filter_iterator is implemented
in this way so it can't be used in our context. So, for implementation of this iterator,
space removal is deferred until the iterator actually is dereferenced.
Comparison
The default implementation of iterator equality of iterator_adaptor just
invokes the equality operator on the base iterators. Generally this is satisfactory.
However, this implies that other operations (E. G. dereference) do not prematurely
increment the base iterator. Avoiding this can be surprisingly tricky in some cases.
(E.G. transform_width)
Iterators Included in the Library
Dataflow iterators for the serialization library are all defined in the namespace
boost::archive::iterators included here are:
boost::filter_iteratorwchar_t
as unsigned short integers (E.G. VC 6) they didn't function as I expected. I also made some
adjustments to be consistent with our concept of Dataflow Iterators. Like the rest of our
iterators, they are found in the namespace boost::archive::interators to avoid
conflicts with the standard library versions.

Serialization
Derivation from an Existing Archive
Log Archive
It may happen that one wants to create a new archive class by derivation from one
of the included ones. Included is a sample program that shows how to derive a
new archive from one of the ones included with the library. The first example is
demo_log.cpp.
xml_oarchive_impl
NOT xml_oarchive
As described in the comments in
xml_oarchive.hpp.
xml_oarchive really a shorthand name for
xml_oarchive_impl<xml_oarchive>. So we should derive
from xml_oarchive_impl<log_archive> rather
than xml_oarchive.
class log_archive :
// don't derive from xml_oarchive !!!
public xml_oarchive_impl<log_archive>
{
...
log_archive between the <>
This is required so that base classes can downcast their this pointer
to the most derived class. This is referred to as Curiously Recurring
Template Pattern (CRTP) [11].
It is used to implement static polymorphism.
friend class detail::common_oarchive<log_archive>;
friend class basic_xml_oarchive<log_archive>;
friend class boost::serialization::save_access;
save_override
for saving primitives. Usage of a function name in a derived class
"hides" similarly named functions of the base class. That is,
function name overloading doesn't automatically
include base classes. To address this, we can use:
which should work on conforming compilers. However, I have found
that the following equivalent works on more compilers.
using xml_oarchive_impl<derived_t>::save;
void save(const unsigned int t);
...
so it's what I use.
// default fall through for any types not specified here
template<class T>
void save(const T & t){
xml_oarchive_impl<derived_t>::save(t);
}
void save(const unsigned int t);
...
for just this purpose. Failure to include required template definitions
will result in undefined symbol errors when the program is linked.
// explicitly instantiate for this type of binary stream
#include <boost/archive/basic_binary_oprimitive.ipp>
Base classes using CRTP must be templates with a parameter corresponding to
the most derived class. As presented here, this class doesn't qualify, so
it cannot be used as a base class. In order to derive further from this class,
it would have to be reorganized along the lines of the original xml_oarchive.
Specifically, it would look something like:
template<class Archive>
class log_archive_impl :
// don't derive from xml_oarchive !!!
public xml_oarchive_impl<Archive>
{
...
};
// do not derive from this class !!!
class log_archive :
public log_archive_impl<log_archive>
{
public:
log_archive(std::ostream & os, unsigned int flags = 0) :
log_archive_impl<xml_oarchive>(os, flags)
{}
};

Serialization
Exception Safety
The process of loading an archive may result in the creation of new objects. That
same process may throw an exception at some point. In order to prevent memory leaks
and invalid pointers, these situations must be considered. Unfortunately, there is
no simple universal solution to this problem. The manner of addressing this must
depend on the design of the data structures to be serialized. Below, we discuss
varying scenarios in increasing order of difficulty. This discussion presumes that
the class member functions are exception safe before considering serialization.
That is, the destructor could be called at anytime without referencing
an invalid pointer, or creating a memory leak.
class contains no pointers
No problem here.
class contains only owned pointers
From here on, we have to make a distinction between pointers used
to manage heap storage (owned pointers) and pointers used to refer
to related objects (referenced pointers). Programs containing owned
pointers must contain code for deleting these objects and returning the
deallocated storage to the heap. Programs containing referenced pointers
must be designed to ensure that no such referenced pointers are de-referenced
after the object pointed to has been destroyed and its storage returned
to the heap. If a pointer is stored in only one place, it must be an owned
pointer.
class contains one or more referenced pointers
This situation can be further subdivided into two cases
owned pointers are always serialized before referenced pointers
Object tracking will ensure that no new objects will be created
by the loading of a referenced pointer.
If an exception occurs, referenced pointers will not need to be deleted
so there will be no memory leaks. The destructor of this class won't attempt to
delete these pointers so there will be no problem with dangling references.
Owned pointers are handled exactly as described above.
class contains referenced pointers which might be created by load
If a referenced pointer is loaded before its corresponding owned
pointer, the object will be allocated on the heap. In certain cases
it cannot be known which pointers were created by their owners and which
were created by the load function. To address this:
demo_exception.cpp
is a program that illustrates this case.
try/catch block.
delete_created_pointers() to delete any pointers
created by the class load. Without other action, objects created in
this way would end up as memory leaks as they are not considered owned
pointers and hence aren't destroyed.
Other cases
Situations not covered above are pointers for which the classifications of
referenced and owned are not applicable. This might occur where
pointers are created by one class but consumed and deleted by another. These
may be addressed with an ad hoc analysis similar to the above. As the
situation becomes more complex this becomes more difficult and error prone.
Eventually, it will be have to addressed by building heap management into the
pointer itself - that is into boost::shared_ptr.
The library includes serialization of boost::shared_ptr. As
previously mentioned, this required a tiny alteration in one of the
boost::shared_ptr implementation files in order to permit
access by the serialization system.

Serialization
Archive Exceptions
Archive operators can throw a unregistered_class
invalid_signature
unsupported_version
unsupported_class_version
pointer_conflict
incompatible_native_format
array_size_too_short
input_stream_error
output_stream_error
invalid_class_name
unregistered_class
multiple_code_instantiation
xml_archive_parsing_error
xml_archive_tag_mismatch
xml_archive_tag_name_error
boost::archive_exception
object which can be caught by an application program. These exceptions are defined
in the files
archive_exception.hpp
and
basic_xml_archive.hpp.
namespace boost {
namespace archive {
class archive_exception : public std::exception
{
public:
typedef enum {
unregistered_class, // attempt to serialize a pointer of
// an unregistered class
invalid_signature, // first line of archive does not contain
// expected string
unsupported_version, // archive created with library version subsequent
// to this one
pointer_conflict // an attempt has been made to directly serialize
// an object after having already serialized the same
// object through a pointer. Were this permitted,
// the archive load would result in the creation
// of an extraneous object.
incompatible_native_format, // attempt to read native binary format
// on incompatible platform
array_size_too_short, // array being loaded doesn't fit in array allocated
input_stream_error // error on stream input
invalid_class_name, // class name greater than the maximum permitted.
// most likely a corrupted archive or an attempt
// to insert virus via buffer overrun method.
unregistered_cast, // base - derived relationship not registered with
// void_cast_register
unsupported_class_version, // type saved with a version # greater than the
// one used by the program. This indicates that the program
// needs to be rebuilt.
multiple_code_instantiation, // code for implementing serialization for some
// type has been instantiated in more than one module.
output_stream_error // error on stream output
} exception_code;
exception_code code;
archive_exception(exception_code c) : code(c) {}
virtual const char *what( ) const throw();
};
class xml_archive_exception : public virtual archive_exception
{
public:
typedef enum {
xml_archive_parsing_error, // archive doesn't contain expected data
xml_archive_tag_mismatch, // start/end tag in archive doesn't match program
xml_archive_tag_name_error // tag name contains invalid characters
} exception_code;
xml_archive_exception(exception_code c){}
virtual const char *what( ) const throw();
};
} // archive
} // boost
An attempt has been made to serialize a polymorphic class through a pointer
without either registering it or associating it with an export key. This can also occur
when using a new archive whose class name has not been added to the system with the
unregistered_classBOOST_ARCHIVE_CUSTOM_ARCHIVE_TYPES macro.
Archives are initiated with a known string. If this string is not found when
the archive is opened, It is presumed that this file is not a valid archive and this
exception is thrown.
invalid_signature
This system records the current library version number to all archives created. Note that this is in
no way related to version number of classes used by application programs. This refers
to the version of the serialization system used to create the archive. Future versions
of this serialization system will be able to identify archives created under a previous
(i.e. this) system and alter the loading procedure accordingly. Hence, future enhancements
to this serialization system should not obsolete any existing archive files. It is only
necessary to increment this version number when the newer system creates archives
incompatible in format with the current one.
unsupported_version
An attempt has been made to load a class whose version has been incremented since the
program was written. Suppose that a class has been assigned version number 3 and the program
has been built and sent to third parties. Now suppose that the definition of that class
has been altered, the version number has been incremented to 4 and new archives have been
built. If one attempts to load these new archives with the original program, this
exception will be thrown.
unsupported_class_version
To understand what this exception means consider the following scenario
pointer_conflict
An object is saved first through a pointer then directly. Upon loading back
in the same sequence, we first create an new object and load in its data. Then
we load the data into another existing object. Where we started with one
object during save, we have two objects after restore. In a more realistic
situation, it could be very difficult to find this error. Fortunately,
these situations can be detected when the archive is created. When
this occurs, this exception is thrown.
template<class Archive>
void T::save(Archive &ar) const
{
const A * aptr = &a;
ar << aptr; // save an instance of object of class A through a pointer
...
ar << a; // save an instance of an object of class A
assert(aptr == &a); // this must be true
}
template<class Archive>
void T::load(Archive &ar)
{
A * aptr;
ar >> aptr; // create and initialize a new instance of class A
...
ar >> a; // restore state of on object of class A
assert(aptr == &a); // this won't be true
}
The library currently supports char text, wide char text and native binary
archive files. At the beginning of every archive, a signature is written indicating
the type of archive. This exception is thrown when an attempt is made to read
an archive written in a different format.
incompatible_native_format
An attempt has been made to read an array that is larger than the array size.
This should only occur when the size of an array in code is reduced after an
archive has already been created.
array_size_too_short
An error has occurred during stream input or ouput. Aside from the common
situations such as a corrupted or truncated input file, there are
several less obvious ones that sometimes occur.
input_stream_error
output_stream_error
use
std::stringstream ss;
std::vector<V> v;
boost::archive::text_oarchive oa(ss);
oa << v;
boost::archive::text_iarchive ia(ss);
ia >> v;
std::stringstream ss;
std::vector<V> v;
{
boost::archive::text_oarchive oa(ss);
oa << v;
}
{
boost::archive::text_iarchive ia(ss);
ia >> v;
}
Class name length greater than the maximum permitted. Most likely cause is a corrupted
archive or an attempt to insert a virus via the buffer overrun method.
invalid_class_name
In order to support casting between pointers of base and derived classes
at runtime, a collection of legitimate conversions is maintained by the system.
Normally this collection is maintained without any explicit action
on the part of the user of the library. However, there are special cases
where this might have to be done explicitly and could be overlooked. This
is described in Runtime Casting.
This exception is thrown if an attempt is made to convert between two pointers
whose relationship has not been registered,
unregistered_cast
This exception is thrown when it is detected that the serialization of the same type
has been instantiated more than once. This might occur when
serialization code is instantiated in both the mainline and one or more DLLS.
multiple_code_instantiation
The XML generated by the serialization process is intimately coupled to the
C++ class structure, relationships between objects and the serialization
specifications. If these become out of sync in any way, the XML may not map
to the loading serialization and this exception might be thrown. This might
occur for one of the following reasons:
xml_archive_parsing_error
This exception will be thrown if the start or end tag of an XML element doesn't match
the name specified for the object in the program.
xml_archive_tag_mismatch
This exception will be thrown if the tag name contains invalid characters. Valid characters
for an XML tag are: upper and lower case letters, digits, and the following punctuation: .(period),
_(underscore), :(colon), and -(hyphen).
xml_archive_tag_name_error

Serialization
extended_type_info
Motivation
The serialization library needs a system like
type_info/typeid() to perform
the following functions
The problem with
std::type_info
std::typeid()
is not available in all environments. Support for this function depends upon
runtime typing(RTTI) support from the compiler. This may be non-existent
or not enabled for reasons such as a perceived inefficiency.
std::type_info includes a string
containing type name. This would seem to satisfy 2) above.
But the format of this string is not consistent accross compilers, libraries,
and operating systems. This makes it unusable for support of portable archives.
Features
extended_type_info is an implementation
of std::type_info functionality with the
following features:
Exported types are maintained in a global table so that given a string key, the
corresponding type can be found. This facility is used by the serialization library
in order to construct types serialized through a base class pointer.
extended_type_info records - one for each type
serialized.
typeid() to find the external identifier
of a class while another might not.
Runtime Interface
namespace boost {
namespace serialization {
class extended_type_info
{
protected:
// this class can't be used as is. It's just the
// common functionality for all type_info replacement
// systems. Hence, make these protected
extended_type_info(
const unsigned int type_info_key,
const char * key
);
~extended_type_info();
void key_register();
void key_unregister();
public:
const char * get_key() const;
bool operator<(const extended_type_info &rhs) const;
bool operator==(const extended_type_info &rhs) const;
bool operator!=(const extended_type_info &rhs) const {
return !(operator==(rhs));
}
// for plugins
virtual void * construct(unsigned int count = 0, ...) const;
virtual void destroy(void const * const p) const;
static const extended_type_info * find(const char *key);
};
} // namespace serialization
} // namespace boost
extended_type_info
instance created for each type. However, this is enforced only at the executable
module level. That is, if a program includes some shared libraries or DLLS,
there may be more than one instance of this class corresponding to a particular type.
For this reason the comparison functions below can't just compare the addresses of
this instance but rather must be programmed to compare the actual information
the instances contain.
extended_type_info(unsigned int type_info_key, const char *key);
void key_register();
void key_unregister();
extended_type_info records.
This table is used when loading pointers to objects serialized
through a base class pointer. In this case, the archive
contains a string which is looked up in this table to
determine which extended_type_info
to use for creating a new object.
extended_type_info
to add and remove entries from this table.
const char *get_key() const;
extended_type_info
instance. If no key has been associated with the instance, then a NULL is returned.
bool operator<(const extended_type_info & rhs) const;
bool operator==(const extended_type_info & rhs) const;
bool operator!=(const extended_type_info & rhs) const;
extended_type_info
objects. They impose a strict total ordering on all
extended_type_info records.
virtual void * construct(unsigned int count = 0, ...) const;
extended_type_info
record corresponds. This function takes a variable list of up to 4 arguments
of any type. These arguments are passed to the type's constructor
at runtime. In order to use the facility,
one must declare a type sequence for the constructor arguments.
Arguments for this function must match in number and type
with those specified when the type was exported.
This function permits one to create instances of
any exported type given only the exported GUID assigned
with BOOST_CLASS_EXPORT.
If these types are defined in DLLS or shared libraries loaded at runtime,
these constructors can be called until the module is unloaded.
Such modules are referred to as plugins.
virtual void destroy(void const * const p) const;
static const extended_type_info * find(const char *key);
extended_type_info
object.
Requirements for an Implementation
In order to be used by the serialization library, an implementation of
extended_type_info,
(referred to as ETI here), must be derived from
extended_type_info
and also implement the following:
template<class ETI>
const extended_type_info *
ETI::get_derived_extended_type_info(const T & t) const;
extended_type_info
instance that corresponds to
the "true type" of the type T. The "true type" is the lowest type in the
hierarchy of classes. The type T can always be cast to the "true type" with
a static cast. Implementation of this function will vary among type id systems
and sometimes will make presumptions about the type T than can be identified
with a particular extended_type_info implementation.
virtual bool ETI::is_less_than(const extended_type_info &rhs) const;
extended_type_info implementation.
virtual bool ETI::is_equal(const extended_type_info &rhs) const;
extended_type_info implementation.
Return true if the types referred
to are the same. Otherwise return
false
const char ETI::get_key() const;
virtual void * construct(unsigned int count, ...) const;
virtual void * destroy(void const * const ptr ) const;
Models
The serialization library includes two distinct
extended_type_info
implementations.
is implemented in terms of the standard typeid(). It presumes that RTTI support is enabled
by the compiler.
extended_type_info_typeid
is implemented in a way that doesn't rely on the existence RTTI.
Instead, it requires that all polymorphic types be explicitly exported.
In addition, if the export facility is to be used to serialize types
through base class pointers, those types are required to implement
a virtual function with the signature:
extended_type_info_no_rtti
which returns a unique string the most derived object this class.
This function must be virtual in order to implement the functionality required by
virtual const char * get_key();
ETI::get_derived_extended_type_info
as described above.
Example
The test program test_no_rtti
implements this function in terms of the
extended_type_info API above to return the export key associated with the class.
This requires that non-abstract types be exported. It also demonstrates the
inter-operability between two different implementations of
extended_type_info.
Requirements for Each Type
Each type to be managed by the system must be
"registered" individually. This is accomplished by instantiating
templates. For example, if the type T is to use the type_info system
one would include the following code:
For those using the serialization library, this step can be skipped
as it is done automatically. The serialization library includes
the macro:
namespace boost {
namespace serialization {
template
struct extended_type_info_typeid<T>;
template
struct extended_type_info_typeid<const T>;
} // serialization
} // boost
which is used to specify which
BOOST_CLASS_TYPE_INFO(
my_type,
extended_type_info_typeid<my_class>
)
extended_type_info system is to
be used for a given type.
extended_type_info includes a facility for constructing
instances of types without knowing what the exact types are. This is done
with the function
virtual void * extended_type_info::construct(unsigned int count = 0, ...) const;
. For example:
struct base {
...
};
struct derived : public base {
...
};
...
extended_type_info *eti = extended_type_info::find("my_class")
base * b = eti->construct(...);
The construct takes an argument count and up to
four parameters of any type. The arguments are passed to the
constructor of "my_class".
A complete example of this can be found
here

Serialization
Tips and Tricks
This section will be used to list answers to questions raised in the mailing
lists. Most of these are due to subtle aspects of the library which are
overlooked even though they might be described in the documentation. Often,
these issues are very easy to address - but can be excruciatingly difficult to
find. Should you have such an experience, feel free to vent your frustration
in a constructive way by adding in your own item. The best way to do this
is to create a "TRAK" item
which includes the text you want to add to this list.

Serialization
Code Structure
This library includes a large number of files. They are organized and classified
according to the purposes listed in the above index.
namespace of classes and templates is synchronized
with the directory in which the file is found. For example, the class declaration
is included with the following declaration
boost::archive::text_oarchive
#include <boost/archive/text_oarchive.hpp>
Files Included by User Programs
Using this library entails including headers listed in this section.
It should not be necessary to explicitly include any other header files.
Archive Implementations
These header files contain declarations used to save and restore data to each type
of archive. Include the archives according to the facilities the code module requires.
Serialization Declarations
To specify how a type is serialized, one codes templates for serialization functions.
In the simplest cases, this does not require the inclusion of any header files for this purpose.
In most cases one or more of the following header files will have to be included in order
to complete or refine the description of the serializaition implementation for a given class.
This group will be required less frequently. The are used to override aspects of
the default implementation of the serialization process for specified types.
Serialization Implementations
This group of headers includes templates which implement serialization for Standard
Library or Boost Library templates. Any program which uses these templates can
invoke serialization of objects of these types just by including the corresponding header.
includes the code to implement serialization of the STL
#include <boost/serialization/list.hpp>
std::list type. While
includes code to implement serialization of the BOOST
#include <boost/serialization/shared_ptr.hpp>
boost::shared_ptr type.
Note that including the serialization header for a type automatically includes the
appropriate header of the type to be serialized.
As of this writing, the library includes templates of all STL library templates as well
as templates for boost::optional,
boost::shared_ptr, and
boost::scoped_ptr.
Presumably, this list will expand with the passage of time.
Files Which Implement the Library
Archive Development
These header files contain declarations for basic types used to create
concrete archive types that are made available to users above. Users wishing
to make their own type of archive may want to examine these headers to
see how the archives included with the libary have been constructed.
class_id_type and others to
record information in archives that is required to reconstruct the original
data structure. These are handled exactly as any other serializable type.
That is, they can be handled as simple primitives such as they are in simple
text files, or with special code as they are in xml archives.
basic_xml_oarchive.hpp
includes code to guarantee that any object not attached to a name will
trap during compile time. On the other hand, basic_text_oarchive.hpp
contains code to strip out and ignore any names attached to objects.
Archive Internals
The interface (see Archive Concepts)
and implementation are factored out into separate classes to minimize code duplication.
These files are found in the directory
boost/archive/detail.
These are included as necessary by the archive class implementations listed above.
This has the unfortunate side effect of making the implementation less transparent.
Users should never find it necessary to change these files.
save_override in the most derived
archive class.
save_override is declared and implemented in each class in
the archive hierarchy.
Note the usage of
Partial Function Template Ordering
to permit the correct save implementation to be selected.
template<class T>
void save_override(T & t, BOOST_PFTO int){
// All for otherwise unhandled types are forwarded to the base class.
// This emulates behavior for function overloading.
this->base::save_override(t, 0);
}
void save_override(const some_type & t, int){
// any special handling for some type
// this will usually entail forwarding some other operation
// in the most derived class.
this->This()->...
// or in one of its parents basic_text_oprimitive
this->This()->save(static_cast<int>(t));
}
... // other special type handling
Archive Library Code Modules
Parts of the library are implemented as library code. All of this code is to be found in
libs/serialization/src.
in the form of *.cpp. The directory
boost/archive/impl
contains *.ipp files which implement templates. These templates are instantiated
only by archive implementation so are generally not included in user code modules.
An example of this is the usage of the spirit library in the library.
It takes a long time to compile and includes lots of other files. Having this
only in the library is much more convenient that having to include it in every
program which uses xml serialization.
Dataflow Iterators
In the course of developing this library, it became convenient to make a set
of composable iterator adaptors for handling archive text. Applications include
escaping and unescaping xml text and implementing to/from base64 conversion among
others.

Serialization
History
base64 character encoding.

Serialization
Implementation Notes
Character Encoding
The whole question of character encoding combined with wide characters
is much more complicated than it would seem to be. The current library
defines in 3 formats (text, binary, and XML), wide and narrow characters,
and attempts to be portable between compiler libraries. The results of
a rather long consideration of all these factors has been to set
default encoding according to the following rules.
This character encoding is implemented by changing the text_?archive) will produce
text output in the current stream locale. Generally this will
produce no changes in string data.
ios::binary.
Failure to do so will result in 0x0d characters (carriage-return)
characters being removed from the input stream if they are followed
by a 0x0a character (line-feed). This could corrupt the input
and make the file unreadable. On UNIX systems the ios::binary
is not required and is ignored if used.
locale.
locale of the
i/o stream used by an archive when the archive is constructed, the stream
locale is changed back to its original value. This action can be overridden
by specifying boost::archive::no_codecvt
when the archive is opened. In this case, the stream locale will
not be changed by the serialization library.
std::string data stored in archives.
Suppose a normal (multi-byte) character string
is written to a wide character stream. Our system uses the current locale
to translate it to a wide character string before writing it out.
Upon reading, it is translated back to a (multi-byte)string.
If the locale on the platform that reads the archive is different than
the locale on the platform that wrote the stream, the actual string data
may be altered by the serialization process. To avoid this, either
avoid usage of locale dependent multi-byte strings or be sure that
the locale is set correctly before reading the archive.
Naturally, the input process has to be symmetrical.
locale to use
boost::archive::codecvt_null<OStream::char_type>
no_codecvt.
Specific Compiler/Library Issues
GCC 4.X
-Wno-non-virtual-dtor
-Wno-ctor-dtor-privacy
Intel C++ 8.0
No known issues. All tests compile and run in debug and release modes.
Visual C++ 8.0
This compiler emits warnings for calls to functions from the standard
library which are deemed security risks. The serialization depends upon
making some of these calls so programs which use the serialization library
will get warning messages. These messages can be suppressed from the command
line by including the following switch:
/wd4996
Visual C++ 7.1
Derivation from an archive class defined in a DLL as described in ... will not work.
This is due to the way that VC++ handles templated code with __decl(dllexport) and
__decl(dllimport) specifications. Basically, this compiler requires that all the
instantiations have the same specification - even though they have different
template arguments. The example
demo_portable_iarchive.cpp would have to be reformulated as a library or dll
similar to the pre-defined archives in order to function.
wchar_t as either
a short integer or an intrinsic type.
If /Zc:wchar_t is specified on the
compile command line, wchar_t will be
considered an intrinsic type - otherwise
it will be treated as a synonym for a 16 bit integer. The library can be used
either way - BUT - both the library AND the application
must be compiled with the same switch settings. Note that BJAM
includes this switch by default. So if want to use the libraries that
BJAM builds, you should include this switch
when you compile your own applications.
Using the Visual C++ IDE
The library includes a VC++ 7.1 "Solution" - BoostSerializationLibrary
along with a set of project files - one for each demo and test. Consider the following if you
decide to use these configurations.
bin subdirectory
within one's main boost directory. Below this there is a whole structure which maintains
object and library files according to the type of build. The easiest way to build this is to
invoke the runtest script which uses bjam (see below). If the libraries are not in these locations,
the projects will have to be modified accordingly.
BOOST_ALL_DYN_LINK=1.
Note that for the executables to run, the PATH
environmental variable will have to include the directories that contain the DLL versions of
the boost libraries.
This will build the serialization library and run the tests on your system. If there are more than a
a couple of test failures, you likely won't be able to get your own projects working. If most of the
tests pass, you can be confident that your own projects will work once you get your project settings
in sync with those included here.
Comeau 4.3.3
Code Warrior 9.x
Code Warrior 8.3
all the above issues for Code Warrior 9.x plus:
TRU64
All tests and demos pass except for test_variant. Boost Variant doesn't function
wih this compiler
Dinkumware Library
Several compilers, including Visual C++ 6.0, use an older dinkumware library.
These platforms have several issues:
imbue function is called before the
stream is opened. In order to use this library with this environment to generate UTF-8
files, one cannot depend on the "automatic" setting of locale that archives implement. The
stream locale must be set explicitly on the stream before an archive is opened on it. The
archive should be opened with the no_codecvt flag. Note this problem will
occur on all compilers shipped with this library.
STLPort 4.5.3

Serialization
Proposed Case Studies
These are not part of the library itself, but rather
techniques on how to use the library to address specific situations.
Serializing a Function Object
An example on how to serialize a function object. I believe this
could be done by serializing a pointer to the object in question. Since
the Serialization library resurrects a pointer of the correct type
this should be easily implementable.
Archive Adaptors
Often users want to add their own special functionality to an
existing archive. Examples of this are performance enhancements
for specific types, adjustment of output syntax for xml archives,
and logging/debug output as archives are written and/or read.
If this functionality is implemented as an "adaptor" template
which takes the base class as a template argument, such functionality could be
appended to any archive for which that functionality makes sense.
For example, an adaptor for generating an xml schema could be
appended to both wide and narrow character versions of xml archives.
Archive Helpers
Some types are not serializable as they stand. That is - they
do not fulfill the requirements of the "Serializable Concept".
The iconic example of this is boost::shared_ptr. Sometimes
these types could be made serializable by adding code inside
the library. Of course, doing that would create a lifetime
of unpaid employment for the library author. Rather than
adding a bunch of special code to the library itself, this
code can packaged as a "helper" or "mix-in" class. Then
a new archive is derived from both the "base" archive class
AND the "helper" class. This is how boost::shared_ptr
has been implemented.

Serialization
Overview
Our goals for such a system are:
Other implementations
Before getting started I searched around for current
implementations. I found several.
Its has lots of differences - and lots in common with this implementation.

Library Status: serialization
Run Date: 02:42:48 UTC, Tuesday 10 June 2008
================================================
FILE: doc/pimpl.html
================================================
Test Name
gcc-3.4.4
profile
peformance_array_binary_archive Pass Profile peformance_array_text_archive Pass Profile peformance_array_text_warchive Missing peformance_array_xml_archive Pass Profile peformance_array_xml_warchive Missing performance_iterators Pass Profile performance_iterators_base64 Pass Profile

Serialization
PIMPL
PIMPL is a C++ programming idiom described by Herb Sutter [10]
which stands for "Private Implementation". It is also referred to as
the "Handle Body Idiom". Included in this library is a program called
demo_pimpl.cpp
which illustrates how this is used. The file
demo_pimpl_A.hpp
contains the declaration of the A class that hides its implementation
by including a pointer to struct B that is only defined as a pointer.
Serialization of A requires access to the definition of B. But that doesn't mean
that it requires the this access from the header file. Since B is a pointer,
a declaration of class B is sufficient. The implemenation of the serialization
of A includes the definition of class B defined in the separately compiled module:
demo_pimpl_A.cpp
by:
// class whose declaration is hidden by a pointer
struct B;
struct A {
// class a contains a pointer to a "hidden" declaration
B *pimpl;
template<class Archive>
void serialize(Archive & ar, const unsigned int file_version);
A();
};
As described in [10] this brings the
following advantages:
#include "demo_pimpl_A.hpp"
// "hidden" definition of class B
struct B {
int b;
template<class Archive>
void serialize(Archive & ar, const unsigned int file_version){
ar & b;
}
};
A::A() :
pimpl(new B)
{}
A::~A(){
delete pimpl;
}
// now we can define the serialization for class A
template<class Archive>
void A::serialize(Archive & ar, const unsigned int file_version){
ar & pimpl;
}
So, we compile the modules and everything is fine. However when we
link, we get an error. Two symbols are undefined:
The problem is that when compiling the above code,
there is no instantiation of the
void A::serialize(boost::archive::text_oarchive & ar, const unsigned int file_version);
void A::serialize(boost::archive::text_iarchive & ar, const unsigned int file_version);
serialize template.
There can't be as it's not "known" what types of archives
the serialization is going to be used with. So these functions are "missing"
when an attempt to link is made. The solution is to explicitly instantiate
serialization code for those archives which are going to be used. In this
example, including the the following code in any *.cpp file does just that:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
template void A::serialize<boost::archive::text_iarchive>(
boost::archive::text_iarchive & ar,
const unsigned int file_version
);
template void A::serialize<boost::archive::text_oarchive>(
boost::archive::text_oarchive & ar,
const unsigned int file_version
);
The program should now link as well as compile.

Serialization
Private Base Classes
In many cases, serialization of private or protected base classes present no special problems.
This is true for both simple classes and types as well as pointers to those
classes and types. That is, the following program compiles and runs exactly as one would expect.
Difficulties start to occur when the base class is made polymorphic by the designation
of one or more functions as "virtual". If a class is polymorphic, the library
presumes that one will want the ability to serialize a derived class through
a pointer to the base class. Included in the macro
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// test_private_base.cpp
// (C) Copyright 2009 Eric Moyer - http://www.rrsd.com .
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <fstream>
#include <boost/config.hpp>
#if defined(BOOST_NO_STDC_NAMESPACE)
namespace std{
using ::remove;
}
#endif
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
class Base {
friend class boost::serialization::access;
int m_i;
template<class Archive>
void serialize(Archive & ar, const unsigned int version){
ar & BOOST_SERIALIZATION_NVP(m_i);
}
protected:
bool equals(const Base &rhs) const {
return m_i == rhs.m_i;
}
Base(int i = 0) :
m_i(i)
{}
};
class Derived : private Base {
friend class boost::serialization::access;
private:
Base & base_cast(){
return static_cast<Base &>(*this);
}
template<class Archive>
void serialize(Archive & ar, const unsigned int version){
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
}
public:
bool operator==(const Derived &rhs) const {
return Base::equals(static_cast<const Base &>(rhs));
}
Derived(int i = 0) :
Base(i)
{}
};
int
main( int /* argc */, char* /* argv */[] )
{
const char * testfile = boost::archive::tmpnam(NULL);
// serialize Derived and Base
Derived a(1), a1(2);
{
test_ostream os(testfile);
test_oarchive oa(os);
oa << boost::serialization::make_nvp("a", a);
}
{
test_istream is(testfile, TEST_STREAM_FLAGS);
test_iarchive ia(is, TEST_ARCHIVE_FLAGS);
ia >> boost::serialization::make_nvp("a", a1);
}
std::remove(testfile);
if(a != a1)
return 1;
// serialize Derived and Base
Derived *ta = &a;
Derived *ta1 = NULL;
{
test_ostream os(testfile);
test_oarchive oa(os);
oa << boost::serialization::make_nvp("ta", ta);
}
{
test_istream is(testfile, TEST_STREAM_FLAGS);
test_iarchive ia(is, TEST_ARCHIVE_FLAGS);
ia >> boost::serialization::make_nvp("ta", ta1);
}
std::remove(testfile);
if(*ta != *ta1)
return 1;
return 0;
}
BOOST_SERIALIZATION_BASE_OBJECT_NVP
is code which links derived and base class definitions in tables used to serialize
derived classes through pointers to a polymorphic base class. This code requires
the ability to invoke
static_cast<Base &>(Derived &)
which C++ will only permit from within the derived class if the base class is
private or protected. The program will fail to compile with an error message
indicating invalid cast.
With this change the program will now compile.
template<class Archive>
void serialize(Archive & ar, const unsigned int version){
//ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
ar & boost::serialization::make_nvp(
"Base",
static_cast<Base &>(*this)
);
}
Base virtual
in order to use the "export" functionality of the serialization library and permit serialization through
a pointer the the base class, we'll be disappointed. Without the ability to
cast to the base class, we can't use the functionality.

Serialization
Rationale
typeid information is not included in archivesThe term "serialization" is preferred to "persistence"
Archives are not streams
Archive Members are Templates
Rather than Virtual Functions
The previous version of this library defined virtual functions for all
primitive types. These were overridden by each archive class. There were
two issues related to this:
long long,
__int64,
etc.) resulted in messy and fragile code. Replacing this with templates
and letting the compiler generate the code for the primitive types actually
used, resolved this problem. Of course, the ripple effects of this design
change were significant, but in the end led to smaller, faster, more
maintainable code.
std::strings are treated specially in text files
std::string
and std::wstring.
Presumably they optimize appropriately.
std::basic_string are in fact handled
as vectors of the element type.
typeid information is not included in archives
I originally thought that I had to save the name of the class specified by std::type_of::name()
in the archive. This created difficulties as std::type_of::name() is not portable and
not guaranteed to return the class name. This makes it almost useless for implementing
archive portability. This topic is explained in much more detail in
[7] page 206. It turned out that it was not necessary.
As long as objects are loaded in the exact sequence as they were saved, the type
is available when loading. The only exception to this is the case of polymorphic
pointers never before loaded/saved. This is addressed with the register_type()
and/or export facilities described in the reference.
In effect, export generates a portable equivalent to
typeid information.
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/reference.html ================================================ |
|
SerializationSerializable Concept |
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/release.html ================================================|
|
SerializationRelease Notes |
This has been addressed by including a small utility in the example
directory named fix_six.cpp. This should be run with the command line
This will assign 7 to the library version number of the archive. This
fix will need to ba applied to native binary archives created with
boost versions 1.42 and 1.43.
fix_six <file name>
Changes have been made to archive classes included with the library. Users who have used these a guide to making their own archive classes will find that these will likely no longer compile. This can be remedied by making the following changes in the code which instantiates these archive classes.
Old Code:
...
#include <boost/archive/impl/archive_pointer_iserializer.ipp>
...
template class detail::archive_pointer_iserializer<naked_text_iarchive> ;
...
template class detail::archive_pointer_iserializer<text_iarchive> ;
should be replaced with this new code:
#include <boost/archive/impl/archive_serializer_map.ipp>
...
template class detail::archive_serializer_map<naked_text_iarchive> ;
...
template class detail::archive_serializer_map<text_iarchive> ;
new/delete
operators. This functionaly is not available on some compilers.
std::streambuf
interface. This should result in noticeably faster execution in many cases.
const correctness for save/load operators. Note that this
may produce compile time errors in code which compiled without problem in
earlier boost releases. In most cases the fix is trivial. In other cases, code
should be scrutinized to be sure that it doesn't use the serialization system
in a way which may introduce subtle bugs in to the program. A fuller
explanation of this issue can be found
here.
shared_ptr<T>.
This is compatible with public interface of shared_ptr<T>
so it should be more robust and not have to change in the future. The
implementation optionally includes code to load shared_ptr<T>
stored in archives created with boost 1.32. This code is stored in 'he header:
boost/serialization/shared_ptr_132.hpp. If your application needs to
load archives created with boost 1.32 libraries, include the above header
before each inclusion of boost/serialization/shared_ptr.hpp.
std::string and
std::wstring
contain characters such as '\0' and -1 (EOF) which cannot be rendered in text
and XML archives without an escape mechanism. Currently there is no such escape
mechanism implemented.
std::map is fixed in this version. Unfortunately, the fix breaks
serialization of std::map
for those compilers which do not support partial template specialization. Also,
types which contain pointers or tracked types might not work correctly.
Aside from the above, there are a number of issues related to specific platforms. These are listed in Specific Compiler/Library Issues.
© Copyright Robert Ramey 2002-2009. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/serialization.html ================================================|
|
SerializationSerializable Concept |
const Members
serialize into
save/load
T is Serializable
if and only if one of the following is true:
serialize
serialize
template<class Archive, class T>
inline void serialize(
Archive & ar,
T & t,
const unsigned int file_version
){
// invoke member function for class T
t.serialize(ar, file_version);
}
That is, the default definition of template serialize
presumes the existence of a class member function template of the following
signature:
template<class Archive>
void serialize(Archive &ar, const unsigned int version){
...
}
If such a member function is not declared, a compile time error will occur. In order
that the member function generated by this template can be called to
append the data to an archive, it either must be public or the class must
be made accessible to the serialization library by including:
friend class boost::serialization::access;
in the class definition. This latter method should be preferred over the option
of making the member function public. This will prevent serialization functions from
being called from outside the library. This is almost certainly an error. Unfortunately,
it may appear to function but fail in a way that is very difficult to find.
It may not be immediately obvious how this one template serves for both
saving data to an archive as well as loading data from the archive.
The key is that the & operator is
defined as <<
for output archives and as >> input archives. The
"polymorphic" behavior of the & permits the same template
to be used for both save and load operations. This is very convenient in that it
saves a lot of typing and guarantees that the saving and loading of class
data members are always in sync. This is the key to the whole serialization
system.
my_class, the
override would be specified as:
// namespace selection
template<class Archive>
inline void serialize(
Archive & ar,
my_class & t,
const unsigned int file_version
){
...
}
Note that we have called this override "non-intrusive". This is slightly
inaccurate. It does not require that the class have special functions, that
it be derived from some common base class or any other fundamental design changes.
However, it will require access to the class members that are to
be saved and loaded. If these members are private, it won't be
possible to serialize them. So in some instances, minor modifications to the
class to be serialized will be necessary even when using this "non-intrusive"
method. In practice this may not be such a problem as many libraries
(E.G. STL) expose enough information to permit implementation of non-intrusive
serialization with absolutely no changes to the library.
boost::serialization. If portability is not a concern and the
compiler being used supports ADL (Argument Dependent Lookup) the free functions and
templates can be in any of the following namespaces:
boost::serialization
Note that, at first glance, this suggestion may seem to be wrong for compilers which implement two phase lookup. In fact, the serialization library used a perhaps overly clever method to support this rule even for such compilers. Those with an interest in studying this further will find more information in serialization.hpp
operator & to all the data members of the class.
{
// save/load class member variables
ar & member1;
ar & member2;
}
template<class Base, class Derived>
Base & base_object(Derived &d);
which should be used to create a reference to an object of the base
which can be used as an argument to the archive serialization operators.
So for a class of Serializable type
T the base class state should be
serialized like this:
{
// invoke serialization of the base class
ar & boost::serialization::base_object<base_class_of_T>(*this);
// save/load class member variables
ar & member1;
ar & member2;
}
Resist the temptation to just cast *this to the base class.
This might seem to work but may fail to invoke code necessary for
proper serialization.
Note that this is NOT the same as calling the serialize
function of the base class. This might seem to work but will circumvent
certain code used for tracking of objects, and registering base-derived
relationships and other bookkeeping that is required for the serialization
system to function as designed. For this reason, all serialize
member functions should be private.
const Membersconst members to an archive
requires no special considerations.
Loading const members can be addressed by using a
const_cast:
ar & const_cast<T &>(t);
Note that this violates the spirit and intention of the const
keyword. const members are initialized when a class instance
is constructed and not changed thereafter. However, this may
be most appropriate in many cases. Ultimately, it comes down to
the question about what const means in the context
of serialization.
boost::shared_ptr<T> and for
std::list<T>. If I have defined serialization for my own
class my_t, then serialization for
std::list< boost::shared_ptr< my_t> > is already available
for use.
For an example that shows how this idea might be implemented for your own
class templates, see
demo_auto_ptr.cpp.
This shows how non-intrusive serialization
for the template auto_ptr from the standard library
can be implemented.
A somewhat trickier addition of serialization to a standard template can be found in the example shared_ptr.hpp
In the specification of serialization for templates, its common
to split serialize
into a load/save pair.
Note that the convenience macro described
above
isn't helpful in these cases as the number and kind of
template class arguments won't match those used when splitting
serialize for a simple class. Use the override
syntax instead.
{
// invoke serialization of the base class
ar & boost::serialization::base_object<base_class_of_T>(*this);
// save/load class member variables
ar & member1;
ar & member2;
// if its a recent version of the class
if(1 < file_version)
// save load recently added class members
ar & member3;
}
serialize into Save/Load
template<class Archive>
void save(Archive & ar, const unsigned int version) const
{
// invoke serialization of the base class
ar << boost::serialization::base_object<const base_class_of_T>(*this);
ar << member1;
ar << member2;
ar << member3;
}
template<class Archive>
void load(Archive & ar, const unsigned int version)
{
// invoke serialization of the base class
ar >> boost::serialization::base_object<base_class_of_T>(*this);
ar >> member1;
ar >> member2;
if(version > 0)
ar >> member3;
}
template<class Archive>
void serialize(
Archive & ar,
const unsigned int file_version
){
boost::serialization::split_member(ar, *this, file_version);
}
This splits the serialization into two separate functions save
and load. Since the new serialize template
is always the same it can be generated by invoking the macro
BOOST_SERIALIZATION_SPLIT_MEMBER() defined in the header file
boost/serialization/split_member.hpp
.
So the entire serialize function above can be replaced with:
BOOST_SERIALIZATION_SPLIT_MEMBER()
serialize function template.
To use save and
load function templates rather than
serialize:
namespace boost { namespace serialization {
template<class Archive>
void save(Archive & ar, const my_class & t, unsigned int version)
{
...
}
template<class Archive>
void load(Archive & ar, my_class & t, unsigned int version)
{
...
}
}}
include the header file
boost/serialization/split_free.hpp
.
and override the free serialize function template:
namespace boost { namespace serialization {
template<class Archive>
inline void serialize(
Archive & ar,
my_class & t,
const unsigned int file_version
){
split_free(ar, t, file_version);
}
}}
To shorten typing, the above template can be replaced with
the macro:
BOOST_SERIALIZATION_SPLIT_FREE(my_class)
Note that although the functionality to split the
serialize function into save/load
has been provided, the usage of the serialize
function with the corresponding & operator
is preferred. The key to the serialization implementation is that objects are saved
and loaded in exactly the same sequence. Using the &
operator and serialize
function guarantees that this is always the case and will minimize the
occurrence of hard to find errors related to synchronization of
save and load
functions.
Also note that BOOST_SERIALIZATION_SPLIT_FREE
must be used outside of any namespace.
To properly save and restore an object through a pointer the following situations must be addressed:
Saving a pointer:
<<
and >> operators
// load data required for construction and invoke constructor in place
template<class Archive, class T>
inline void load_construct_data(
Archive & ar, T * t, const unsigned int file_version
){
// default just uses the default constructor to initialize
// previously allocated memory.
::new(t)T();
}
The default load_construct_data invokes the
default constructor "in-place" to initialize the memory.
If there is no such default constructor, the function templates
load_construct_data and
perhaps save_construct_data
will have to be overridden. Here is a simple example:
class my_class {
private:
friend class boost::serialization::access;
const int m_attribute; // some immutable aspect of the instance
int m_state; // mutable state of this instance
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version){
ar & m_state;
}
public:
// no default construct guarantees that no invalid object
// ever exists
my_class(int attribute) :
m_attribute(attribute),
m_state(0)
{}
};
the overrides would be:
namespace boost { namespace serialization {
template<class Archive>
inline void save_construct_data(
Archive & ar, const my_class * t, const unsigned int file_version
){
// save data required to construct instance
ar << t->m_attribute;
}
template<class Archive>
inline void load_construct_data(
Archive & ar, my_class * t, const unsigned int file_version
){
// retrieve data from archive required to construct new instance
int attribute;
ar >> attribute;
// invoke inplace constructor to initialize instance of my_class
::new(t)my_class(attribute);
}
}} // namespace ...
In addition to the deserialization of pointers, these overrides are used
in the deserialization of STL containers whose element type has no default
constructor.
class base {
...
};
class derived_one : public base {
...
};
class derived_two : public base {
...
};
int main(){
...
base *b;
...
ar & b;
}
When saving b what kind of object should be saved?
When loading b what kind of object should be created?
Should it be an object of class derived_one,
derived_two, or maybe base?
It turns out that the kind of object serialized depends upon whether the base class
(base in this case) is polymorphic or not.
If base is not polymorphic, that is if it has no
virtual functions, then an object of the type base
will be serialized. Information in any derived classes will be lost. If this is what is desired
(it usually isn't) then no other effort is required.
If the base class is polymorphic, an object of the most derived type
(derived_one
or derived_two
in this case) will be serialized. The question of which type of object is to be
serialized is (almost) automatically handled by the library.
The system "registers" each class in an archive the first time an object of that class it is serialized and assigns a sequential number to it. Next time an object of that class is serialized in that same archive, this number is written in the archive. So every class is identified uniquely within the archive. When the archive is read back in, each new sequence number is re-associated with the class being read. Note that this implies that "registration" has to occur during both save and load so that the class-integer table built on load is identical to the class-integer table built on save. In fact, the key to whole serialization system is that things are always saved and loaded in the same sequence. This includes "registration".
Expanding our previous example:
int main(){
derived_one d1;
derived_two d2:
...
ar & d1;
ar & d2;
// A side effect of serialization of objects d1 and d2 is that
// the classes derived_one and derived_two become known to the archive.
// So subsequent serialization of those classes by base pointer works
// without any special considerations.
base *b;
...
ar & b;
}
When b is read it is
preceded by a unique (to the archive) class identifier which
has previously been related to class derived_one or
derived_two.
If a derived class has NOT been automatically "registered" as described
above, an
unregistered_class exception
will be thrown when serialization is invoked.
This can be addressed by registering the derived class explicitly. All archives are derived from a base class which implements the following template:
template<class T>
register_type(T * = NULL);
So our problem could just as well be addressed by writing:
int main(){
...
ar.template register_type<derived_one>();
ar.template register_type<derived_two>();
base *b;
...
ar & b;
}
Note that if the serialization function is split between save and load, both
functions must include the registration. This is required to keep the save
and corresponding load in synchronization.
So we have another method:
#include <boost/serialization/export.hpp>
...
BOOST_CLASS_EXPORT_GUID(derived_one, "derived_one")
BOOST_CLASS_EXPORT_GUID(derived_two, "derived_two")
int main(){
...
base *b;
...
ar & b;
}
The macro BOOST_CLASS_EXPORT_GUID associates a string literal
with a class. In the above example we've used a string rendering
of the class name. If a object of such an "exported" class is serialized
through a pointer and is otherwise unregistered, the "export" string is
included in the archive. When the archive
is later read, the string literal is used to find the class which
should be created by the serialization library.
This permits each class to be in a separate header file along with its
string identifier. There is no need to maintain a separate "pre-registration"
of derived classes that might be serialized. This method of
registration is referred to as "key export". More information on this
topic is found in the section Class Traits -
Export Key.
template<class Archive, class T>.
This means that serialization code must be instantiated for each
combination of archive and data type that is serialized in the program.
Polymorphic pointers of derived classes may never be referred to
explicitly by the program so normally code to serialize such classes
would never be instantiated. So in addition to including export key
strings in an archive, BOOST_CLASS_EXPORT_GUID explicitly
instantiates the class serialization code for all archive classes used
by the program.
track_selectively.
That is, track objects if and only if they are serialized through pointers anywhere
in the program. Any objects that are "registered" by any of the above means are presumed
to be serialized through pointers somewhere in the program and therefore
would be tracked. In certain situations this could lead to an inefficiency.
Suppose we have a class module used by multiple programs. Because
some programs serializes polymorphic pointers to objects of this class, we
export a class
identifier by specifying BOOST_CLASS_EXPORT in the
class header. When this module is included by another program,
objects of this class will always be tracked even though it
may not be necessary. This situation could be addressed by using
track_never
in those programs.
It could also occur that even though a program serializes through
a pointer, we are more concerned with efficiency than avoiding the
the possibility of creating duplicate objects. It could be
that we happen to know that there will be no duplicates. It could
also be that the creation of a few duplicates is benign and not
worth avoiding given the runtime cost of tracking duplicates.
Again, track_never
can be used.
boost::serialization::base_object<Base>(Derived &)
is to ensure that the base/derived pair is added to the table
before the main function is entered.
This is very convenient and results in a clean syntax. The only
problem is that it can occur where a derived class serialized
through a pointer has no need to invoke the serialization of
its base class. In such a case, there are two choices. The obvious
one is to invoke the base class serialization with base_object
and specify an empty function for the base class serialization.
The alternative is to "register" the Base/Derived relationship
explicitly by invoking the template
void_cast_register<Derived, Base>();.
Note that this usage of the term "register" is not related
to its usage in the previous section. Here is an example of how this is done:
#include <sstream>
#include <boost/serialization/serialization.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
class base {
friend class boost::serialization::access;
//...
// only required when using method 1 below
// no real serialization required - specify a vestigial one
template<class Archive>
void serialize(Archive & ar, const unsigned int file_version){}
};
class derived : public base {
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int file_version){
// method 1 : invoke base class serialization
ar & boost::serialization::base_object<base>(*this);
// method 2 : explicitly register base/derived relationship
boost::serialization::void_cast_register<derived, base>(
static_cast<derived *>(NULL),
static_cast<base *>(NULL)
)
}
};
BOOST_CLASS_EXPORT_GUID(derived, "derived")
int main(){
//...
std::stringstream ss;
boost::archive::text_iarchive ar(ss);
base *b;
ar >> b;
}
In order for this template to be invoked in code compiled by non-conforming compilers, the following syntax may be used:
boost::serialization::void_cast_register(
static_cast<Derived *>(NULL),
static_cast<Base *>(NULL)
);
For more information, see Template Invocation syntax
class object;
class my_class {
private:
friend class boost::serialization::access;
int member1;
object & member2;
template<class Archive>
void serialize(Archive &ar, const unsigned int file_version);
public:
my_class(int m, object & o) :
member1(m),
member2(o)
{}
};
the overrides would be:
namespace boost { namespace serialization {
template<class Archive>
inline void save_construct_data(
Archive & ar, const my_class * t, const unsigned int file_version
){
// save data required to construct instance
ar << t.member1;
// serialize reference to object as a pointer
ar << & t.member2;
}
template<class Archive>
inline void load_construct_data(
Archive & ar, my_class * t, const unsigned int file_version
){
// retrieve data from archive required to construct new instance
int m;
ar >> m;
// create and load data through pointer to object
// tracking handles issues of duplicates.
object * optr;
ar >> optr;
// invoke inplace constructor to initialize instance of my_class
::new(t)my_class(m, *optr);
}
}} // namespace ...
T is a serializable type,
then any native C++ array of type T is a serializable type.
That is, if T
is a serializable type, then the following
is automatically available and will function as expected:
T t[4];
ar << t;
...
ar >> t;
std::list, use:
#include <boost/serialization/list.hpp>
rather than
#include <list>
Since the former includes the latter, this is all that is necessary.
The same holds true for all STL collections as well as templates
required to support them (e.g. std::pair).
As of this writing, the library contains serialization of the following boost classes:
std::variant is supported as well.
Others are being added to the list so check the boost files section and headers for
new implementations!
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/shared_ptr.html ================================================|
|
SerializationTemplate serialization -
|
boost::serialization namespace.
shared_ptr<T> is defined in
shared_ptr.hpp.
The general class outline for a shared_ptr<T> is:
shared_ptr<T> contains:
T *px;
shared_count pn; which contains a pointer to:
sp_counted_base_impl<T, ...> which is
derived from the polymorphic abstract class
sp_counted_base
The first cut at implementing serialization for shared_ptr
just serializes the relevant members of shared_ptr.
It's almost trivial:
template<class Archive, class T>
inline void serialize(
Archive & ar,
shared_ptr<T> & t,
const unsigned int file_version,
int
){
ar & t.px; // save the raw pointer
ar & t.pn; // save the shared reference count
}
So far so good. Now for the serialization of shared_count:
template<class Archive>
inline void save(
Archive & ar,
const boost::detail::shared_count & t,
const unsigned int file_version
){
ar << t.pi_;
}
template<class Archive>
inline void load(
Archive & ar,
boost::detail::shared_count & t,
const unsigned int file_version
){
ar >> t.pi_;
}
A key feature of this library is the ability to specify serialization
of a class or template without changing the class or template declaration
or definition. This is referred to as non-intrusive serialization.
The pi_member of shared count is a pointer to an
instance of sp_counted_base_impl. Since this class
doesn't have a default constructor, serialization requires
specification of the following overload:
template<class Archive, class P, class D>
inline void save_construct_data(
Archive & ar,
const boost::detail::sp_counted_base_impl<P, D> * t,
const unsigned int file_version
){
// variables used for construction
ar << t->ptr;
ar << *t;
}
template<class Archive, class P, class D>
inline void load_construct_data(
Archive & ar,
boost::detail::sp_counted_base_impl<P, D> * t,
const unsigned int file_version
){
P ptr_;
ar >> ptr_;
// placement new
::new(t)boost::detail::sp_counted_base_impl<P, D>(ptr_, D());
ar >> *t;
}
The statement ar >> ptr_ is key. This deserializes
the same pointer deserialized above. Default object tracking will ensure
that no more than one instance of the object is created and that the
pointer returned by multiple deserializations are all the same. Hence,
regardless of how many instances of shared_ptr/shared_count
corresponding to a particular object are created, they will all point
to the same object.
Since sp_counted_base_impl<P, D> is derived from
sp_counted_base, the following is needed:
template<class Archive, class P, class D>
inline void serialize(
Archive & ar,
boost::detail::sp_counted_base_impl<P, D> & t,
const unsigned int file_version,
int
){
ar & boost::serialization::base_object<
boost::detail::sp_counted_base
>(*this);
}
which will in turn require serialization of its base class:
inline void serialize(
Archive & ar,
boost::detail::sp_counted & t,
const unsigned int file_version,
int
){
}
It would seem we're done, but running the test program,
demo_shared_ptr.cpp
,
with this code produces the following output.
a = 0x003017A0 use count = 2
a1 = 0x003017A0 use count = 2
unique element count = 1
a = 0x00000000 use count = 0
a1 = 0x00000000 use count = 0
unique element count = 0
a = 0x00303060 use count = 1
a1 = 0x00303060 use count = 1
unique element count = 1
This indicates that we're not quite done. Due to default object
tracking, sp_counted_base_impl<P, D> is only
created once regardless of how many shared pointers point to the
same object. Of course, it has to be this way. The reference
count starts at 1 and is never incremented. Code must be added
to the serialization functions to maintain the proper reference
count.
The process of serialization of an empty base class -
sp_counted_base - seems like additional overhead.
Examination of code in
base_object.hpp
reveals that base_object.hpp provides two functions:
// register the relationship between each derived class
// and its polymorphic base
void_cast_register<
boost::detail::sp_counted_base_impl<P, D>
boost::detail::sp_counted_base,
>();
and we don't have to include a trivial serializer for sp_counted_base.
Finally we need to specify name-value pair wrappers if we want to be able to use this serialization with XML archives.
Actually, even this is really just a start. Among the issues not addressed in this implementation are:
weak_ptr is not addressed. I haven't even looked into this.
shared_ptr
haven't been addressed at all. To be confident that the implementation is
complete and correct, all these should be addressed as well.
BOOST_SHARED_POINTER_EXPORT(T)
BOOST_SHARED_POINTER_EXPORT_GUID(T, K)
These are specialized versions of the macros used for exporting classes serialized through raw pointers.
Clear, complete, correct and exception safe serialization of smart pointers is going to be a challenge. I hope that this implementation provides a useful starting point for such an effort.
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/shared_ptr2.html ================================================ |
|
Serialization
|
shared_ptr
illustrates the straightforward way of serializing a moderately complicated class structure.
Unfortunately, this way of doing it suffered from some undesirable features
shared_ptr.
The shared_ptr interface has been included
in std::tr1 and may someday be included in the standard
C++ library. An implementation which depends only on the public interface can be guaranteed to
function with any other future implementation of shared_ptr.
template<class Archive, class T>
inline void save(
Archive & ar,
const boost::shared_ptr<T> &t,
const unsigned int /* file_version */
){
const T * t_ptr = t.get();
// just serialize the underlying raw pointer
ar << boost::serialization::make_nvp("px", t_ptr);
}
template<class Archive, class T>
inline void load(
Archive & ar,
boost::shared_ptr<T> &t,
const unsigned int file_version
){
T* r;
// recover the underlying raw pointer
ar >> boost::serialization::make_nvp("px", r);
// To Do - match up with other shared pointers which
// use this same raw pointer.
...
}
In principle, this is very much simpler than the original implementation. Completion of
this code requires:
shared_ptr instances.
weak_ptr.
boost::serialization::shared_ptr.hpp
Note that if your code needs to read archives created under boost version 1.32, you will have to include the following
#include <boost/serialization/shared_ptr_132.hpp>
#include <boost/serialization/shared_ptr.hpp>
rather than just
#include <boost/serialization/shared_ptr.hpp>
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/simple_log.html ================================================|
|
SerializationA Simple Logging Archive Class |
simple_log_archive.hpp implements a simple but useful
archive class. This class can be used to send any serializable types
on an output text stream in a readable format. Usage of this facility
is trivially easy:
#include "simple_log_archive.hpp"
...
// display the complete schedule
simple_log_archive log(std::cout);
log << schedule;
and it produces the following output
schedule
count 6
item
first
driver bob
hour 6
minute 24
second ->
stops
count 3
item ->
latitude
degrees 34
minutes 135
seconds 52.56
longitude
degrees 134
minutes 22
seconds 78.3
...
The complete example is
demo_simple_log.cpp. Look at
Trivial Archive to get a
better understanding of how this works.
Also, note the following:
© Copyright Robert Ramey 2002-2010. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/singleton.html ================================================|
|
Serialization
|
main is called
regardless of where they might be referenced within the program.
In a multi-tasking system, this guarantees that there will be no
race conditions during the construction of any instance. No
thread locking is required to guarantee this.
const
instances are thread-safe during the whole program. Again, no
thread locking is required.
main is called.
For a more general purpose usage, thread locking on this
singleton could easily be implemented. But as the serialization
library didn't require it, it wasn't implemented.
namespace boost {
namespace serialization {
template <class T>
class singleton : public boost::noncopyable
{
public:
static const T & get_const_instance();
static T & get_mutable_instance();
static bool is_destroyed();
};
} // namespace serialization
} // namespace boost
static const T & get_const_instance();
static T & get_mutable_instance();
static bool is_destroyed();
true if the destructor on this singleton has been
called. Otherwise, return false.
singleton<T>
, the type T must be default constructible.
It doesn't require static variables - though it may have them.
Since the library guarantees that only one instance of
singleton<T>
exists and all access is through the above static interface
functions, common member functions of T become
the functional equivalent of
static functions.
The first way is illustrated by an excerpt from the file
extended_type_info.cpp.
which contains the following code:
typedef std::set<const extended_type_info *, key_compare> ktmap;
...
void
extended_type_info::key_register(const char *key) {
...
result = singleton<ktmap>::get_mutable_instance().insert(this);
...
}
Just by referring to the singleton instance anywhere in the program
will guarantee that one and only one instance for the specified
type (ktmap in this example)
will exist throughout the program. There is no need for any other
declaration or definition.
A second way is to use
singleton<T>
as one of the base classes of the type. This is illustrated by a simplified
excerpt from
extended_type_info_typeid.hpp
template<class T>
class extended_type_info_typeid :
public detail::extended_type_info_typeid_0,
public singleton<extended_type_info_typeid<const T> >
{
friend class singleton<extended_type_info_typeid<const T> >;
private:
// private constructor to inhibit any existence other than the
// static one. Note: not all compilers support this !!!
extended_type_info_typeid() :
detail::extended_type_info_typeid_0()
{
type_register(typeid(T));
}
~extended_type_info_typeid(){}
...
};
This usage will permit a more natural syntax to be used:
extended_type_info_typeid<T>::get_const_instance()
Again, including one or more of the above statements anywhere
in the program will guarantee that one and only one instance
is created and referred to.
Do not call get_mutable_instance when more than one thread is running! All singletons used in the serialization library follow this rule. In order to help detect accidental violations of this rule there exist singleton lock/unlock functions.
void boost::serialization::singleton_module::lock();
void boost::serialization::singleton_module::unlock();
bool boost::serialization::singleton_module::is_locked();
In a program compiled for debug, any invocation of
get_mutable_instance()
while the library is in a "locked" state will trap in an assertion.
The singleton module lock state is initialized as "unlocked" to permit
alteration of static variables before
main is called.
The lock() and
unlock() are "global"
in that they affect ALL the singletons defined by this template.
All serialization tests invoke lock()
at the start of the program. For programs compiled in release
mode these functions have no effect.
© Copyright Robert Ramey 2007. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/smart_cast.html ================================================|
|
Serialization
|
static_cast<T *>(U *)
static_cast<T &>(U &)
dynamic_cast<T *>(U *)
dynamic_cast<T &>(U &)
#include <boost/serialization/smart_cast.hpp>
struct top {
};
struct base1 : public top {
bool is_storable() const {
return true;
}
virtual ~base1();
};
struct base2 {
virtual ~base2();
};
struct derived1 :
public base1
{
derived1();
};
struct derived2 :
public base1,
public base2
{
derived2();
};
template<class T>
bool is_storable(T &t){
// what type of cast to use here?
// this fails at compile time when T == base2
// return static_cast<base1 &>(t).is_storable();
// this fails at compile time when T == top
// otherwise it works but cannot optimize inline function call
// return dynamic_cast<base1 &>(t).is_storable();
// this always works - and is guaranteed to generate the fastest code !
return (boost::smart_cast_reference<base1 &>(t)).is_storable();
}
int main(){
derived1 d1;
top & t1 = d1;
derived2 d2;
base2 & b2 = d2;
bool result;
result = is_storable(d1);
result = is_storable(d2);
result = is_storable(b2);
result = is_storable(b2);
result = is_storable(t1);
return 0;
}
The serialization library includes a mix of classes which use
both static polymorphism (CRTP) and dynamic
polymorphism via virtual functions. smart_cast
was written to address the more problematic manifestations of the
situation exemplified above.
smart_cast<Target *, Source *>(Source * s);
smart_cast<Target *>(Source * s);
smart_cast<Target &, Source &>(Source & s);
Note that the above syntax doesn't include
smart_cast<Target & >(Source & s)
but the same functionality is supported with the following special syntax
smart_cast_reference<Target &>(Source & s)
smart_cast can be used only on compilers that support partial
template specialization or on types for which the
macro
BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION(<type>)
has been applied.
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/special.html ================================================|
|
SerializationSpecial Considerations |
This could cause problems in programs where the copies of different objects are saved from the same address.
template<class Archive>
void save(boost::basic_oarchive & ar, const unsigned int version) const
{
for(int i = 0; i < 10; ++i){
A x = a[i];
ar << x;
}
}
In this case, the data to be saved exists on the stack. Each iteration
of the loop updates the value on the stack. So although the data changes
each iteration, the address of the data doesn't. If a[i] is an array of
objects being tracked by memory address, the library will skip storing
objects after the first as it will be assumed that objects at the same address
are really the same object.
To help detect such cases, output archive operators expect to be passed
const reference arguments.
Given this, the above code will invoke a compile time assertion. The obvious fix in this example is to use
template<class Archive>
void save(boost::basic_oarchive & ar, const unsigned int version) const
{
for(int i = 0; i < 10; ++i){
ar << a[i];
}
}
which will compile and run without problem.
The usage of const by the output archive operators
will ensure that the process of serialization doesn't
change the state of the objects being serialized. An attempt to do this
would constitute augmentation of the concept of saving of state with
some sort of non-obvious side effect. This would almost surely be a mistake
and a likely source of very subtle bugs.
Unfortunately, implementation issues currently prevent the detection of this kind of error when the data item is wrapped as a name-value pair.
A similar problem can occur when different objects are loaded to an address which is different from the final location:
template<class Archive>
void load(boost::basic_oarchive & ar, const unsigned int version) const
{
for(int i = 0; i < 10; ++i){
A x;
ar >> x;
std::m_set.insert(x);
}
}
In this case, the address of x is the one that is tracked rather than
the address of the new item added to the set. Left unaddressed
this will break the features that depend on tracking such as loading an object through a pointer.
Subtle bugs will be introduced into the program. This can be
addressed by altering the above code thusly:
template<class Archive>
void load(boost::basic_iarchive & ar, const unsigned int version) const
{
for(int i = 0; i < 10; ++i){
A x;
ar >> x;
std::pair<std::set::const_iterator, bool> result;
result = std::m_set.insert(x);
ar.reset_object_address(& (*result.first), &x);
}
}
This will adjust the tracking information to reflect the final resting place of
the moved variable and thereby rectify the above problem.
If it is known a priori that no pointer values are duplicated, overhead associated with object tracking can be eliminated by setting the object tracking class serialization trait appropriately.
By default, data types designated primitive by the
Implementation Level
class serialization trait are never tracked. If it is desired to
track a shared primitive object through a pointer (e.g. a
long used as a reference count), It should be wrapped
in a class/struct so that it is an identifiable type.
The alternative of changing the implementation level of a long
would affect all longs serialized in the whole
program - probably not what one would intend.
It is possible that we may want to track addresses even though
the object is never serialized through a pointer. For example,
a virtual base class need be saved/loaded only once. By setting
this serialization trait to track_always, we can suppress
redundant save/load operations.
BOOST_CLASS_TRACKING(my_virtual_base_class, boost::serialization::track_always)
To implement this facility, one declares a helper object associated to the current archive that can be used to store contextual information relevant to the particular type serialization algorithm.
template<class T>
class shared_ptr
{
...
};
BOOST_SERIALIZATION_SPLIT_FREE(shared_ptr)
class shared_ptr_serialization_helper
{
// table of previously loaded shared_ptr
// lookup a shared_ptr from the object address
shared_ptr<T> lookup(const T *);
// insert a new shared_ptr
void insert<shared_ptr<T> >(const shared_ptr<T> *);
};
namespace boost {
namespace serialization {
template<class Archive>
void save(Archive & ar, const shared_ptr & x, const unsigned int /* version */)
{
// save shared ptr
...
}
template<class Archive>
void load(Archive & ar, shared_ptr & x, const unsigned int /* version */)
{
// get a unique identifier. Using a constant means that all shared pointers
// are held in the same set. Thus we detect handle multiple pointers to the
// same value instances in the archive.
const void * shared_ptr_helper_id = 0;
shared_ptr_serialization_helper & hlp =
ar.template get_helper<shared_ptr_serialization_helper>(helper_instance_id);
// load shared pointer object
...
shared_ptr_serialization_helper & hlp =
ar.template get_helper<shared_ptr_serialization_helper>(shared_ptr_helper_id);
// look up object in helper object
T * shared_object hlp.lookup(...);
// if found, return the one from the table
// load the shared_ptr data
shared_ptr<T> sp = ...
// and add it to the table
hlp.insert(sp);
// implement shared_ptr_serialization_helper load algorithm with the aid of hlp
}
} // namespace serialization
} // namespace boost
get_helper<shared_ptr_serialization_helper>();
creates a helper object associated to the archive the first time it is invoked;
subsequent invocations return a reference to the object created in the first
place, so that hlp can effectively be
used to store contextual information persisting through the serialization
of different complex_type objects on
the same archive.
Helpers may be created for saving and loading archives. The same program might have several different helpers or the same helper instantiated separately from different parts of the program. This is what makes the helper_instance_id necessary. In principle it could be any unique integer. In practice it seems easiest to use the address of the serialization function which contains it. The above example uses this technique.
boost::serialization::object_serializable.
Turning off tracking and class information serialization will result in pure template inline code that in principle could be optimised down to a simple stream write/read. Elimination of all serialization overhead in this manner comes at a cost. Once archives are released to users, the class serialization traits cannot be changed without invalidating the old archives. Including the class information in the archive assures us that they will be readable in the future even if the class definition is revised. A light weight structure such as a display pixel might be declared in a header like this:
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/level.hpp>
#include <boost/serialization/tracking.hpp>
// a pixel is a light weight struct which is used in great numbers.
struct pixel
{
unsigned char red, green, blue;
template<class Archive>
void serialize(Archive & ar, const unsigned int /* version */){
ar << red << green << blue;
}
};
// elminate serialization overhead at the cost of
// never being able to increase the version.
BOOST_CLASS_IMPLEMENTATION(pixel, boost::serialization::object_serializable);
// eliminate object tracking (even if serialized through a pointer)
// at the risk of a programming error creating duplicate objects.
BOOST_CLASS_TRACKING(pixel, boost::serialization::track_never)
wchar_t while other compilers reserve only 2 bytes.
So it's possible that a value could be written that couldn't be represented by the loading program. This is a
fairly obvious situation and easily handled by using the numeric types in
<boost/cstdint.hpp>
A special integral type is std::size_t which is a typedef
of an integral types guaranteed to be large enough
to hold the size of any collection, but its actual size can differ depending
on the platform. The
collection_size_type
wrapper exists to enable a portable serialization of collection sizes by an archive.
Recommended choices for a portable serialization of collection sizes are to
use either 64-bit or variable length integer representation.
template<class T>
struct my_wrapper {
template<class Archive>
Archive & serialize ...
};
...
class my_class {
wchar_t a;
short unsigned b;
template<class Archive>
Archive & serialize(Archive & ar, unsigned int version){
ar & my_wrapper(a);
ar & my_wrapper(b);
}
};
If my_wrapper uses default serialization
traits there could be a problem. With the default traits, each time a new type is
added to the archive, bookkeeping information is added. So in this example, the
archive would include such bookkeeping information for
my_wrapper<wchar_t> and for
my_wrapper<short_unsigned>.
Or would it? What about compilers that treat
wchar_t as a
synonym for unsigned short?
In this case there is only one distinct type - not two. If archives are passed between
programs with compilers that differ in their treatment
of wchar_t the load operation will fail
in a catastrophic way.
One remedy for this is to assign serialization traits to the template
my_template such that class
information for instantiations of this template is never serialized. This
process is described above and
has been used for Name-Value Pairs.
Wrappers would typically be assigned such traits.
Another way to avoid this problem is to assign serialization traits
to all specializations of the template my_wrapper
for all primitive types so that class information is never saved. This is what has
been done for our implementation of serializations for STL collections.
ios::binary. If this is not done, the archive generated
will be unreadable.
Unfortunately, no way has been found to detect this error before loading the archive. Debug builds will assert when this is detected so that may be helpful in catching this error.
BOOST_CLASS_EXPORT.
Export implies two things:
BOOST_CLASS_EXPORT in the same
source module that includes any of the archive class headers will
instantiate code required to serialize polymorphic pointers of
the indicated type to the all those archive classes. If no
archive class headers are included, then no code will be instantiated.
Note that the implemenation of this functionality requires
that the BOOST_CLASS_EXPORT
macro appear after the inclusion of any archive
class headers for which code is to be instantiated.
So, code that uses BOOST_CLASS_EXPORT
will look like the following:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
... // other archives
#include "a.hpp" // header declaration for class a
BOOST_CLASS_EXPORT(a)
... // other class headers and exports
This will be true regardless of whether the code is part
of a stand alone executable, a static library or
a dynamic or shared library.
Including
BOOST_CLASS_EXPORT
in the "a.hpp" header itself as one would do with
other serialization traits will make it difficult
or impossible to follow the rule above regarding
inclusion of archive headers before
BOOST_CLASS_EXPORT
is invoked. This can best be addressed by using
BOOST_CLASS_EXPORT_KEY
in the header declarations and
BOOST_CLASS_EXPORT_IMPLEMENT
in the class definition file.
This system has certain implications for placing code in static or shared
libraries. Placing BOOST_CLASS_EXPORT
in library code will have no effect unless archive class headers are
also included. So when building a library, one should include all headers
for all the archive classes which he anticipates using. Alternatively,
one can include headers for just the
Polymorphic Archives.
Strictly speaking, export should not be necessary if all pointer serialization occurs through the most derived class. However, in order to detect what would be a catastrophic error, the library traps ALL serializations through a pointer to a polymorphic class which are not exported or otherwise registered. So, in practice, be prepared to register or export all classes with one or more virtual functions which are serialized through a pointer.
Note that the implementation of this functionality depends upon vendor specific extensions to the C++ language. So, there is no guaranteed portability of programs which use this facility. However, all C++ compilers which are tested with boost provide the required extensions. The library includes the extra declarations required by each of these compilers. It's reasonable to expect that future C++ compilers will support these extensions or something equivalent.
BOOST_CLASS_EXPORT_KEY
in headers.
BOOST_CLASS_EXPORT_IMPLEMENT
in definitions compiled in the library. For any particular type,
there should be only one file which contains
BOOST_CLASS_EXPORT_IMPLEMENT
for that type. This ensures that only one copy
of serialization code will exist within the program. It avoids
wasted space and the possibility of having different
versions of the serialization code in the same program.
Including
BOOST_CLASS_EXPORT_IMPLEMENT
in multiple files could result in a failure
to link due to duplicated symbols or the throwing
of a runtime exception.
demo_pimpl.cpp
,
demo_pimpl_A.cpp
and
demo_pimpl_A.hpp
where implementation of serialization is in a static library
completely separate from the main program.
test_dll_simple
,
and
dll_a.cpp
where implementation of serialization is also completely separate
from the main program but the code is loaded at runtime. In this
example, this code is loaded automatically when the program which
uses it starts up, but it could just as well be loaded and unloaded
with an OS dependent API call.
Also included are
test_dll_exported.cpp
,
and
polymorphic_derived2.cpp
which are similar to the above but include tests of the export
and no_rtti facilities in the context of DLLS.
For best results, write your code to conform to the following guidelines:
inline code in classes used in DLLS.
This will generate duplicate code in the DLLS and mainline. This
needlessly duplicates code. Worse, it makes is possible for
different versions of the same code to exist simultaneously. This
type of error turns out to be excruciatingly difficult to debug.
Finally, it opens the possibility that a module being referred to
might be explicitly unloaded which would (hopefully) result in
a runtime error. This is another bug that is not always
reproducible or easy to find. For class member templates use something like
template<class Archive>
void serialize(Archive & ar, const unsigned int version);
in the header, and
template<class Archive>
void myclass::serialize(Archive & ar, const unsigned int version){
...
}
BOOST_CLASS_EXPORT_IMPLEMENT(my_class)
#include <boost/archive/text_oarchive>
#include <boost/archive/text_iarchive>
template myclass::serialize(boost::archive::text_oarchive & ar, const unsigned int version);
template myclass::serialize(boost::archive::text_iarchive & ar, const unsigned int version);
... // repeat for each archive class to be used.
in the implementation file. This will result in generation of all code
required in only one place. The library does not detect this type of error for you.
dlopen in *nix or
LoadLibrary in Windows). Try to arrange that they are unloaded in the reverse
sequence. This should guarantee that problems are avoided even if the
above guideline hasn't been followed.
extended_type_info
for associating classes with external identifying strings (GUID)
and void_cast
for casting between pointers of related types.
To complete the functionality of
extended_type_info
the ability to construct and destroy corresponding types has been
added. In order to use this functionality, one must specify
how each type is created. This should be done at the time
a class is exported. So, a more complete example of the code above would be:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
... // other archives
#include "a.hpp" // header declaration for class a
// this class has a default constructor
BOOST_SERIALIZATION_FACTORY_0(a)
// as well as one that takes one integer argument
BOOST_SERIALIZATION_FACTORY_1(a, int)
// specify the GUID for this class
BOOST_CLASS_EXPORT(a)
... // other class headers and exports
With this in place, one can construct, serialize and destroy a class
about which is known only the GUID and a base class.
However, Writing/Reading different archives simultaneously
in different tasks is permitted as each archive instance is (almost)
completely independent from any other archive instance. The only shared
information is some type tables which have been implemented using a
lock-free thread-safe
singleton
described elsewhere in this documentation.
This singleton implementation guarantees that all of this shared information is initialized when the code module which contains it is loaded. The serialization library takes care to ensure that these data structures are not subsequently modified. The only time there could be a problem would be if code is loaded/unloaded while another task is serializing data. This could only occur for types whose serialization is implemented in a dynamically loaded/unloaded DLL or shared library. So if the following is avoided:
array
wrapper.
Serialization functions for data types containing contiguous arrays of homogeneous
types, such as for std::vector, std::valarray or
boost::multiarray should serialize them using an
array wrapper to make use of
these optimizations.
Archive types that can provide optimized serialization for contiguous arrays of
homogeneous types should implement these by overloading the serialization of
the array wrapper, as is done
for the binary archives.
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/state_saver.html ================================================state_saver|
|
Serialization
|
Sometimes a certain value has to change only for a limited scope. This class wrapper saves a copy of the current state of some object, and resets the object's state at destruction time, undoing any change the object may have gone through. Here is the interface:
template<class T>
// T requirements:
// - POD or object semantic (cannot be reference, function, ...)
// - copy constructor
// - operator = (no-throw one preferred)
class state_saver : private boost::noncopyable
{
private:
... // implementation
public:
state_saver(T & object);
~state_saver();
};
The complete implementation can be found
here
The following illustrates how this is expected to be used.
#include <boost/state_saver.hpp>
void func(A & a)
boost::state_saver<A> s(a);
... // alter state of a by calling non-const functions
... // call other functions
// original state of a automatically restored on exit
}
Robert Ramey made an initial version for the serialization library.
Pavel Vozenilek made several non-obvious refinements to make it more secure and boost friendly
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/static_warning.html ================================================|
|
Serialization
|
<boost/serialization/static_warning.hpp> supplies a single macro
BOOST_STATIC_WARNING(x), which generates a compile time warning message if
the integral-constant-expression x is not true.
Note that if the condition is true, then the macro will generate neither code nor data - and the macro can also be used at either namespace, class or function scope. When used in a template, the expression x will be evaluated at the time the template is instantiated; this is particularly useful for validating template parameters.
It is intended that the functioning of BOOST_STATIC_WARNING(x)
be identical to that of BOOST_STATIC_ASSERT(x)
except that rather than resulting in a compilation error, it will result in
a compiler warning. In all other respects it should be the same. So
for more information on using BOOST_STATIC_WARNING(x)
consult the documentation for BOOST_STATIC_ASSERT(x)
here.
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/strong_typedef.html ================================================|
|
Serialization
|
typedef creates an alias for an existing type. It does not create
a new type that can be used for matching either function or template parameters.
This can be shown by trying to compile the following example.
typedef int a;
void f(int x); // (1) function to handle simple integers
void f(a x); // (2) special function to handle integers of type a
int main(){
int x = 1;
a y;
y = x; // other operations permitted as a is converted as necessary
f(x); // chooses (1)
f(y); // chooses (2)
}
Since typedef doesn't create a new type, this program can't compile to code
that implements its obvious intention.
Usage of BOOST_STRONG_TYPEDEF addresses this.
#include <boost/serialization/strong_typedef.hpp>
BOOST_STRONG_TYPEDEF(int, a)
void f(int x); // (1) function to handle simple integers
void f(a x); // (2) special function to handle integers of type a
int main(){
int x = 1;
a y;
y = x; // other operations permitted as a is converted as necessary
f(x); // chooses (1)
f(y); // chooses (2)
}
The program will now compile and run as expected.
BOOST_STRONG_TYPEDEF
has been designed to be similar to the standard
typedef. So
BOOST_STRONG_TYPEDEF(primitive type, name)
will create a new type "name" which will be substitutable for the original
type but still of distinct type.
BOOST_STRONG_TYPEDEF is a macro
which generates a class named "name" which wraps an instance of its
primitive type and provides appropriate conversion operators in order
to make the new type substitutable for the one that it wraps.
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/style.css ================================================ pre{ BORDER-RIGHT: gray 1pt solid; BORDER-TOP: gray 1pt solid; BORDER-LEFT: gray 1pt solid; BORDER-BOTTOM: gray 1pt solid; MARGIN-LEFT: 0pt; background-color: #EEEEEE; } /* (C) Copyright 2008 Robert Ramey - http://www.rrsd.com . Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ ================================================ FILE: doc/todo.html ================================================|
|
SerializationTo Do |
It's missing the following:
The first thing I did was include some of the serialization library tests. It became immediately apparent that these tests were totally unsuitable for performance testing and that new tests needed to be written for this purpose. These tests would highlight the location of any performance bottlenecks in the serialization library. Whenever I've subjected my code in the past to this type of analysis, I've always been surprised to find bottlenecks in totally unanticipated places and fixing those has always led to large improvements in performance. I expect that this project would have a huge impact on the utility of the serialization library.
extended_typeinfo
implemenation which presumes that all classes names have been exported.
So, to make this library compatible for platforms without RTTI,
a set of tests, examples and new manual section would have to be created.
Revised 1 November, 2008
© Copyright Robert Ramey 2002-2008. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/traits.html ================================================|
|
SerializationClass Serialization Traits |
int, it wouldn't make sense to save
a version number in the archive. Likewise, for a data type that is never
serialized through a pointer, it would (almost) never make sense to track
the address of objects saved to/loaded from the archive as it will never
be saved/loaded more than once in any case. Details of
serialization for a particular data type will vary depending on the
type, the way it is used and specifications of the programmer.
One can alter the manner in which a particular data type is serialized by specifying one or more class serialization traits. It is not generally necessary for the programmer to explicitly assign traits to his classes as there are default values for all traits. If the default values are not appropriate they can be assigned by the programmer. A template is used to associate a typename with a constant. For example see version.hpp.
namespace boost {
namespace serialization {
template<class T>
struct version
{
BOOST_STATIC_CONSTANT(unsigned int, value = 0);
};
} // namespace serialization
} // namespace boost
For any class T, The default definition
of boost::serialization::version<T>::value is 0.
If we want to assign a value of 2 as the version for class my_class
we specialize the version template:
namespace boost {
namespace serialization {
struct version<my_class>
{
BOOST_STATIC_CONSTANT(unsigned int, value = 2);
};
} // namespace serialization
} // namespace boost
Now whenever the version number for class my_class is required,
the value 2 will be returned rather than the default value of 0.
To diminish typing and enhance readability, a macro is defined so that instead of the above, we could write:
BOOST_CLASS_VERSION(my_class, 2)
which expands to the code above.
// names for each level
enum level_type
{
// Don't serialize this type. An attempt to do so should
// invoke a compile time assertion.
not_serializable = 0,
// write/read this type directly to the archive. In this case
// serialization code won't be called. This is the default
// case for fundamental types. It presumes a member function or
// template in the archive class that can handle this type.
// there is no runtime overhead associated reading/writing
// instances of this level
primitive_type = 1,
// Serialize the objects of this type using the objects "serialize"
// function or template. This permits values to be written/read
// to/from archives but includes no class or version information.
object_serializable = 2,
///////////////////////////////////////////////////////////////////
// once an object is serialized at one of the above levels, the
// corresponding archives cannot be read if the implementation level
// for the archive object is changed.
///////////////////////////////////////////////////////////////////
// Add class information to the archive. Class information includes
// implementation level, class version and class name if available.
object_class_info = 3,
};
Using a macro defined in level.hpp we can specify
that my_class should be serialized along with its version number:
BOOST_CLASS_IMPLEMENTATION(my_class, boost::serialization::object_class_info)
If implementation level is not explicitly assigned, the system uses
a default according to the following rules.
volatile
assign not_serializable
primitive_type
object_class_info
object_serializable
to override the default setting of object_class_info.
For example,
this has been done for the
binary_object wrapper
// names for each tracking level
enum tracking_type
{
// never track this type
track_never = 0,
// track objects of this type if the object is serialized through a
// pointer.
track_selectively = 1,
// always track this type
track_always = 2
};
A corresponding macro is defined so that we can use:
BOOST_CLASS_TRACKING(my_class, boost::serialization::track_never)
Default tracking traits are:
track_never.
track_never.
That is, addresses of addresses are not tracked by default.
boost::serialization::nvp,
track_never.
track_selectively.
That is addresses of serialized objects are tracked if and only if
one or more of the following is true:
The default behavior is almost always the most convenient one. However, there a few cases where it would be desirable to override the default. One case is that of a virtual base class. In a diamond inheritance structure with a virtual base class, object tracking will prevent redundant save/load invocations. So here is one case where it might be convenient to override the default tracking trait. (Note: in a future version the default will be reimplemented to automatically track classes used as virtual bases). This situation is demonstrated by test_diamond.cpp included with the library.
This is addressed by invoking
BOOST_CLASS_EXPORT_IMPLEMENT(T)
in the file which defines (implements) the class T.
This ensures that code for the derived class T will
be explicitly instantiated.
typeid() which can be
used to return a unique string for the class. This is not entirely
satisfactory for our purposes for the following reasons:
So in the serialization library, this is addressed by invoking
BOOST_CLASS_EXPORT_KEY2(my_class, "my_class_external_identifier")
in the header file which declares the class.
In a large majority of applications, the class name works just fine
for the external identifier string so the following short cut is
defined -
BOOST_CLASS_EXPORT_KEY(my_class).
BOOST_CLASS_EXPORT(my_class)
or
BOOST_CLASS_EXPORT_GUID(my_class, "my_class_external_identifier")
in either the declaration header or definition. These macros
expand to invocation of both of the macros described above.
(GUID stands for Globally Unique IDentifier.)
(Elsewhere in this manual, the serialization of derived classes is addressed in detail.)
The header file export.hpp contains all macro definitions described here. The library will throw a runtime exception if
BOOST_IS_ABSTRACT(T)
to do this. Not all compilers support this type trait and corresponding
macro. To address this, the macro
BOOST_SERIALIZATION_ASSUME_ABSTRACT(T) has been
implemented to permit one to explicitly indicate that a specified
type is in fact abstract. This will guarentee that
BOOST_IS_ABSTRACT
will return the correct value for all compilers.
typeid(...) which is available
in systems which support RTTI (Run Time
Type Information).
This will be satisfactory in almost all cases and most users of this
library will lose nothing in skipping this section of the manual.
However, there are some cases where the default type determination
system is not convenient. Some platforms might not support
RTTI or it may have been disabled in order to speed execution
or for some other reason. Some applications, E.G. runtime linking
of plug-in modules, can't depend on C++ RTTI to determine the
true derived class. RTTI only returns the correct type for polymorphic
classes - classes with at least one virtual function. If any of these
situations applies, one may substitute his own implementation of
extended_type_info
The interface to facilities required to implement serialization is defined in
extended_type_info.hpp.
Default implementation of these facilities based on typeid(...)
is defined in
extended_type_info_typeid.hpp.
An alternative implementation based on exported class identifiers
is defined in
extended_type_info_no_rtti.hpp.
By invoking the macro:
BOOST_CLASS_TYPE_INFO(
my_class,
extended_type_info_no_rtti<my_class>
)
we can assign the type information implementation to each class on a case by
case basis. There is no requirement that all classes in a program use the same
implementation of extended_type_info. This supports the concept
that serialization of each class is specified "once and for all" in a header
file that can be included in any project without change.
This is illustrated by the test program test_no_rtti.cpp. Other implementations are possible and might be necessary for certain special cases.
namespace boost {
namespace serialization {
template<class T>
struct is_wrapper
: public mpl::false_
{};
} // namespace serialization
} // namespace boost
For any class T, The default definition
of boost::serialization::is_wrapper<T>::value is thus false.
If we want to declare that a class my_class
is a wrapper we specialize the version template:
namespace boost {
namespace serialization {
struct is_wrapper<my_class>
: mpl::true_
{};
} // namespace serialization
} // namespace boost
To diminish typing and enhance readability, a macro is defined so that instead of the above, we could write:
BOOST_CLASS_IS_WRAPPER(my_class)
which expands to the code above.
namespace boost { namespace serialization {
template<class T>
struct is_bitwise_serializable
: public is_arithmetic<T>
{};
} }
is used, and can be specialized for other classes. The specialization
is made easy by the corresponding macro:
BOOST_IS_BITWISE_SERIALIZABLE(my_class)
template<class T>
struct nvp : public std::pair<const char *, T *>
{
...
};
used by XML archives to associate a name with a data variable of type T.
These data types are never tracked and never versioned. So one might
want to specify:
BOOST_CLASS_IMPLEMENTATION(nvp<T>, boost::serialization::level_type::object_serializable)
BOOST_CLASS_TRACKING(nvp<T>, boost::serialization::track_never)
Examination of the definition of these macros reveals that they won't expand
to sensible code when used with a template argument. So rather than using the
convenience macros, use the original definitions
template<class T>
struct implementation_level<nvp<T> >
{
typedef mpl::integral_c_tag tag;
typedef mpl::int_<object_serializable> type;
BOOST_STATIC_CONSTANT(
int,
value = implementation_level::type::value
);
};
// nvp objects are generally created on the stack and are never tracked
template<class T>
struct tracking_level<nvp<T> >
{
typedef mpl::integral_c_tag tag;
typedef mpl::int_<track_never> type;
BOOST_STATIC_CONSTANT(
int,
value = tracking_level::type::value
);
};
to assign serialization traits to all classes generated by the template
nvp<T>
Note that it is only possible to use the above method to assign traits to templates when using compilers which correctly support Partial Template Specialization. One's first impulse might be to do something like:
#ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
template<class T>
struct implementation_level<nvp<T> >
{
... // see above
};
// nvp objects are generally created on the stack and are never tracked
template<class T>
struct tracking_level<nvp<T> >
{
... // see above
};
#endif
This can be problematic when one wants to make his code and archives
portable to other platforms. It means the objects will be serialized differently
depending on the platform used. This implies that objects saved from one platform
won't be loaded properly on another. In other words, archives won't be portable.
This problem is addressed by creating another method of assigning serialization traits to user classes. This is illustrated by the serialization for a name-value pair.
Specifically, this entails deriving the template from a special class
boost::serialization::traits which is specialized for a specific
combination of serialization traits.
When looking up the serialization traits, the library first checks to see if this class has been
used as a base class. If so, the corresponding traits are used. Otherwise, the standard defaults
are used. By deriving from a serialization traits class rather than relying upon Partial Template
Specializaton, one can a apply serialization traits to a template and those traits will be
the same across all known platforms.
The signature for the traits template is:
template<
class T,
int Level,
int Tracking,
unsigned int Version = 0,
class ETII = BOOST_SERIALIZATION_DEFAULT_TYPE_INFO(T),
class IsWrapper = mpl::false_
>
struct traits
and template parameters should be assigned according to the following table:
| parameter | description | permitted values | default value |
|---|---|---|---|
T | target class | class name | none |
Level | implementation level | not_serializable | none |
Tracking | tracking level | track_never | none |
Version | class version | unsigned integer | 0 |
ETTI | type_info implementation | extended_type_info_typeid | default type_info implementation |
IsWrapper | is the type a wrapper? | mpl::false_ | mpl::false_ |
T t;
ar << t;
unless the tracking_level serialization trait is set to "track_never". The following
will compile without problem:
const T t
ar << t;
Likewise, the following code will trap at compile time:
T * t;
ar >> t;
if the tracking_level serialization trait is set to "track_never".
The following case illustrates the function of this message.
It was originally used as an example in the
mailing list by Peter Dimov.
Suppose that the above message is not displayed and the code is used as is.
class construct_from
{
...
};
void main(){
...
Y y;
construct_from x(y);
ar << x;
}
void main(){
...
Y y;
construct_from x(y);
ar << x;
...
x.f(); // change x in some way
...
ar << x
}
Again no problem. He gets two different of copies in the archive, each one is different. That is he gets exactly what he expects and is naturally delighted.
class K {
shared_ptr <construct_from> z;
template <class Archive>
void serialize(Archive & ar, const unsigned version){
ar << z;
}
};
He builds and runs the program and tests his new functionality. It works great and he's delighted.
BOOST_CLASS_TRACKING(construct_from, track_never)
shared_ptr<construct_from>
is not going to have a single raw pointer shared amongst the instances. Each loaded
shared_ptr<construct_from> is going to
have its own distinct raw pointer. This will break
shared_ptr and cause a memory leak. Again,
The cause of this problem is very far removed from the point of discovery. It could
well be that the problem is not even discovered until after the archives are loaded.
Now we not only have a difficult to find and fix program bug, but we have a bunch of
invalid archives and lost data.
Now consider what happens when the message is displayed:
ar << x;
const_cast
- because it looks bad. So he'll just make the following change an move on.
Y y;
const construct_from x(y);
ar << x;
Things work fine and he moves on.
Y y;
const construct_from x(y);
...
x.f(); // change x in some way ; compile error f() is not const
...
ar << x
He's mildly annoyed now he tries the following:
const
from const construct_from above - damn now he
gets the trap. If he looks at the comment code where the
BOOST_STATIC_ASSERT
occurs, he'll do one of two things
const_cast
and fire off a complaint to the list and maybe they will fix it.
In this case, the story branches off to the previous scenario.
BOOST_CLASS_TRACKING(construct_from, track_never)
construct_from trait has been set to
"track_never" so he should always get copies and the log should be what we expect.
BOOST_CLASS_TRACKING that
was inserted. Now the second trap is avoided, But damn - the first trap is
popping up again. Eventually, after some code restructuring, the differing
requirements of serializating construct_from
are reconciled.
ar << T * anywhere else in the
program is used to infer that an object of this type should be tracked.
A problem arises when a program which reads an archive
includes the operation ar >> T * so that tracking information
will be included in the archive. When a program which
creates the archive doesn't include ar << T it is presumed
that the archive doesn't include tracking information and
the archive will fail to load. Also the reverse situation could
trigger a similar problem.
Though this situation is unlikely for several reasons, it is possible - hence this warning.
© Copyright Robert Ramey 2002-2004 and Matthias Troyer 2006. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/tutorial.html ================================================|
|
SerializationTutorial |
serialize into save/load
ar << data;
ar & data;
An input archive is similar to an input datastream. Data can be loaded from the archive
with either the >> or the & operator.
ar >> data;
ar & data;
When these operators are invoked for primitive data types, the data is simply saved/loaded
to/from the archive. When invoked for class data types, the class
serialize function is invoked. Each
serialize function is uses the above operators
to save/load its data members. This process will continue in a recursive manner until
all the data contained in the class is saved/loaded.
serialize
function to save and load class data members.
Included in this library is a program called demo.cpp which illustrates how to use this system. Below we excerpt code from this program to illustrate with the simplest possible case how this library is intended to be used.
#include <fstream>
// include headers that implement a archive in simple text format
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
/////////////////////////////////////////////////////////////
// gps coordinate
//
// illustrates serialization for a simple type
//
class gps_position
{
private:
friend class boost::serialization::access;
// When the class Archive corresponds to an output archive, the
// & operator is defined similar to <<. Likewise, when the class Archive
// is a type of input archive the & operator is defined similar to >>.
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & degrees;
ar & minutes;
ar & seconds;
}
int degrees;
int minutes;
float seconds;
public:
gps_position(){};
gps_position(int d, int m, float s) :
degrees(d), minutes(m), seconds(s)
{}
};
int main() {
// create and open a character archive for output
std::ofstream ofs("filename");
// create class instance
const gps_position g(35, 59, 24.567f);
// save data to archive
{
boost::archive::text_oarchive oa(ofs);
// write class instance to archive
oa << g;
// archive and stream closed when destructors are called
}
// ... some time later restore the class instance to its orginal state
gps_position newg;
{
// create and open an archive for input
std::ifstream ifs("filename");
boost::archive::text_iarchive ia(ifs);
// read class state from archive
ia >> newg;
// archive and stream closed when destructors are called
}
return 0;
}
For each class to be saved via serialization, there must exist a function to
save all the class members which define the state of the class.
For each class to be loaded via serialization, there must exist a function to
load these class members in the same sequence as they were saved.
In the above example, these functions are generated by the
template member function serialize.
The above formulation is intrusive. That is, it requires that classes whose instances are to be serialized be altered. This can be inconvenient in some cases. An equivalent alternative formulation permitted by the system would be:
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
class gps_position
{
public:
int degrees;
int minutes;
float seconds;
gps_position(){};
gps_position(int d, int m, float s) :
degrees(d), minutes(m), seconds(s)
{}
};
namespace boost {
namespace serialization {
template<class Archive>
void serialize(Archive & ar, gps_position & g, const unsigned int version)
{
ar & g.degrees;
ar & g.minutes;
ar & g.seconds;
}
} // namespace serialization
} // namespace boost
In this case the generated serialize functions are not members of the
gps_position class. The two formulations function
in exactly the same way.
The main application of non-intrusive serialization is to permit serialization
to be implemented for classes without changing the class definition.
In order for this to be possible, the class must expose enough information
to reconstruct the class state. In this example, we presumed that the
class had public members - not a common occurrence. Only
classes which expose enough information to save and restore the class
state will be serializable without changing the class definition.
A serializable class with serializable members would look like this:
class bus_stop
{
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & latitude;
ar & longitude;
}
gps_position latitude;
gps_position longitude;
protected:
bus_stop(const gps_position & lat_, const gps_position & long_) :
latitude(lat_), longitude(long_)
{}
public:
bus_stop(){}
// See item # 14 in Effective C++ by Scott Meyers.
// re non-virtual destructors in base classes.
virtual ~bus_stop(){}
};
That is, members of class type are serialized just as members of primitive types are.
Note that saving an instance of the class bus_stop
with one of the archive operators will invoke the
serialize function which saves
latitude and
longitude. Each of these in turn will be saved by invoking
serialize in the definition of
gps_position. In this manner the whole
data structure is saved by the application of an archive operator to
just its root item.
Derived classes should include serializations of their base classes.
#include <boost/serialization/base_object.hpp>
class bus_stop_corner : public bus_stop
{
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// serialize base class information
ar & boost::serialization::base_object<bus_stop>(*this);
ar & street1;
ar & street2;
}
std::string street1;
std::string street2;
virtual std::string description() const
{
return street1 + " and " + street2;
}
public:
bus_stop_corner(){}
bus_stop_corner(const gps_position & lat_, const gps_position & long_,
const std::string & s1_, const std::string & s2_
) :
bus_stop(lat_, long_), street1(s1_), street2(s2_)
{}
};
Note the serialization of the base classes from the derived
class. Do NOT directly call the base class serialize
functions. Doing so might seem to work but will bypass the code
that tracks instances written to storage to eliminate redundancies.
It will also bypass the writing of class version information into
the archive. For this reason, it is advisable to always make member
serialize functions private. The declaration
friend boost::serialization::access will grant to the
serialization library access to private member variables and functions.
bus_stop.
class bus_route
{
friend class boost::serialization::access;
bus_stop * stops[10];
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
int i;
for(i = 0; i < 10; ++i)
ar & stops[i];
}
public:
bus_route(){}
};
Each member of the array stops will be serialized.
But remember each member is a pointer - so what can this really
mean? The whole object of this serialization is to permit
reconstruction of the original data structures at another place
and time. In order to accomplish this with a pointer, it is
not sufficient to save the value of the pointer, rather the
object it points to must be saved. When the member is later
loaded, a new object has to be created and a new pointer has
to be loaded into the class member.
If the same pointer is serialized more than once, only one instance is be added to the archive. When read back, no data is read back in. The only operation that occurs is for the second pointer is set equal to the first
Note that, in this example, the array consists of polymorphic pointers. That is, each array element point to one of several possible kinds of bus stops. So when the pointer is saved, some sort of class identifier must be saved. When the pointer is loaded, the class identifier must be read and and instance of the corresponding class must be constructed. Finally the data can be loaded to newly created instance of the correct type. As can be seen in demo.cpp, serialization of pointers to derived classes through a base clas pointer may require explicit enumeration of the derived classes to be serialized. This is referred to as "registration" or "export" of derived classes. This requirement and the methods of satisfying it are explained in detail here.
All this is accomplished automatically by the serialization library. The above code is all that is necessary to accomplish the saving and loading of objects accessed through pointers.
class bus_route
{
friend class boost::serialization::access;
bus_stop * stops[10];
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & stops;
}
public:
bus_route(){}
};
#include <boost/serialization/list.hpp>
class bus_route
{
friend class boost::serialization::access;
std::list<bus_stop *> stops;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & stops;
}
public:
bus_route(){}
};
Suppose we're satisfied with our bus_route class, build a program
that uses it and ship the product. Some time later, it's decided
that the program needs enhancement and the bus_route class is
altered to include the name of the driver of the route. So the
new version looks like:
#include <boost/serialization/list.hpp>
#include <boost/serialization/string.hpp>
class bus_route
{
friend class boost::serialization::access;
std::list<bus_stop *> stops;
std::string driver_name;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & driver_name;
ar & stops;
}
public:
bus_route(){}
};
Great, we're all done. Except... what about people using our application
who now have a bunch of files created under the previous program.
How can these be used with our new program version?
In general, the serialization library stores a version number in the archive for each class serialized. By default this version number is 0. When the archive is loaded, the version number under which it was saved is read. The above code can be altered to exploit this
#include <boost/serialization/list.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/version.hpp>
class bus_route
{
friend class boost::serialization::access;
std::list<bus_stop *> stops;
std::string driver_name;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
// only save/load driver_name for newer archives
if(version > 0)
ar & driver_name;
ar & stops;
}
public:
bus_route(){}
};
BOOST_CLASS_VERSION(bus_route, 1)
By application of versioning to each class, there is no need to
try to maintain a versioning of files. That is, a file version
is the combination of the versions of all its constituent classes.
This system permits programs to be always compatible with archives
created by all previous versions of a program with no more
effort than required by this example.
serialize
into save/loadserialize function is simple, concise, and guarantees
that class members are saved and loaded in the same sequence
- the key to the serialization system. However, there are cases
where the load and save operations are not as similar as the examples
used here. For example, this could occur with a class that has evolved through
multiple versions. The above class can be reformulated as:
#include <boost/serialization/list.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/split_member.hpp>
class bus_route
{
friend class boost::serialization::access;
std::list<bus_stop *> stops;
std::string driver_name;
template<class Archive>
void save(Archive & ar, const unsigned int version) const
{
// note, version is always the latest when saving
ar & driver_name;
ar & stops;
}
template<class Archive>
void load(Archive & ar, const unsigned int version)
{
if(version > 0)
ar & driver_name;
ar & stops;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
public:
bus_route(){}
};
BOOST_CLASS_VERSION(bus_route, 1)
The macro BOOST_SERIALIZATION_SPLIT_MEMBER() generates
code which invokes the save
or load
depending on whether the archive is used for saving or loading.
In this tutorial, we have used a particular
archive class - text_oarchive for saving and
text_iarchive for loading.
text archives render data as text and are portable across platforms. In addition
to text archives, the library includes archive class for native binary data
and xml formatted data. Interfaces to all archive classes are all identical.
Once serialization has been defined for a class, that class can be serialized to
any type of archive.
If the current set of archive classes doesn't provide the attributes, format, or behavior needed for a particular application, one can either make a new archive class or derive from an existing one. This is described later in the manual.
The astute reader might notice that these examples contain a subtle but important flaw.
They leak memory. The bus stops are created in the
main function. The bus schedules may refer to these bus stops
any number of times. At the end of the main function after the bus schedules are destroyed,
the bus stops are destroyed. This seems fine. But what about the structure
new_schedule data item created by the
process of loading from an archive? This contains its own separate set of bus stops
that are not referenced outside of the bus schedule. These won't be destroyed
anywhere in the program - a memory leak.
There are couple of ways of fixing this. One way is to explicitly manage the bus stops.
However, a more robust and transparent is to use
shared_ptr rather than raw pointers. Along
with serialization implementations for the Standard Library, the serialization library
includes implementation of serialization for
boost::shared ptr. Given this, it should be
easy to alter any of these examples to eliminate the memory leak. This is left
as an exercise for the reader.
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/void_cast.html ================================================ |
|
Serialization
|
dynamic_cast<T>(U * u)
for casting a pointer at runtime between two related types. However, this can only be
used for polymorphic classes. That is, it can only be used with related classes which have at least one virtual function.
Limiting the serialization of pointers to only such classes would diminish the applicability
of the library.
boost::serialization.
template<class Derived, class Base>
const void_cast_detail::void_caster &
void_cast_register(
Derived const * derived = NULL,
Base * const base = NULL
);
Derived is immediately derived from
Base in a global table.
void_cast_register<A, B>();
void_cast_register<B, C>();
automatically derives the fact that A can be upcast to C and vice-versa.
void *
void_upcast(
extended_type_info const & derived_type,
extended_type_info const & base_type,
void * const t
);
void *
void_downcast(
extended_type_info const & derived_type,
extended_type_info const & base_type,
void * const t
);
extended_type_info
records. An attempt to cast between types not "registered" with
void_cast_register
will throw a
boost::archive::archive_exception
with value equal to
unregistered_cast
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
================================================ FILE: doc/wrappers.html ================================================ |
|
SerializationSerialization Wrappers |
BOOST_STRONG_TYPEDEF
Wrappers need to be treated in a special way by some archives, and hence
the is_wrapper trait for
these wrapper classes is set to true.
boost::serialization::binary_object(void * t, size_t size);
boost::serialization::make_binary_object(void * t, size_t size);
which will construct a temporary binary object that can be serialized just like any other object.
Its default serialization is to use archive class primitives
save_binary and load_binary.
Note that it doesn't allocated any storage or create any objects.
Its sole purpose is to pass the data size and address as a pair to the archive class.
boost::array<T> or a std::vector<T>.
The purpose of this wrapper is to support archive types (such as binary
archives) that provide optimized serialization for contiguous sequences of
objects of the same type.
The header file
array.hpp
includes the function
template <T>
boost::serialization::make_array(T* t, std::size_t size);
which will construct a temporary array object
template<class T>
class array
{
public:
typedef T value_type;
array(value_type* t, std::size_t s);
value_type* address() const;
std::size_t count() const;
};
that can be serialized just like any other object.
Its default serialization is to serialize each array element.
Note that it doesn't allocated any storage or create any objects.
Its sole purpose is to pass the data type, size and address to the archive class.
Archive types that can provide optimized implementations for contiguous
arrays of homogeneous data types should overload the serialization of
array.
BOOST_STRONG_TYPEDEFBOOST_STRONG_TYPEDEF template.
The serialization libraries uses these to pass particular kinds of integers such
as object_id, version, etc. to an archive class. Given that these integers
are now distinguishable according to their type, XML archives can apply
special handling to these types. For example, a version number is rendered
as an XML attribute in the form "version=12". In the absence of any specific override,
these types are automatically converted to the underlying integer type so the
special overrides used for XML archives aren't needed for other archives.
collection_size_type in the
header file
collection_size_type.hpp
. This type should be used for serializing the size of a C++ collection, so
that the archive can pick the best integral representation for the serialization
of collection sizes. This is necessary since, although std::size_t
is guaranteed to be an integral type large enough to represent the size of
a collection on a specific platform, the archive might want to serialize
the size differently than this type. For example, the collection_size_type
might be serialized as a variable length integer in a portable binary archive.
Our solution is to wrap class members to be serialized in a
name-value-pair. This structure is defined in
nvp.hpp.
It is just a reference to the data member coupled with a pointer to
to a const char * which
corresponds to the XML name. It implements the default
serialization functions for a name-value pair. This default
action is to just ignore the item name and serialize the
data value in the normal manner. For archive classes that
don't make any special provision for name-value pairs, this
is the action which will be invoked when the name-value pair
is serialized. Hence, wrapping a data value into a name-value
pair will have no effect when used with archives which
make no special provision for this wrapper.
The xml archive classes contain code similar to:
// special treatment for name-value pairs.
template<class T>
xml_oarchive & operator&(const boost::serialization::nvp<T> & t)
{
// write an xml start tag
start_tag(t.name());
// serialize the data as usual
*this & t.value();
// write an xml end tag
end_tag(t.name());
}
The most obvious and convenient name to assign to as the XML data item name
is - surprise! - the name of the C++ class data member. So our serialization
code will look like:
ar & make_nvp("my_variable", my_variable);
To simplify typing and enhance readability a macro is defined so we can write:
ar & BOOST_SERIALIZATION_NVP(my_variable);
Similarly there exists a macro definition that permits us to write:
BOOST_SERIALIZATION_BASE_OBJECT_NVP(my_base_class)
Note that these macros must be used in the namespace of the class,
and without qualifying the namespace in the argument.
demo_gps.hpp includes NVP wrappers or all data members. demo_xml.cpp saves and loads data to an XML archive. Here is example of the XML Archive corresponding to our tutorial example.
ar & make_nvp("named_binary_object", make_binary_object(address, size));
© Copyright Robert Ramey 2002-2004. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)