diff --git a/doc/doxygen.config b/doc/doxygen.config index 2e3e07a0..d8602293 100644 --- a/doc/doxygen.config +++ b/doc/doxygen.config @@ -10,6 +10,8 @@ INPUT = \ ../immer/heap \ ../immer/refcount \ ../immer/transience \ + ../immer/extra/persist \ + ../immer/extra/persist/xxhash \ ../immer/extra/persist/cereal INCLUDE_PATH = .. QUIET = YES diff --git a/doc/persist.rst b/doc/persist.rst index b35a4cc3..ba2b19d3 100644 --- a/doc/persist.rst +++ b/doc/persist.rst @@ -174,10 +174,10 @@ The next component we need is the pools of all the containers from the value: .. literalinclude:: ../test/extra/persist/test_for_docs.cpp :language: c++ - :start-after: start-get_auto_pool - :end-before: end-get_auto_pool + :start-after: start-get_output_pools + :end-before: end-get_output_pools -The ``get_auto_pool`` function returns the output pools of all ``immer`` containers that would be serialized using +The ``get_output_pools`` function returns the output pools of all ``immer`` containers that would be serialized using pools, as controlled by the policy. Here we use the default policy ``hana_struct_auto_policy`` which will use pools for all ``immer`` containers inside of the document type which must be a ``hana::Struct``. @@ -232,8 +232,8 @@ The first two steps are the same as in the previous example: .. literalinclude:: ../test/extra/persist/test_for_docs.cpp :language: c++ - :start-after: start-get_auto_pool - :end-before: end-get_auto_pool + :start-after: start-get_output_pools + :end-before: end-get_output_pools Only this time the transforming function will convert an integer into a string: @@ -266,6 +266,7 @@ And serialize it with pools: In the resulting JSON we can confirm that the node ``{"key": 2, "value": ["_1_", "_2_"]}`` is reused for both vectors. +.. _transforming-hash-based-containers: Transforming hash-based containers ---------------------------------- @@ -376,6 +377,7 @@ Finally, to convert the ``value`` with the defined ``conversion_map`` we prepare We can see that the ``new_value`` table contains the transformed data from the original ``value`` table. +.. _modifying-the-hash-of-the-id: Modifying the hash of the ID ---------------------------- @@ -416,6 +418,7 @@ a ``immer::persist::incompatible_hash_wrapper`` as the result of the ``immer::pe We can see that the transformation has been applied, the keys have the ``_key`` suffix. +.. _transforming-nested-containers: Transforming nested containers ------------------------------ @@ -496,3 +499,19 @@ API Overview .. doxygengroup:: persist-api :project: immer :content-only: + + +Transform API +--------------- + +.. doxygengroup:: Persist-transform + :project: immer + :content-only: + + +Exceptions +---------- + +.. doxygengroup:: Persist-exceptions + :project: immer + :content-only: diff --git a/immer/extra/persist/cereal/archives.hpp b/immer/extra/persist/cereal/archives.hpp index 56ff00b1..6d311738 100644 --- a/immer/extra/persist/cereal/archives.hpp +++ b/immer/extra/persist/cereal/archives.hpp @@ -74,15 +74,12 @@ constexpr bool is_pool_empty() } // namespace detail /** - * Adapted from cereal/archives/adapters.hpp - */ - -/** - * @brief An output archive wrapper that provides access to the ``Pools`` stored - * inside. And serializes the ``pools`` object alongside the user document. + * @brief A wrapper type that wraps a `cereal::OutputArchive` (for example, + * `JSONOutputArchive`), provides access to the `Pools` object stored inside, + * and serializes the `pools` object alongside the user document. * * Normally, the function `cereal_save_with_pools` should be used instead of - * using wrapper directly. + * using this wrapper directly. * * @see cereal_save_with_pools * diff --git a/immer/extra/persist/cereal/with_pools.hpp b/immer/extra/persist/cereal/with_pools.hpp index 48bfe58b..86a7a699 100644 --- a/immer/extra/persist/cereal/with_pools.hpp +++ b/immer/extra/persist/cereal/with_pools.hpp @@ -12,8 +12,9 @@ namespace immer::persist { */ /** - * @brief Serialize the provided value using the provided policy to JSON - * outputting into the provided stream. + * @brief Serialize the provided value with pools using the provided policy + * outputting into the provided stream. By default, `cereal::JSONOutputArchive` + * is used but a different `cereal` output archive can be provided. * * @see Policy * @ingroup persist-api @@ -44,7 +45,9 @@ void cereal_save_with_pools(std::ostream& os, } /** - * @brief Serialize the provided value using the provided policy to JSON. + * @brief Serialize the provided value with pools using the provided policy. By + * default, `cereal::JSONOutputArchive` is used but a different `cereal` output + * archive can be provided. * * @return std::string The resulting JSON. * @ingroup persist-api @@ -62,7 +65,9 @@ std::string cereal_save_with_pools(const T& value0, /** * @brief Load a value of the given type `T` from the provided stream using - * pools. + * pools. By default, `cereal::JSONInputArchive` is used but a different + * `cereal` input archive can be provided. + * * @ingroup persist-api */ template (is, policy); } +/** + * @brief Return just the pools of all the containers of the provided value + * serialized using the provided policy. + * + * @ingroup persist-transform + * @see convert_container + */ template Policy = hana_struct_auto_policy> -auto get_auto_pool(const T& value0, const Policy& policy = Policy{}) +auto get_output_pools(const T& value0, const Policy& policy = Policy{}) { const auto types = policy.get_pool_types(value0); auto pools = detail::generate_output_pools(types); diff --git a/immer/extra/persist/errors.hpp b/immer/extra/persist/errors.hpp index dc30b5cd..c6cf9d60 100644 --- a/immer/extra/persist/errors.hpp +++ b/immer/extra/persist/errors.hpp @@ -8,12 +8,26 @@ namespace immer::persist { +/** + * @defgroup persist-exceptions + */ + +/** + * Base class from which all the exceptions in `immer::persist` are derived. + * + * @ingroup persist-exceptions + */ class pool_exception : public std::invalid_argument { public: using invalid_argument::invalid_argument; }; +/** + * Thrown when a cycle is detected in the pool of vectors. + * + * @ingroup persist-exceptions + */ class pool_has_cycles : public pool_exception { public: @@ -23,6 +37,11 @@ class pool_has_cycles : public pool_exception } }; +/** + * Thrown when a non-existent node is mentioned. + * + * @ingroup persist-exceptions + */ class invalid_node_id : public pool_exception { public: @@ -32,6 +51,11 @@ class invalid_node_id : public pool_exception } }; +/** + * Thrown when a non-existent container is mentioned. + * + * @ingroup persist-exceptions + */ class invalid_container_id : public pool_exception { public: @@ -41,6 +65,11 @@ class invalid_container_id : public pool_exception } }; +/** + * Thrown when a node has more children than expected. + * + * @ingroup persist-exceptions + */ class invalid_children_count : public pool_exception { public: @@ -51,6 +80,11 @@ class invalid_children_count : public pool_exception } }; +/** + * Thrown when duplicate pool name is detected. + * + * @ingroup persist-exceptions + */ class duplicate_name_pool_detected : public pool_exception { public: diff --git a/immer/extra/persist/hash_container_conversion.hpp b/immer/extra/persist/hash_container_conversion.hpp index dbf1bc06..4c92bd26 100644 --- a/immer/extra/persist/hash_container_conversion.hpp +++ b/immer/extra/persist/hash_container_conversion.hpp @@ -3,19 +3,33 @@ namespace immer::persist { /** - * The wrapper is used to enable the incompatible_hash_mode, which is required + * The wrapper is used to enable the incompatible hash mode which is required * when the key of a hash-based container transformed in a way that changes its * hash. + * + * A value of this type should be returned from a transforming function + * accepting `target_container_type_request`. + * + * @ingroup persist-transform + * @see + * @rst + * :ref:`modifying-the-hash-of-the-id` + * @endrst */ template struct incompatible_hash_wrapper {}; /** - * A bit of a hack but currently this is the simplest way to request a type of - * the hash function to be used after the transformation. Maybe the whole thing - * would change later. Right now everything is driven by the single function, - * which seems to be convenient otherwise. + * This type is used as an argument for a transforming function. + * The return type of the function is used to specify the desired container type + * to contain the transformed values. + * + * @ingroup persist-transform + * @see + * @rst + * :ref:`transforming-hash-based-containers` + * @endrst */ struct target_container_type_request {}; diff --git a/immer/extra/persist/transform.hpp b/immer/extra/persist/transform.hpp index 022cf5de..ccd02a05 100644 --- a/immer/extra/persist/transform.hpp +++ b/immer/extra/persist/transform.hpp @@ -5,8 +5,22 @@ namespace immer::persist { /** - * Given an output_pools and a map of transformations, produce a new type of - * load pool with those transformations applied + * @defgroup persist-transform + */ + +/** + * Given output_pools and a map of transformations, produce a new type of + * input pools with those transformations applied. + * + * `conversion_map` is a `boost::hana::map` where keys are types of `immer` + * containers and values are the transforming functions. + * + * @ingroup persist-transform + * @see get_output_pools + * @rst + * :ref:`transformations-with-pools` + * :ref:`transforming-nested-containers` + * @endrst */ template inline auto @@ -23,18 +37,24 @@ transform_output_pool(const detail::output_pools& old_pools, } /** - * Given an old save pools and a new (transformed) load pools, effectively + * Given output_pools and new (transformed) input_pools, effectively * convert the given container. + * + * @ingroup persist-transform + * @see get_output_pools + * @rst + * :ref:`transformations-with-pools` + * :ref:`transforming-nested-containers` + * @endrst */ template -auto convert_container(const detail::output_pools& old_save_pools, - detail::input_pools& new_load_pools, +auto convert_container(const detail::output_pools& output_pools, + detail::input_pools& new_input_pools, const Container& container) { - const auto container_id = - detail::get_container_id(old_save_pools, container); + const auto container_id = detail::get_container_id(output_pools, container); auto& loader = - new_load_pools + new_input_pools .template get_loader_by_old_container>(); auto result = loader.load(container_id); return result; diff --git a/immer/extra/persist/xxhash/xxhash.hpp b/immer/extra/persist/xxhash/xxhash.hpp index efc119d9..45d765ba 100644 --- a/immer/extra/persist/xxhash/xxhash.hpp +++ b/immer/extra/persist/xxhash/xxhash.hpp @@ -4,6 +4,13 @@ namespace immer::persist { +/** + * xxHash is a good option to be used with `immer::persist` as it produces + * hashes identical across all platforms. + * + * @see https://xxhash.com/ + * @ingroup persist-api + */ template struct xx_hash { diff --git a/test/extra/persist/test_circular_dependency_conversion.cpp b/test/extra/persist/test_circular_dependency_conversion.cpp index 5d2129b6..1dfef4f5 100644 --- a/test/extra/persist/test_circular_dependency_conversion.cpp +++ b/test/extra/persist/test_circular_dependency_conversion.cpp @@ -241,7 +241,7 @@ TEST_CASE("Test exception while circular converting") const auto names = immer::persist::detail::get_named_pools_for_hana_type< model::value_one>(); - const auto model_pool = immer::persist::get_auto_pool(value); + const auto model_pool = immer::persist::get_output_pools(value); SECTION("Try to load") { @@ -409,7 +409,7 @@ TEST_CASE("Test circular dependency pools", "[conversion]") const auto names = immer::persist::detail::get_named_pools_for_hana_type< model::value_one>(); - const auto model_pools = immer::persist::get_auto_pool(value); + const auto model_pools = immer::persist::get_output_pools(value); /** * NOTE: There is a circular dependency between pools: to convert diff --git a/test/extra/persist/test_conversion.cpp b/test/extra/persist/test_conversion.cpp index 52a6e805..cba50c96 100644 --- a/test/extra/persist/test_conversion.cpp +++ b/test/extra/persist/test_conversion.cpp @@ -152,7 +152,7 @@ TEST_CASE("Convert between two hierarchies via JSON compatibility", const auto value = model::make_example_history(); - const auto model_pools = immer::persist::get_auto_pool(value); + const auto model_pools = immer::persist::get_output_pools(value); const auto map = hana::make_map( hana::make_pair(hana::type_c>, @@ -201,7 +201,7 @@ struct two_vectors TEST_CASE("Not every type is converted", "[conversion]") { - const auto pools = immer::persist::get_auto_pool(two_vectors{}); + const auto pools = immer::persist::get_output_pools(two_vectors{}); const auto map = hana::make_map(hana::make_pair(hana::type_c>, diff --git a/test/extra/persist/test_for_docs.cpp b/test/extra/persist/test_for_docs.cpp index 44f20d78..3d0eee9c 100644 --- a/test/extra/persist/test_for_docs.cpp +++ b/test/extra/persist/test_for_docs.cpp @@ -293,9 +293,9 @@ TEST_CASE("Transformations", "[docs]") const auto v2 = v1.push_back(4).push_back(5).push_back(6); const auto value = document{v1, v2}; - // include:start-get_auto_pool - const auto pools = immer::persist::get_auto_pool(value); - // include:end-get_auto_pool + // include:start-get_output_pools + const auto pools = immer::persist::get_output_pools(value); + // include:end-get_output_pools namespace hana = boost::hana; @@ -433,7 +433,7 @@ TEST_CASE("Transform hash-based containers", "[docs]") immer::map>; const auto value = int_map_t{{"one", 1}, {"two", 2}}; - const auto pools = immer::persist::get_auto_pool( + const auto pools = immer::persist::get_output_pools( value, direct_container_policy{}); // include:end-prepare-int-map @@ -509,7 +509,7 @@ TEST_CASE("Transform table's ID type", "[docs]") immer::table_key_fn, immer::persist::xx_hash>; const auto value = table_t{old_item{"one", 1}, old_item{"two", 2}}; - const auto pools = immer::persist::get_auto_pool( + const auto pools = immer::persist::get_output_pools( value, direct_container_policy{}); // include:end-prepare-table-value @@ -742,7 +742,7 @@ TEST_CASE("Transform nested containers", "[docs]") // include:end-nested-conversion_map // include:start-apply-nested-transformations - const auto pools = immer::persist::get_auto_pool(value, policy); + const auto pools = immer::persist::get_output_pools(value, policy); auto transformed_pools = immer::persist::transform_output_pool(pools, conversion_map); diff --git a/test/extra/persist/test_special_pool_auto.cpp b/test/extra/persist/test_special_pool_auto.cpp index 2d6b9297..20324d52 100644 --- a/test/extra/persist/test_special_pool_auto.cpp +++ b/test/extra/persist/test_special_pool_auto.cpp @@ -789,7 +789,7 @@ TEST_CASE("Structure breaks when hash is changed") .map = {{123, "123"}, {456, "456"}}, }; - const auto out_pool = immer::persist::get_auto_pool(value); + const auto out_pool = immer::persist::get_output_pools(value); constexpr auto convert_pair = [](const std::pair& old) { return std::make_pair(fmt::format("_{}_", old.first), old.second); @@ -820,7 +820,7 @@ TEST_CASE("Converting between incompatible keys") .table = {{901}, {902}}, }; - const auto ar = immer::persist::get_auto_pool(value); + const auto ar = immer::persist::get_output_pools(value); constexpr auto convert_pair = [](const std::pair& old) { return std::make_pair(fmt::format("_{}_", old.first), old.second);