--- /dev/null
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2019 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii_algorithms_general_data_storage_h
+#define dealii_algorithms_general_data_storage_h
+
+#include <deal.II/base/config.h>
+
+#include <deal.II/base/exceptions.h>
+#include <deal.II/base/subscriptor.h>
+
+#include <boost/any.hpp>
+#include <boost/core/demangle.hpp>
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <typeinfo>
+
+DEAL_II_NAMESPACE_OPEN
+
+
+/**
+ * This class facilitates the storage of any general data.
+ * It offers a mechanism to store any amount of data, of any type,
+ * which is then made accessible by an identifier string.
+ *
+ * When using this class, please cite
+ *
+ * @code{.bib}
+ * @article{SartoriGiulianiBardelloni-2018-a,
+ * Author = {Sartori, Alberto and Giuliani, Nicola and
+ * Bardelloni, Mauro and Heltai, Luca},
+ * Journal = {SoftwareX},
+ * Pages = {318--327},
+ * Title = {{deal2lkit: A toolkit library for high performance
+ * programming in deal.II}},
+ * Volume = {7},
+ * Year = {2018}}
+ * @endcode
+ *
+ * @author Luca Heltai, Jean-Paul Pelteret, 2019
+ */
+class GeneralDataStorage : public Subscriptor
+{
+public:
+ /**
+ * Default constructor.
+ */
+ GeneralDataStorage() = default;
+
+ /**
+ * Copy constructor.
+ */
+ GeneralDataStorage(const GeneralDataStorage &) = default;
+
+ /**
+ * Move constructor.
+ */
+ GeneralDataStorage(GeneralDataStorage &&) = default;
+
+ /**
+ * Number of objects stored by this class instance.
+ */
+ std::size_t
+ size() const;
+
+ /**
+ * Merge the contents of @p other_data with this object.
+ */
+ void
+ merge(const GeneralDataStorage &other_data);
+
+ /**
+ * Print the contents of the internal cache to the @p stream.
+ *
+ * Each key and value pair in the @p any_data map are printed on an
+ * individual line, with the <tt>std::string<\tt> key listed first
+ * followed by the demangled <tt>type_id</tt> of the associated mapped
+ * type.
+ */
+ template <class Stream>
+ void
+ print_info(Stream &stream);
+
+ /**
+ * Clear all data stored in this class instance.
+ *
+ * After this function is called, all copied data owned by this class will
+ * go out of scope. Furthermore, all scoping requirements for data referenced
+ * by this class instance will be lifted.
+ *
+ * To clarify this point, consider the following small example:
+ *
+ * @code
+ * GeneralDataStorage data;
+ *
+ * {
+ * const double some_number = ...;
+ * data.add_unique_reference("value", some_number);
+ *
+ * // Adding either of these next two lines could fix the
+ * // issue, by removing the association of some_number with data:
+ * // data.remove_object_with_name("value");
+ * // data.reset();
+ * } // some_number goes out of scope here
+ *
+ * const double some_other_number
+ * = data.get_object_with_name<double>("value"); // Invalid call
+ * @endcode
+ *
+ * In the code above, the @p data object has a longer scope than
+ * <tt>some_number</tt>. By the time we fetch the <tt>"value"</tt> from
+ * @p data , the reference to @p some_number is no longer valid.
+ *
+ * Similarly, for data copied into a GeneralDataStorage object one should
+ * consider the scope under which it remains valid:
+ *
+ * @code
+ * double* ptr_to_some_number = null_ptr;
+ *
+ * {
+ * GeneralDataStorage data;
+ * const double some_number = ...;
+ * data.add_unique_copy("value", some_number);
+ *
+ * ptr_to_some_number = &(data.get_object_with_name<double>("value"));
+ * } // The copy to some_number goes out of scope here
+ *
+ * const double some_other_number
+ * = *ptr_to_some_number; // Invalid call
+ * @endcode
+ *
+ * Similar to the first example, we must be concious of the fact that the
+ * copies of any @p Type stored by @p data only remains valid while the
+ * GeneralDataStorage instance in which it is stored is alive.
+ *
+ * Furthermore, as elucidated in the last example, the copy of the
+ * class instance (owned by GeneralDataStorage) that is being pointed to
+ * is no longer alive when the reset() function is called, or when it is
+ * removed via a call to remove_object_with_name().
+ *
+ * @code
+ * GeneralDataStorage data;
+ * double* ptr_to_some_number = null_ptr;
+ *
+ * {
+ * const double some_number = ...;
+ * data.add_unique_copy("value", some_number);
+ *
+ * ptr_to_some_number = &(data.get_object_with_name<double>("value"));
+ *
+ * // The copy to some_number would go out of scope when either of
+ * // following two calls are made:
+ * data.remove_object_with_name("value");
+ * data.reset();
+ * }
+ *
+ * const double some_other_number
+ * = *ptr_to_some_number; // Invalid call
+ * @endcode
+ */
+ void
+ reset();
+
+ /**
+ * @name Data storage and access
+ */
+ //@{
+
+ /**
+ * Store internally a copy of the given object. The copied object is
+ * owned by this class, and is accessible via reference through the
+ * get_object_with_name() method.
+ *
+ * This function ensures that no @p entry with the given @name is
+ * already stored by this class instance.
+ */
+ template <typename Type>
+ void
+ add_unique_copy(const std::string &name, const Type &entry);
+
+ /**
+ * Store internally a copy of the given object. The copied object is
+ * owned by this class, and is accessible via reference through the
+ * get_object_with_name() method.
+ *
+ * This function does not perform any checks to ensure that the @p entry
+ * with the given @name is already stored by this class instance. If the
+ * @p name does in fact point to existing data, then this is overwritten.
+ */
+ template <typename Type>
+ void
+ add_or_overwrite_copy(const std::string &name, const Type &entry);
+
+ /**
+ * Add a reference to an already existing object. The object is not
+ * owned by this class, and the user has to guarantee that the
+ * referenced object lives longer than this class instance. The stored
+ * reference is accessible through the get_object_with_name() method.
+ *
+ * This function ensures that no @p entry with the given @name is
+ * already stored by this class instance.
+ */
+ template <typename Type>
+ void
+ add_unique_reference(const std::string &name, Type &entry);
+
+ /**
+ * Add a reference to an already existing object. The object is not
+ * owned by this class, and the user has to guarantee that the
+ * referenced object lives longer than this class instance. The stored
+ * reference is accessible through the get_object_with_name() method.
+ *
+ * This function does not perform any checks to ensure that the @p entry
+ * with the given @name is already stored by this class instance. If the
+ * @p name does in fact point to existing data, then this is overwritten.
+ */
+ template <typename Type>
+ void
+ add_or_overwrite_reference(const std::string &name, Type &entry);
+
+ /**
+ * Return a reference to the object with given name. If the object does
+ * not exist, then the input @p arguments will be used to construct an object
+ * of the given @p Type and a reference to this new object then be returned.
+ *
+ * A copy of an object of type @p Type , which is owned by this class
+ * instance, is generated by calling its constructor with the given set of
+ * arguments. For this function, the @p arguments are passed as
+ * <tt>lvalue</tt> references.
+ */
+ template <typename Type, typename... Args>
+ Type &
+ get_or_add_object_with_name(const std::string &name, Args &... arguments);
+
+ /**
+ * Return a reference to the object with given name. If the object does
+ * not exist, then the input @p arguments will be used to construct an object
+ * of the given @p Type and a reference to this new object then be returned.
+ *
+ * A copy of an object of type @p Type , which is owned by this class
+ * instance, is generated by calling its constructor with the given set of
+ * arguments. In contrast to the previous function of the same name, for
+ * this function the @p arguments are passed as <tt>rvalue</tt> references.
+ */
+ template <typename Type, typename... Args>
+ Type &
+ get_or_add_object_with_name(const std::string &name, Args &&... arguments);
+
+ /**
+ * Return a reference to the object with given name.
+ *
+ * This function throws an exception if either an object with the given name
+ * is not stored in this class, or if the object with the given name is
+ * neither of the exact specified @p Type nor a pointer to the @p Type.
+ */
+ template <typename Type>
+ Type &
+ get_object_with_name(const std::string &name);
+
+ /**
+ * Return a constant reference to the object with the given name.
+ *
+ * This function throws an exception if either an object with the given name
+ * is not stored in this class, or if the object with the given name is
+ * neither of the exact specified @p Type nor a pointer to the @p Type.
+ */
+ template <typename Type>
+ const Type &
+ get_object_with_name(const std::string &name) const;
+
+ /**
+ * Find out if we store an object with given name.
+ */
+ bool
+ stores_object_with_name(const std::string &name) const;
+
+ /**
+ * Find out if we store an object with given name.
+ */
+ void
+ remove_object_with_name(const std::string &name);
+
+ //@}
+
+ /**
+ * An entry with this name does not exist in the internal boost::any map.
+ */
+ DeclException1(ExcNameNotFound,
+ std::string,
+ << "No entry with the name " << arg1 << " exists.");
+
+ /**
+ * An entry with this name does not exist in the internal boost::any map.
+ */
+ DeclException1(ExcNameHasBeenFound,
+ std::string,
+ << "An entry with the name " << arg1 << " already exists.");
+
+ /**
+ * The requested type and the stored type are different.
+ */
+ DeclException3(ExcTypeMismatch,
+ std::string,
+ const char *,
+ const char *,
+ << "The stored type for entry with name \"" << arg1 << "\" is "
+ << arg2 << " but you requested type " << arg3 << ".");
+
+private:
+ /**
+ * Arbitrary user data, identified by a string.
+ */
+ std::map<std::string, boost::any> any_data;
+};
+
+
+/*----------------- Inline and template methods -----------------*/
+
+
+#ifndef DOXYGEN
+
+
+template <class Stream>
+void
+GeneralDataStorage::print_info(Stream &os)
+{
+ for (auto it : any_data)
+ {
+ os << it.first << '\t' << '\t'
+ << boost::core::demangle(it.second.type().name()) << std::endl;
+ }
+}
+
+
+template <typename Type>
+void
+GeneralDataStorage::add_unique_copy(const std::string &name, const Type &entry)
+{
+ Assert(!stores_object_with_name(name), ExcNameHasBeenFound(name));
+ add_or_overwrite_copy(name, entry);
+}
+
+
+template <typename Type>
+void
+GeneralDataStorage::add_or_overwrite_copy(const std::string &name,
+ const Type & entry)
+{
+ any_data[name] = entry;
+}
+
+
+template <typename Type>
+void
+GeneralDataStorage::add_unique_reference(const std::string &name, Type &entry)
+{
+ Assert(!stores_object_with_name(name), ExcNameHasBeenFound(name));
+ add_or_overwrite_reference(name, entry);
+}
+
+
+template <typename Type>
+void
+GeneralDataStorage::add_or_overwrite_reference(const std::string &name,
+ Type & entry)
+{
+ Type *ptr = &entry;
+ any_data[name] = ptr;
+}
+
+
+template <typename Type>
+Type &
+GeneralDataStorage::get_object_with_name(const std::string &name)
+{
+ Assert(stores_object_with_name(name), ExcNameNotFound(name));
+
+ Type *p = nullptr;
+
+ if (any_data[name].type() == typeid(Type *))
+ {
+ p = boost::any_cast<Type *>(any_data[name]);
+ }
+ else if (any_data[name].type() == typeid(Type))
+ {
+ p = boost::any_cast<Type>(&any_data[name]);
+ }
+ else
+ {
+ Assert(false,
+ ExcTypeMismatch(name,
+ any_data[name].type().name(),
+ typeid(Type).name()));
+ }
+
+ return *p;
+}
+
+
+template <typename Type>
+const Type &
+GeneralDataStorage::get_object_with_name(const std::string &name) const
+{
+ AssertThrow(stores_object_with_name(name), ExcNameNotFound(name));
+
+ const auto it = any_data.find(name);
+
+ if (it->second.type() == typeid(Type *))
+ {
+ const Type *p = boost::any_cast<Type *>(it->second);
+ return *p;
+ }
+ else if (it->second.type() == typeid(Type))
+ {
+ const Type *p = boost::any_cast<Type>(&it->second);
+ return *p;
+ }
+ else
+ {
+ Assert(false,
+ ExcTypeMismatch(name,
+ it->second.type().name(),
+ typeid(Type).name()));
+ const Type *p = nullptr;
+ return *p;
+ }
+}
+
+
+template <typename Type, typename... Args>
+Type &
+GeneralDataStorage::get_or_add_object_with_name(const std::string &name,
+ Args &... arguments)
+{
+ if (!stores_object_with_name(name))
+ add_unique_copy(name, Type(arguments...));
+
+ return get_object_with_name<Type>(name);
+}
+
+
+template <typename Type, typename... Args>
+Type &
+GeneralDataStorage::get_or_add_object_with_name(const std::string &name,
+ Args &&... arguments)
+{
+ if (!stores_object_with_name(name))
+ add_unique_copy(name, Type(std::forward<Args>(arguments)...));
+
+ return get_object_with_name<Type>(name);
+}
+
+
+#endif // DOXYGEN
+
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // dealii_algorithms_general_data_storage_h
--- /dev/null
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2019 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+
+// Test the core functionality of the GeneralDataStorage class
+
+
+#include <deal.II/algorithms/general_data_storage.h>
+
+#include <utility>
+
+#include "../tests.h"
+
+int
+main()
+{
+ initlog();
+
+ GeneralDataStorage data;
+
+ deallog << "Add by copy" << std::endl;
+ {
+ data.reset();
+
+ // Create new data instance
+ const double val_1 = 1.0;
+ data.add_unique_copy("value", val_1);
+ const double &val_2 = data.get_object_with_name<double>("value");
+ Assert(data.stores_object_with_name("value"), ExcInternalError());
+ Assert(val_2 == val_1, ExcInternalError());
+ Assert(&val_2 != &val_1, ExcInternalError());
+
+ // Allowed overwrite of existing data
+ const double val_3 = 2.0;
+ data.add_or_overwrite_copy("value", val_3);
+ const double &val_4 = data.get_object_with_name<double>("value");
+ Assert(data.stores_object_with_name("value"), ExcInternalError());
+ Assert(val_4 == val_3, ExcInternalError());
+ Assert(&val_4 != &val_3, ExcInternalError());
+
+ // Create new data instance using alternative function
+ const double val_5 = 3.0;
+ data.add_or_overwrite_copy("value_2", val_5);
+ const double &val_6 = data.get_object_with_name<double>("value_2");
+ Assert(data.stores_object_with_name("value_2"), ExcInternalError());
+ Assert(val_6 == val_5, ExcInternalError());
+ Assert(&val_6 != &val_5, ExcInternalError());
+
+ deallog << "Size: " << data.size() << std::endl;
+ }
+
+ deallog << "Add by reference" << std::endl;
+ {
+ data.reset();
+
+ // Create new data instance
+ const double val_1 = 1.0;
+ data.add_unique_reference("value", val_1);
+ const double &val_2 = data.get_object_with_name<const double>("value");
+ Assert(data.stores_object_with_name("value"), ExcInternalError());
+ Assert(val_2 == val_1, ExcInternalError());
+ Assert(&val_2 == &val_1, ExcInternalError());
+
+ // Allowed overwrite of existing data
+ const double val_3 = 2.0;
+ data.add_or_overwrite_reference("value", val_3);
+ const double &val_4 = data.get_object_with_name<const double>("value");
+ Assert(data.stores_object_with_name("value"), ExcInternalError());
+ Assert(val_4 == val_3, ExcInternalError());
+ Assert(&val_4 == &val_3, ExcInternalError());
+
+ // Create new data instance using alternative function
+ const double val_5 = 3.0;
+ data.add_or_overwrite_reference("value_2", val_5);
+ const double &val_6 = data.get_object_with_name<const double>("value_2");
+ Assert(data.stores_object_with_name("value_2"), ExcInternalError());
+ Assert(val_6 == val_5, ExcInternalError());
+ Assert(&val_6 == &val_5, ExcInternalError());
+
+ deallog << "Size: " << data.size() << std::endl;
+ }
+
+ deallog << "Add or construct" << std::endl;
+ {
+ data.reset();
+
+ using Type = std::pair<double, double>;
+
+ // Create new data instance
+ const Type &val_1 =
+ data.get_or_add_object_with_name<Type>("value", 1.0, 2.0);
+ Assert(data.stores_object_with_name("value"), ExcInternalError());
+ Assert(val_1 == Type({1.0, 2.0}), ExcInternalError());
+
+ // Should not overwrite existing data
+ const Type &val_2 =
+ data.get_or_add_object_with_name<Type>("value", Type(3.0, 4.0));
+ Assert(data.stores_object_with_name("value"), ExcInternalError());
+ Assert(val_2 == Type({1.0, 2.0}), ExcInternalError());
+
+ deallog << "Size: " << data.size() << std::endl;
+ }
+
+ deallog << "Merge" << std::endl;
+ {
+ data.reset();
+
+ const double val_1 = 1.0;
+ data.add_unique_copy("value", val_1);
+
+ GeneralDataStorage data_2;
+ data_2.add_unique_copy("value", 2.0); // Duplicate
+ data_2.add_unique_copy("value_2", 3.0);
+
+ deallog << "Data pre-merge:" << std::endl;
+ data.print_info(deallog);
+ deallog << "Size: " << data.size() << std::endl;
+ deallog << "Data 2 pre-merge:" << std::endl;
+ data_2.print_info(deallog);
+ deallog << "Size: " << data_2.size() << std::endl;
+
+ data.merge(data_2);
+
+ deallog << "Data post-merge:" << std::endl;
+ data.print_info(deallog);
+ deallog << "Size: " << data.size() << std::endl;
+ }
+
+
+ deal_II_exceptions::disable_abort_on_exception();
+
+ deallog << "Try to overwrite existing entry: Copy" << std::endl;
+ {
+ data.reset();
+
+ const double val_1 = 1.0;
+ data.add_unique_copy("value", val_1);
+
+ try
+ {
+ const double val_2 = 1.0;
+ data.add_unique_copy("value", val_2);
+ }
+ catch (const GeneralDataStorage::ExcNameHasBeenFound &exc)
+ {
+ deallog << exc.what() << std::endl;
+ }
+ }
+
+ deallog << "Try to overwrite existing entry: Reference" << std::endl;
+ {
+ data.reset();
+
+ const double val_1 = 1.0;
+ data.add_unique_reference("value", val_1);
+
+ try
+ {
+ const double val_2 = 2.0;
+ data.add_unique_reference("value", val_2);
+ }
+ catch (const GeneralDataStorage::ExcNameHasBeenFound &exc)
+ {
+ deallog << exc.what() << std::endl;
+ }
+ }
+
+ deallog << "Fetch non-existing entry" << std::endl;
+ {
+ data.reset();
+
+ try
+ {
+ data.get_object_with_name<double>("value");
+ }
+ catch (const GeneralDataStorage::ExcNameNotFound &exc)
+ {
+ deallog << exc.what() << std::endl;
+ }
+ }
+
+ deallog << "Access removed entry (reference)" << std::endl;
+ {
+ data.reset();
+
+ const double val_1 = 1.0;
+ data.add_unique_reference("value", val_1);
+ data.remove_object_with_name("value");
+
+ try
+ {
+ data.get_object_with_name<double>("value");
+ }
+ catch (const GeneralDataStorage::ExcNameNotFound &exc)
+ {
+ deallog << exc.what() << std::endl;
+ }
+ }
+
+ deallog << "Access removed entry (copy)" << std::endl;
+ {
+ data.reset();
+
+ data.add_unique_copy("value", 1.0);
+ data.remove_object_with_name("value");
+
+ try
+ {
+ data.get_object_with_name<double>("value");
+ }
+ catch (const GeneralDataStorage::ExcNameNotFound &exc)
+ {
+ deallog << exc.what() << std::endl;
+ }
+ }
+}