From 1bfb5d463d50fd035e7d05e6b8c3d966cbd4f99c Mon Sep 17 00:00:00 2001 From: Seung Hyun Kim Date: Fri, 12 Jul 2024 22:28:55 -0500 Subject: [PATCH] files: block headers --- backend/src/Systems/Block/Block/Aliases.hpp | 80 ++ .../src/Systems/Block/Block/AsVariables.hpp | 39 + .../src/Systems/Block/Block/BlockFacade.hpp | 466 ++++++++++ .../src/Systems/Block/Block/BlockIterator.hpp | 617 +++++++++++++ .../src/Systems/Block/Block/BlockSlice.hpp | 430 +++++++++ .../Systems/Block/Block/BlockSliceFacade.hpp | 476 ++++++++++ backend/src/Systems/Block/Block/BlockView.hpp | 708 ++++++++++++++ .../Systems/Block/Block/BlockViewFacade.hpp | 503 ++++++++++ backend/src/Systems/Block/Block/Metadata.hpp | 309 +++++++ backend/src/Systems/Block/Block/Protocols.hpp | 93 ++ .../src/Systems/Block/Block/TypeTraits.hpp | 862 ++++++++++++++++++ .../src/Systems/Block/Block/VariableCache.hpp | 249 +++++ .../Systems/Block/BlockVariables/Aliases.hpp | 126 +++ .../Block/BlockVariables/BlockInitializer.hpp | 576 ++++++++++++ .../Block/BlockVariables/BlockVariables.hpp | 464 ++++++++++ .../Block/BlockVariables/Protocols.hpp | 92 ++ .../Block/BlockVariables/TypeTraits.hpp | 257 ++++++ .../Systems/Block/BlockVariables/Types.hpp | 44 + backend/src/Systems/Block/Protocols.hpp | 32 + backend/src/Systems/Block/Serialize.hpp | 56 ++ 20 files changed, 6479 insertions(+) create mode 100644 backend/src/Systems/Block/Block/Aliases.hpp create mode 100644 backend/src/Systems/Block/Block/AsVariables.hpp create mode 100644 backend/src/Systems/Block/Block/BlockFacade.hpp create mode 100644 backend/src/Systems/Block/Block/BlockIterator.hpp create mode 100644 backend/src/Systems/Block/Block/BlockSlice.hpp create mode 100644 backend/src/Systems/Block/Block/BlockSliceFacade.hpp create mode 100644 backend/src/Systems/Block/Block/BlockView.hpp create mode 100644 backend/src/Systems/Block/Block/BlockViewFacade.hpp create mode 100644 backend/src/Systems/Block/Block/Metadata.hpp create mode 100644 backend/src/Systems/Block/Block/Protocols.hpp create mode 100644 backend/src/Systems/Block/Block/TypeTraits.hpp create mode 100644 backend/src/Systems/Block/Block/VariableCache.hpp create mode 100644 backend/src/Systems/Block/BlockVariables/Aliases.hpp create mode 100644 backend/src/Systems/Block/BlockVariables/BlockInitializer.hpp create mode 100644 backend/src/Systems/Block/BlockVariables/BlockVariables.hpp create mode 100644 backend/src/Systems/Block/BlockVariables/Protocols.hpp create mode 100644 backend/src/Systems/Block/BlockVariables/TypeTraits.hpp create mode 100644 backend/src/Systems/Block/BlockVariables/Types.hpp create mode 100644 backend/src/Systems/Block/Protocols.hpp create mode 100644 backend/src/Systems/Block/Serialize.hpp diff --git a/backend/src/Systems/Block/Block/Aliases.hpp b/backend/src/Systems/Block/Block/Aliases.hpp new file mode 100644 index 00000000..a7f39df0 --- /dev/null +++ b/backend/src/Systems/Block/Block/Aliases.hpp @@ -0,0 +1,80 @@ +#pragma once + +namespace blocks { + + //============================================================================ + // + // ALIAS DECLARATIONS + // + //============================================================================ + + //**************************************************************************** + /*!\brief Gets the nested `Variables` from `T` + * \ingroup block_tt + * + * The variables_t alias declaration provides a convenient + * shortcut to access the nested `Variables` type definition of + * the given type \a T. The following code example shows both ways to access + * the nested type definition: + * + * \example + * \code + * using Type1 = typename T::Variables; + * using Type2 = variables_t; + * \endcode + * + * \see Block, BlockSlice + */ + // [variables_t] + template + using variables_t = typename T::Variables; + // [variables_t] + //**************************************************************************** + + //**************************************************************************** + /*!\brief Gets the nested `InitializedVariables` from `T` + * \ingroup block_tt + * + * The initialized_variables_t alias declaration provides a convenient + * shortcut to access the nested `InitializedVariables` type definition of + * the given type \a T. The following code example shows both ways to access + * the nested type definition: + * + * \example + * \code + * using Type1 = typename T::InitializedVariables; + * using Type2 = initialized_variables_t; + * \endcode + * + * \see Block, BlockSlice + */ + // [initialized_variables_t] + template + using initialized_variables_t = typename T::InitializedVariables; + // [initialized_variables_t] + //**************************************************************************** + + //**************************************************************************** + /*!\brief Gets the nested `ComputedVariables` from `T` + * \ingroup block_tt + * + * The computed_variables_t alias declaration provides a convenient + * shortcut to access the nested `ComputedVariables` type definition of + * the given type \a T. The following code example shows both ways to access + * the nested type definition: + * + * \example + * \code + * using Type1 = typename T::ComputedVariables; + * using Type2 = computed_variables_t; + * \endcode + * + * \see Block, BlockSlice + */ + // [computed_variables_t] + template + using computed_variables_t = typename T::ComputedVariables; + // [computed_variables_t] + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/AsVariables.hpp b/backend/src/Systems/Block/Block/AsVariables.hpp new file mode 100644 index 00000000..af12673a --- /dev/null +++ b/backend/src/Systems/Block/Block/AsVariables.hpp @@ -0,0 +1,39 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Utilities/AsTaggedTuple.hpp" + +namespace blocks { + + //**************************************************************************** + /*!\brief Metafunction to define storage a typelist of block variables + * \ingroup blocks + * + * \details + * The as_block_variables template type helps obtain the heterogeneous + * variable storage container from a typelist of declared blocks::Variable, + * used as shown below. + * + * \example + * \code + * // ... Declare variables Var1, Var2, Var3 + * + * // TypeList of all variables + * using VariablesList = tmpl::list; + * + * // Convert into a storage type which has value semantics + * using BlockVariables = as_block_variables; + * \endcode + * + * \tparam L typelist of block::Variables + * + * \see blocks::Variables, Block + */ + template + using as_block_variables = tmpl::as_tagged_tuple; + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockFacade.hpp b/backend/src/Systems/Block/Block/BlockFacade.hpp new file mode 100644 index 00000000..c8863612 --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockFacade.hpp @@ -0,0 +1,466 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/Block/Block/Types.hpp" +// +#include "Systems/Block/Block/Concepts.hpp" +// +#include "Systems/Block/Block/Aliases.hpp" +#include "Systems/Block/Block/AsVariables.hpp" +#include "Systems/Block/BlockVariables/TypeTraits.hpp" +// +#include "Utilities/CRTP.hpp" +#include "Utilities/Requires.hpp" +#include "Utilities/TypeTraits/Cpp17.hpp" +#include "Utilities/TypeTraits/IsDetected.hpp" +// +#include + +namespace blocks { + + namespace detail { + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Fallback for when `Parent` does not require InitializedVariables + * in a BlockFacade + * \ingroup blocks + */ + struct EmptyInitializedVariables { + using InitializedVariables = tmpl::list<>; + }; + /*! \endcond */ + //************************************************************************** + + //************************************************************************ + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Signature of expected a derived Block + * \ingroup blocks + * + * Very similar to protocols, but we don't need to derive from the + * protocol class. Rather, signatures rely on an implicit interface + * guarantee. + * + * \tparam BlockImplementation The block that derives from BlockFacade + */ + template + struct Signatures { + /// [slice_signature] + template + using slice_signature = + decltype(std::declval().template slice( + std::declval())); + // This is equivalent to + // template + // // some_return_type + // slice(std::size_t index) & + // { + // // return the slice at the location index + // } + /// [slice_signature] + + /// [const_slice_signature] + template + using const_slice_signature = + decltype(std::declval() + .template slice(std::declval())); + // This is equivalent to + // template + // // some_return_type + // slice(std::size_t index) const & + // { + // // return the slice at the location index + // } + /// [const_slice_signature] + }; + /*! \endcond */ + //************************************************************************ + + } // namespace detail + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief A helper facade for ease in implementing new Block types + * \ingroup blocks + * + * \details + * The BlockFacade is a helper template to assist in conforming to the + * protocols expected by a Block class, when specializing it for + * particular Plugin types. It provides the interface of a block in terms of a + * few core functions and associated types, which are to be supplied by a + * Block that derives from BlockFacade. Internally, BlockFacade uses the CRTP + * pattern to access the core functions supplied by the Block. + * + * BlockFacade is modeled after boost::iterator_facade. Notably, it differs + * from boost::iterator_facade in one key aspect : the BlockFacade is + * internally templated on blocks::Block---hence its use for anything + * other then implementing a specialization of blocks::Block template is + * ill-formed by definition. + * + * \usage + * While customizing a blocks::block for some type `CustomPlugin` (which needs + * to meet the expected interface for a valid `Plugin`, see Blocks), we can + * use BlockFacade to minimize the code we need to write, as shown below: + * \snippet Test_BlockFacade.cpp block_facade_usage + * For correct use with a BlockFacade, the following members in the deriving + * Block need to be defined. + * \snippet this slice_signature + * \snippet this const_slice_signature + * + * where + * - `Tag` is the tag to be retrieved + * + * \example + * For a simple custom Plugin as shown below, + * \snippet Test_BlocksFramework.cpp int_plugin + * Without a BlockFacade, one needs to write out + * \snippet Test_BlocksFramework.cpp int_block + * With a BlockFacade however, this process becomes more simpler + * \snippet Test_BlocksFramework.cpp fac_int_block + * + * Additionally for more practical, operate-intensive Plugins in \elastica, + * such as Cosserat rods, BlockFacade takes care of setting up the necessary + * type definitions for slicing/initialization among others, including setting + * up subscript operators etc given the Block has the necessary functions + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see Block + */ + template + class BlockFacade : public elastica::CRTPHelper, BlockFacade>, + public Gettable>, + public Spannable>, + public Plugin { + protected: + //**Type definitions******************************************************** + /*! \cond ELASTICA_INTERNAL */ + //! Tag marking need for the block to be initialized + using to_be_initialized_tag = std::true_type; + //! Tag marking no need for block to be initialized + using need_not_be_initialized_tag = std::false_type; + /*! \endcond */ + //************************************************************************** + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Compile-time check for initialized variables in Plugin + * + * \details + * If the `Plugin` class has no initialized_variables_t alias, + * meaning that there are no variables to initialized, we can simply skip + * initialization, and the `Plugin` doesn't need to initialize these members + * and initialization can be skipped. This check for skipping can happen in + * compile-time and is facilitated by this function. + * + * \metareturns cpp17::bool_constant + */ + static inline constexpr auto parent_to_be_initialized() noexcept { + // This one-liner doesn't work if the initialized_variables_t is protected + // which is the intended use case + // return ::tt::is_detected{}; + return ::tt::is_detected{}; + } + /*! \endcond */ + //************************************************************************** + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Compile-time check for initialized variables in Plugin + * + * \metareturns cpp17::bool_constant + * + * \see parent_to_be_initialized() + */ + static inline constexpr auto parent_need_not_be_initialized() noexcept { + return cpp17::negation{}; + } + /*! \endcond */ + //************************************************************************** + + private: + //**Type definitions******************************************************** + //! Parent type + using Parent = Plugin; + //! This type + using This = BlockFacade; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! Plugin type + using PluginType = Parent; + //! List of variables + using typename Parent::Variables; + //! List of initialized variables + using InitializedVariables = blocks::initialized_variables_t< + std::conditional_t>; + //! Container for all variables + using BlockVariables = as_block_variables; + //! Conformant mapping between tags and variables + using VariableMap = VariableMapping; + //************************************************************************** + + private: + //**Type definitions******************************************************** + //! CRTP Type + using CRTP = elastica::CRTPHelper, BlockFacade>; + //! CRTP methods + using CRTP::self; + //! Type of gettable + using GetAffordance = Gettable>; + //! Type of spannable + using SpanAffordance = Spannable>; + //************************************************************************** + + protected: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief The default constructor. + * + */ + BlockFacade() noexcept( + std::is_nothrow_default_constructible::value) + : CRTP(), + GetAffordance(), + SpanAffordance(), + Parent(), + variables_(){}; + //************************************************************************** + + //************************************************************************** + /*!\brief The move constructor. + * + * \param other block to move from + */ + BlockFacade(BlockFacade&& other) noexcept + : CRTP(), + GetAffordance(), + SpanAffordance(), + Parent(), + variables_(std::move(other.variables_)){}; + //************************************************************************** + + public: + //************************************************************************** + /*!\brief Deleted copy constructor. + * + */ + BlockFacade(BlockFacade const&) = delete; + //************************************************************************** + + //@} + //************************************************************************** + + public: + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~BlockFacade() = default; + //@} + //************************************************************************** + + public: + //**Access operators******************************************************** + /*!\name Access operators */ + //@{ + //! Operator for slicing and viewing + using SpanAffordance::operator[]; + //@} + //************************************************************************** + + public: + //**Data access************************************************************* + /*!\name Data access */ + //@{ + + //************************************************************************** + /*!\brief Access to the underlying data + * + * \return Mutable lvalue reference to the underlying data + */ + inline constexpr BlockVariables& data() & noexcept { return variables_; } + //************************************************************************** + + //************************************************************************** + /*!\brief Access to the underlying data + * + * \return Constant lvalue reference to the underlying data + */ + inline constexpr BlockVariables const& data() const& noexcept { + return variables_; + } + //************************************************************************** + + //************************************************************************** + /*!\brief Access to the underlying data + * + * \return Mutable rvalue reference to the underlying data + */ + inline constexpr BlockVariables&& data() && noexcept { + return static_cast(variables_); + } + + //************************************************************************** + + //************************************************************************** + /*!\brief Access to the underlying data + * + * \return Const rvalue reference to the underlying data + */ + inline constexpr BlockVariables const&& data() const&& noexcept { + return static_cast(variables_); + } + //************************************************************************** + + //@} + //************************************************************************** + + //**Utility methods********************************************************* + /*!\name Utility methods*/ + //@{ + + //************************************************************************** + /*!\brief Returns the current block + */ + inline constexpr auto parent() & noexcept -> Block& { + return self(); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Returns the current block + */ + inline constexpr auto parent() const& noexcept -> Block const& { + return self(); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Returns the current block + */ + inline constexpr auto parent() && noexcept -> Block&& { + return static_cast&&>(*this); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Returns the current block + */ + inline constexpr auto parent() const&& noexcept -> Block const&& { + return static_cast const&&>(*this); + } + //************************************************************************** + + //@} + //************************************************************************** + + private: + //************************************************************************** + /*!\brief Implementation to initialize the underlying data + */ + template + static void initialize_impl(DownstreamBlock&& downstream_block, + Initializer&& initializer, + to_be_initialized_tag /* meta */) { + Parent::initialize(std::forward(downstream_block), + std::forward(initializer)); + } + //************************************************************************** + + //************************************************************************** + /*!\brief Implementation to initialize the underlying data + */ + template + static void initialize_impl(DownstreamBlock&&, Initializer&&, + need_not_be_initialized_tag /* meta */) {} + //************************************************************************** + + protected: + //************************************************************************** + /*!\brief Initialize the underlying data + */ + template + static void initialize(DownstreamBlock&& downstream_block, + Initializer&& initializer) { + This::initialize_impl(std::forward(downstream_block), + std::forward(initializer), + This::parent_to_be_initialized()); + } + //************************************************************************** + + private: + //**Member variables******************************************************** + /*!\name Member variables */ + //@{ + //! All block variables + BlockVariables variables_; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name Get functions for Block */ + //@{ + + //**************************************************************************** + /*!\brief Extract element from a Block + * \ingroup blocks + * + * \details + * Extracts the element of the Block `block_like` whose tag type is `Tag`. + * Fails to compile unless the block has the `Tag` being extracted. + * + * \usage + * The usage is similar to std::get(), shown below + * \code + Block<...> b; + auto my_tag_data = blocks::get(b); + * \endcode + * + * \tparam Tag Tag to extract + * + * \param block_like The block to extract the tag from + */ + template + inline constexpr decltype(auto) get_backend(Block& block) noexcept { + return tuples::get(block.data()); + } + + template + inline constexpr decltype(auto) get_backend( + Block const& block) noexcept { + return tuples::get(block.data()); + } + + template + inline constexpr decltype(auto) get_backend(Block&& block) noexcept { + return tuples::get( + static_cast&&>(block).data()); + } + + template + inline constexpr decltype(auto) get_backend( + Block const&& block) noexcept { + return tuples::get( + static_cast const&&>(block).data()); + } + + //@} + /*! \endcond */ + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockIterator.hpp b/backend/src/Systems/Block/Block/BlockIterator.hpp new file mode 100644 index 00000000..0b3b8aba --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockIterator.hpp @@ -0,0 +1,617 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include +#include +#include + +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/Block/Types.hpp" +// + +namespace blocks { + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Implementation of a iterator for blocks. + * \ingroup blocks + * + * The BlockIterator represents a generic random-access iterator that can be + * used for all block types in \elastica + */ + template + class BlockIterator { + private: + //**Type definitions******************************************************** + using ParentBlock = Block; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! The iterator category. + using IteratorCategory = std::random_access_iterator_tag; + //! Type of the underlying elements. + using ValueType = + typename PluginFrom::template to::type; + //! Pointer return type. + using PointerType = ValueType; + //! Reference return type. + using ReferenceType = ValueType; + //! Difference between two iterators. + using DifferenceType = std::size_t; + + // STL iterator requirements + //! The iterator category. + using iterator_category = IteratorCategory; + //! Type of the underlying elements. + using value_type = ValueType; + //! Pointer return type. + using pointer = PointerType; + //! Reference return type. + using reference = ReferenceType; + //! Difference between two iterators. + using difference_type = DifferenceType; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief Default constructor for the BlockIterator class. + */ + explicit constexpr BlockIterator() noexcept + : block_(nullptr), curr_idx_(std::numeric_limits::max()) {} + //************************************************************************** + + //************************************************************************** + /*!\brief Constructor for the BlockIterator class. + * + * \param ptr Pointer to the block + * \param idx Index of slicing + */ + constexpr BlockIterator(ParentBlock* ptr, std::size_t idx) noexcept + : block_(ptr), curr_idx_(idx) {} + BlockIterator(const BlockIterator&) = default; + //@} + //************************************************************************** + + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~BlockIterator() = default; + //@} + //************************************************************************** + + //**Assignment operators**************************************************** + /*!\name Assignment operators */ + //@{ + + //************************************************************************** + /*!\brief Addition assignment operator. + * + * \param inc The increment of the iterator. + * \return Reference to the incremented iterator. + */ + constexpr BlockIterator& operator+=(difference_type inc) noexcept { + curr_idx_ += inc; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Subtraction assignment operator. + * + * \param dec The decrement of the iterator. + * \return Reference to the decremented iterator. + */ + constexpr BlockIterator& operator-=(difference_type dec) noexcept { + // does this by any chance go beyond bounds? + curr_idx_ -= dec; + return *this; + }; + //************************************************************************** + + BlockIterator& operator=(const BlockIterator&) = default; + //@} + //************************************************************************** + + //**Increment/decrement operators******************************************* + /*!\name Increment/decrement operators */ + //@{ + + //************************************************************************** + /*!\brief Pre-increment operator. + * + * \return Reference to the incremented iterator. + */ + constexpr BlockIterator& operator++() noexcept { + ++curr_idx_; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Post-increment operator. + * + * \return The previous position of the iterator. + */ + constexpr const BlockIterator operator++(int) noexcept { + return BlockIterator(block_, curr_idx_++); + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Pre-decrement operator. + * + * \return Reference to the decremented iterator. + */ + constexpr BlockIterator& operator--() noexcept { + --curr_idx_; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Post-decrement operator. + * + * \return The previous position of the iterator. + */ + constexpr const BlockIterator operator--(int) noexcept { + return BlockIterator(block_, curr_idx_--); + }; + //************************************************************************** + + //@} + //************************************************************************** + + //**Access operators******************************************************** + /*!\name Access operators */ + //@{ + + //************************************************************************** + /*!\brief Direct access to the underlying slice. + * + * \param index Access index. + * \return Accessed slice. + */ + constexpr ReferenceType operator[](size_t index) const noexcept { + // A static cast is necessary to properly return tagged systems. Tagged + // systems may be implemented as deriving from a Block, in which case the + // slice method() returns a non-tagged reference, which is then converted + // to a tagged reference. + return static_cast(slice(*block_, curr_idx_ + index)); + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Direct access to the slice at the current iterator position. + * + * \return Current slice. + */ + constexpr ReferenceType operator*() const noexcept { + return static_cast(slice_backend(*block_, curr_idx_)); + }; + //@} + //************************************************************************** + + //**Utility functions******************************************************* + /*!\name Utility functions */ + //@{ + + //************************************************************************** + /*!\brief Low-level access to the underlying data of the iterator. + * + * \return Pointer to the parent block. + */ + constexpr ParentBlock* data() const noexcept { return block_; } + //************************************************************************** + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() & noexcept -> ParentBlock& { + return *block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const& noexcept -> ParentBlock const& { + return *block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is useful when xvalue block-slices are generated + * on-the-fly via BlockRefs. If this overload is not present, then the + * const& is picked up, and it is not possible to assign values to the + * parent block anymore. + */ + inline constexpr auto parent() && noexcept -> ParentBlock& { + return *block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is provided for symmetry with the overload above. + */ + inline constexpr auto parent() const&& noexcept -> ParentBlock const& { + return *block_; + } + //************************************************************************ + + //************************************************************************** + /*!\brief Low-level access to the index of the iterator. + * + * \return Index into the current slice location + */ + constexpr auto index() const noexcept -> std::size_t { return curr_idx_; } + //************************************************************************** + + //@} + //************************************************************************** + + private: + //**Member variables******************************************************** + /*!\name Member variables */ + //@{ + //! Pointer to the held block. + ParentBlock* block_; + //! Current slicing index + std::size_t curr_idx_; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //============================================================================ + // + // GLOBAL OPERATORS + // + //============================================================================ + + //**************************************************************************** + /*!\name BlockIterator operators */ + //@{ + + //**************************************************************************** + /*!\brief Equality comparison between two BlockIterator objects. + * + * \param lhs The left-hand side iterator. + * \param rhs The right-hand side iterator. + * \return \a true if the iterators refer to the same element, \a false if + * not. + */ + template + constexpr inline bool operator==(const BlockIterator& lhs, + const BlockIterator& rhs) noexcept { + return lhs.data() == rhs.data() and lhs.index() == rhs.index(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief InEquality comparison between two BlockIterator objects. + * + * \param lhs The left-hand side iterator. + * \param rhs The right-hand side iterator. + * \return \a true if the iterators don't refer to the same element, \a false + * if they do. + */ + template + constexpr inline bool operator!=(const BlockIterator& lhs, + const BlockIterator& rhs) noexcept { + return lhs.data() != rhs.data() or lhs.index() != rhs.index(); + } + //**************************************************************************** + + //@} + //**************************************************************************** + +} // namespace blocks + +namespace blocks { + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Implementation of a iterator for blocks. + * \ingroup blocks + * + * The ConstBlockIterator represents a generic random-access iterator that can + * be used for all block types in \elastica + */ + template + class ConstBlockIterator { + private: + //**Type definitions******************************************************** + using ParentBlock = Block; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! The iterator category. + using IteratorCategory = std::random_access_iterator_tag; + //! Type of the underlying elements. + using ValueType = + typename PluginFrom::template to::type; + //! Pointer return type. + using PointerType = ValueType; + //! Reference return type. + using ReferenceType = ValueType; + //! Difference between two iterators. + using DifferenceType = std::size_t; + + // STL iterator requirements + //! The iterator category. + using iterator_category = IteratorCategory; + //! Type of the underlying elements. + using value_type = ValueType; + //! Pointer return type. + using pointer = PointerType; + //! Reference return type. + using reference = ReferenceType; + //! Difference between two iterators. + using difference_type = DifferenceType; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief Default constructor for the ConstBlockIterator class. + */ + explicit constexpr ConstBlockIterator() noexcept + : block_(nullptr), curr_idx_(std::numeric_limits::max()) {} + //************************************************************************** + + //************************************************************************** + /*!\brief Constructor for the ConstBlockIterator class. + * + * \param ptr Pointer to the block + * \param idx Index of slicing + */ + constexpr ConstBlockIterator(ParentBlock const* ptr, + std::size_t idx) noexcept + : block_(ptr), curr_idx_(idx) {} + //************************************************************************** + + //************************************************************************** + /*!\brief Conversion constructor from a BlockIterator instance. + * + * \param it The BlockIterator instance to be copied. + */ + constexpr ConstBlockIterator(BlockIterator const& it) noexcept + : block_(it.data()), curr_idx_(it.index()){}; + //************************************************************************** + + ConstBlockIterator(const ConstBlockIterator&) = default; + //@} + //************************************************************************** + + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~ConstBlockIterator() = default; + //@} + //************************************************************************** + + //**Assignment operators**************************************************** + /*!\name Assignment operators */ + //@{ + + //************************************************************************** + /*!\brief Addition assignment operator. + * + * \param inc The increment of the iterator. + * \return Reference to the incremented iterator. + */ + constexpr ConstBlockIterator& operator+=(difference_type inc) noexcept { + curr_idx_ += inc; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Subtraction assignment operator. + * + * \param dec The decrement of the iterator. + * \return Reference to the decremented iterator. + */ + constexpr ConstBlockIterator& operator-=(difference_type dec) noexcept { + // does this by any chance go beyond bounds? + curr_idx_ -= dec; + return *this; + }; + //************************************************************************** + + ConstBlockIterator& operator=(const ConstBlockIterator&) = default; + //@} + //************************************************************************** + + //**Increment/decrement operators******************************************* + /*!\name Increment/decrement operators */ + //@{ + + //************************************************************************** + /*!\brief Pre-increment operator. + * + * \return Reference to the incremented iterator. + */ + constexpr ConstBlockIterator& operator++() noexcept { + ++curr_idx_; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Post-increment operator. + * + * \return The previous position of the iterator. + */ + constexpr const ConstBlockIterator operator++(int) noexcept { + return ConstBlockIterator(block_, curr_idx_++); + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Pre-decrement operator. + * + * \return Reference to the decremented iterator. + */ + constexpr ConstBlockIterator& operator--() noexcept { + --curr_idx_; + return *this; + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Post-decrement operator. + * + * \return The previous position of the iterator. + */ + constexpr const ConstBlockIterator operator--(int) noexcept { + return ConstBlockIterator(block_, curr_idx_--); + }; + //************************************************************************** + + //@} + //************************************************************************** + + //**Access operators******************************************************** + /*!\name Access operators */ + //@{ + + //************************************************************************** + /*!\brief Direct access to the underlying slice. + * + * \param index Access index. + * \return Accessed slice. + */ + constexpr ReferenceType operator[](size_t index) const noexcept { + return static_cast(slice(*block_, curr_idx_ + index)); + }; + //************************************************************************** + + //************************************************************************** + /*!\brief Direct access to the slice at the current iterator position. + * + * \return Current slice. + */ + constexpr ReferenceType operator*() const noexcept { + return static_cast(slice(*block_, curr_idx_)); + }; + //@} + //************************************************************************** + + //**Utility functions******************************************************* + /*!\name Utility functions */ + //@{ + + //************************************************************************** + /*!\brief Low-level access to the underlying data of the iterator. + * + * \return Pointer to the parent block. + */ + constexpr ParentBlock const* data() const noexcept { return block_; } + //************************************************************************** + + //************************************************************************** + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const noexcept -> ParentBlock const& { + return *block_; + } + //************************************************************************** + + //************************************************************************** + /*!\brief Low-level access to the index of the iterator. + * + * \return Index into the current slice location + */ + constexpr auto index() const noexcept -> std::size_t { return curr_idx_; } + //************************************************************************** + + //@} + //************************************************************************** + + private: + //**Member variables******************************************************** + /*!\name Member variables */ + //@{ + //! Pointer to the held (const) block. + ParentBlock const* block_; + //! Current slicing index + std::size_t curr_idx_; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //============================================================================ + // + // GLOBAL OPERATORS + // + //============================================================================ + + //**************************************************************************** + /*!\name ConstBlockIterator operators */ + //@{ + + //**************************************************************************** + /*!\brief Equality comparison between two ConstBlockIterator objects. + * + * \param lhs The left-hand side iterator. + * \param rhs The right-hand side iterator. + * \return \a true if the iterators refer to the same element, \a false if + * not. + */ + template + constexpr inline bool operator==( + const ConstBlockIterator& lhs, + const ConstBlockIterator& rhs) noexcept { + return lhs.data() == rhs.data() and lhs.index() == rhs.index(); + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief InEquality comparison between two ConstBlockIterator objects. + * + * \param lhs The left-hand side iterator. + * \param rhs The right-hand side iterator. + * \return \a true if the iterators don't refer to the same element, \a false + * if they do. + */ + template + constexpr inline bool operator!=( + const ConstBlockIterator& lhs, + const ConstBlockIterator& rhs) noexcept { + return lhs.data() != rhs.data() or lhs.index() != rhs.index(); + } + //**************************************************************************** + + //@} + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockSlice.hpp b/backend/src/Systems/Block/Block/BlockSlice.hpp new file mode 100644 index 00000000..685bef5f --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockSlice.hpp @@ -0,0 +1,430 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Systems/Block/Block/Types.hpp" +// +#include "Systems/Block/Block/AsVariables.hpp" +#include "Systems/Block/Block/BlockSliceFacade.hpp" +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/BlockVariables/TypeTraits.hpp" + +namespace blocks { + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Implementation of a slice/view on an \elastica Block + * \ingroup blocks + * + * \details + * The BlockSlice template class provides a slice (or) view into a Block of + * `data` and `operations`. It also models the `ComputationalBlock` concept + * and hence can be used across places where a \elastica Block is used as + * a template parameter. + * + * Similar to Block, BlockSlice can also be customized (specialized) for any + * Lagrangian entity, but experience suggests that extensive customization is + * not needed. For a concrete example, please see specializations of + * BlockSlice in @ref cosserat_rod. + * + * \usage + * The intended usage of a BlockSlice is when a view is required into the data + * held by a block---this is frequently the case when a user either + * 1. adds an entity into the Simulator, or + * 2. requires access (for e.g. reading/writing to disk) to only a portion of + * the block. + * The pattern that is most commonly seen in the use case (1) is for a + * BlockSlice templated on some `Plugin` type to be only used with a Block + * of the same `Plugin` type, when adding new entities to the Block. + * For use case (2) we suggest the user to the blocks::slice() function, which + * has an intuitive, explicit slice syntax, or even the subscript operator[]. + * We note that we might explicitly disable the subscript operator [] for + * slicing in the future, as the semantics are potentially unclear. + * + * Finally, with no additional coding effort, the BlockSlice has exactly the + * same operations as the mother Block (aka Block of the same `Plugin` type), + * but now it operates only on that slice of the data. This means that a + * BlockSlice can be used as a small Block in itself which greatly + * sbackendifies interfacing different components of \elastica---the user need + * not care or even know about whether the data that she has is a Block or a + * BlockSlice! For example, \code + * // ... make simulator etc ... + * auto my_rod = simulator.emplace_back( * ...args... *); + * // the args go and form a Block + * // which in turn returns a BlockSlice + * // which the user gets as my_rod + * + * // use my_rod like a regular cosserat rod + * simulator->constrain(my_rod)->using( *...args... *); + * \endcode + * This abstraction helped us constrain \c my_rod, embedded in a Block of + * data, using \c SomeConstraint just like any non-Blocked item of the + * \elastica library. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see Block, blocks::slice + */ + template + class BlockSlice : public detail::BlockSliceFacade, + public Gettable>, + public Plugin { + // NOTE : Plugin is inherited after Facade so that indices and references to + // the parent block is filled first. This is because some elements of Plugin + // may require that the slice (and index) be valid, for example to generate + // internal refs/pointers for time-stepping. + protected: + //**Type definitions******************************************************** + //! Type of the parent plugin + using Parent = detail::BlockSliceFacade; + //! Type of the slice + using This = BlockSlice; + //! Type of gettable + using GetAffordance = Gettable; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! Type of the parent plugin + using typename Parent::PluginType; + //! Type of the parent block + using typename Parent::ParentBlock; + //! Type of Variables + using typename Parent::Variables; + //************************************************************************** + + //**Friendships************************************************************* + //! Friend the main block + friend ParentBlock; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param index Index of the slice + */ + explicit BlockSlice(ParentBlock& parent_block, std::size_t index) noexcept + : Parent(parent_block, index), GetAffordance(), PluginType() {} + //************************************************************************** + + //************************************************************************** + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + BlockSlice(BlockSlice const& other) + : Parent(static_cast(other)), + GetAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //************************************************************************** + /*!\brief The move constructor. + * + * \param other slice to move from + */ + BlockSlice(BlockSlice&& other) noexcept + : Parent(static_cast(other)), + GetAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //@} + //************************************************************************** + + public: + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~BlockSlice() = default; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Implementation of a slice/view on a const \elastica Block + * \ingroup blocks + * + * \details + * The ConstBlockSlice template class provides a slice (or) view into a + * constant Block of \a data and \a operations. It also models the + * `ComputationalBlock` concept and hence can be used across places where a + * \elastica Block is used as a template parameter. + * + * Notably, it differs from BlockSlice in one key aspect : the underlying + * data/view is always constant and cannot be modified (it is only read only). + * Hence ConstBlock is useful for propagating const-correctness throughout the + * code. It can be used in places where one needs to pass (const)-data to the + * user which she can then copy and use it for her own purposes. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see BlockSlice + */ + template + class ConstBlockSlice : public detail::ConstBlockSliceFacade, + public Gettable>, + public Plugin { + // NOTE : Plugin is inherited after Facade so that indices and references to + // the parent block is filled first. This is because some elements of Plugin + // may require that the slice (and index) be valid, for example to generate + // internal refs/pointers for time-stepping. + protected: + //**Type definitions******************************************************** + //! Type of the parent plugin + using Parent = detail::ConstBlockSliceFacade; + //! Type of the slice + using This = ConstBlockSlice; + //! Type of gettable + using GetAffordance = Gettable; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! Type of the parent plugin + using typename Parent::PluginType; + //! Type of the parent block + using typename Parent::ParentBlock; + //! Type of Variables + using typename Parent::Variables; + //************************************************************************** + + //**Friendships************************************************************* + //! Friend the main block + friend ParentBlock; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param index Index of the slice + */ + ConstBlockSlice(ParentBlock const& parent_block, std::size_t index) noexcept + : Parent(parent_block, index), GetAffordance(), PluginType() {} + //************************************************************************** + + //************************************************************************** + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + ConstBlockSlice(ConstBlockSlice const& other) + : Parent(static_cast(other)), + GetAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //************************************************************************** + /*!\brief The move constructor. + * + * \param other slice to move from + */ + ConstBlockSlice(ConstBlockSlice&& other) noexcept + : Parent(static_cast(other)), + GetAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //@} + //************************************************************************** + + public: + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~ConstBlockSlice() = default; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //============================================================================ + // + // GLOBAL OPERATORS + // + //============================================================================ + + //**Equality operator********************************************************* + /*!\brief Equality comparison between two BlockSlice objects. + * + * \param lhs The left-hand side slice. + * \param rhs The right-hand side slice. + * \return \a true if the slices are same, else \a false + */ + template + inline constexpr auto operator==(BlockSlice const& lhs, + BlockSlice const& rhs) noexcept + -> bool { + return static_cast const&>(lhs) == + static_cast const&>(rhs); + } + //**************************************************************************** + + //**Equality operator********************************************************* + /*!\brief Equality comparison between two ConstBlockSlice objects. + * + * \param lhs The left-hand side const slice. + * \param rhs The right-hand side const slice. + * \return \a true if the const slices are same, else \a false + */ + template + inline constexpr auto operator==(ConstBlockSlice const& lhs, + ConstBlockSlice const& rhs) noexcept + -> bool { + return static_cast const&>(lhs) == + static_cast const&>(rhs); + } + //**************************************************************************** + + //============================================================================ + // + // GET FUNCTIONS + // + //============================================================================ + + namespace detail { + + template + inline constexpr decltype(auto) get_slice(SliceLike&& slice_like) noexcept { + return std::forward(slice_like) + .parent() + .template slice( + std::forward(slice_like).index()); + } + + } // namespace detail + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name Get functions for Slice types */ + //@{ + + //**************************************************************************** + /*!\brief Extract element from a BlockSlice + * \ingroup blocks + * + * \details + * Extracts the element of the Block `block_slice` whose tag type is `Tag`. + * Fails to compile unless the block has the `Tag` being extracted. + * + * \usage + * The usage is similar to std::get(), shown below + * \code + Block<...> b; + auto my_tag_data = blocks::get(b); + * \endcode + * + * \tparam Tag Tag to extract + * + * \param block_like The block to extract the tag from + */ + template + inline constexpr decltype(auto) get_backend( + BlockSlice& block_slice) noexcept { + return detail::get_slice(block_slice); + } + + template + inline constexpr decltype(auto) get_backend( + BlockSlice const& block_slice) noexcept { + return detail::get_slice(block_slice); + } + + template + inline constexpr decltype(auto) get_backend( + BlockSlice&& block_slice) noexcept { + return detail::get_slice( + static_cast&&>(block_slice)); + } + + template + inline constexpr decltype(auto) get_backend( + BlockSlice const&& block_slice) noexcept { + return detail::get_slice( + static_cast const&&>(block_slice)); + } + + template + inline constexpr decltype(auto) get_backend( + ConstBlockSlice const& block_slice) noexcept { + return detail::get_slice(block_slice); + } + //@} + /*! \endcond */ + //**************************************************************************** + + //============================================================================ + // + // SLICE FUNCTIONS + // + //============================================================================ + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name Block slice functions */ + //@{ + + //**************************************************************************** + /*!\brief Slice backend for Block + * \ingroup blocks + * + * \details + * Implements slices on entities modeling block concept. + * + * \param block_like Data-structure modeling the block concept + * \param index The index of the slice (int or from_end) + * + * \example + * Given a block `b`, we can slice it using + * \code + * auto b_slice = block::slice(b, 5UL); // at index 5 from start + * auto b_slice_from_end = block::slice(b, elastica::end - 2UL); // 2 from end + * \endcode + * + * \note + * not marked noexcept because we can have out of range indices + */ + template + inline decltype(auto) slice_backend(Block& block_like, + std::size_t index) noexcept { + using ReturnType = + typename PluginFrom>::template to::type; + return ReturnType{block_like, index}; + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(Block const& block_like, + std::size_t index) noexcept { + using ReturnType = + typename PluginFrom>::template to::type; + return ReturnType{block_like, index}; + } + //**************************************************************************** + + //@} + /*! \endcond */ + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockSliceFacade.hpp b/backend/src/Systems/Block/Block/BlockSliceFacade.hpp new file mode 100644 index 00000000..4bc36eae --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockSliceFacade.hpp @@ -0,0 +1,476 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/Block/Block/Types.hpp" +// +#include "Systems/Block/Block/AsVariables.hpp" +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/BlockVariables/TypeTraits.hpp" + +namespace blocks { + + namespace detail { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Implementation of a slice/view on an \elastica Block + * \ingroup blocks + * + * \details + * The BlockSliceFacade template class provides a slice (or) view into a + * Block of `data` and `operations`. It also models the `ComputationalBlock` + * concept and hence can be used across places where a \elastica Block is + * used as a template parameter. + * + * Similar to Block, BlockSliceFacade can also be customized (specialized) + * for any Lagrangian entity, but experience suggests that extensive + * customization is not needed. For a concrete example, please see + * specializations of BlockSliceFacade in @ref cosserat_rod + * + * \usage + * The intended usage of a BlockSliceFacade is when a view is required into + the data + * held by a block---this is frequently the case when a user either + * 1. adds an entity into the Simulator, or + * 2. requires access (for e.g. reading/writing to disk) to only a portion + of + * the block. + * The pattern that is most commonly seen in the use case (1) is for a + * BlockSliceFacade templated on some `Plugin` type to be only used with a + Block + * of the same `Plugin` type, when adding new entities to the Block. + * For use case (2) we suggest the user to the blocks::slice() function, + which + * has an intuitive, explicit slice syntax, or even the subscript + operator[]. + * We note that we might explicitly disable the subscript operator [] for + * slicing in the future, as the semantics are potentially unclear. + * + * Finally, with no additional coding effort, the BlockSliceFacade has + exactly the + * same operations as the mother Block (aka Block of the same `Plugin` + type), + * but now it operates only on that slice of the data. This means that a + * BlockSliceFacade can be used as a small Block in itself which greatly + simplifies + * interfacing different components of \elastica---the user need not care or + * even know about whether the data that she has is a Block or a + BlockSliceFacade! + * For example, + \code + // ... make simulator etc ... + auto my_rod = simulator.emplace_back( * ...args... *); + // the args go and form a Block + // which in turn returns a BlockSliceFacade + // which the user gets as my_rod + + // use my_rod like a regular cosserat rod + simulator->constrain(my_rod)->using( *...args... *); + \endcode + * This abstraction helped us constrain \c my_rod, embedded in a Block of + * data, using \c SomeConstraint just like any non-Blocked item of the + * \elastica library. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see Block, blocks::slice + */ + template + class BlockSliceFacade { + protected: + //**Type definitions****************************************************** + //! Type of the slice + using This = BlockSliceFacade; + //************************************************************************ + + public: + //**Type definitions****************************************************** + //! Type of the plugin + using PluginType = Plugin; + //! List of variables + using Variables = typename PluginType::Variables; + //! Typelist of all variable slices + using VariableSlicesList = typename as_slices::slices; + //! Values of all variable slices + using BlockVariableSlices = as_block_variables; + //! Typelist of all variable slices + using VariableConstSlicesList = + typename as_slices::const_slices; + //! Values of all variable slices + using BlockVariableConstSlices = + as_block_variables; + //! Conformant mapping between tags and variables + using VariableMap = VariableMapping; + //! Type of the parent block + using ParentBlock = + typename PluginFrom>::template to::type; + //************************************************************************ + + protected: + //**Constructors********************************************************** + /*!\name Constructors */ + //@{ + + //************************************************************************ + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param index Index of the slice + */ + BlockSliceFacade(ParentBlock& parent_block, std::size_t index) noexcept + : index_(index), parent_block_(parent_block) {} + //************************************************************************ + + //************************************************************************ + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + BlockSliceFacade(BlockSliceFacade const& other) noexcept + : index_(other.index_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //************************************************************************ + /*!\brief The move constructor. + * + * \param other slice to move from + */ + BlockSliceFacade(BlockSliceFacade&& other) noexcept + : index_(other.index_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //@} + //************************************************************************ + + public: + //**Destructor************************************************************ + /*!\name Destructor */ + //@{ + ~BlockSliceFacade() = default; + //@} + //************************************************************************ + + //**Utility methods******************************************************* + /*!\name Utility methods*/ + //@{ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() & noexcept -> ParentBlock& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const& noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is useful when xvalue block-slices are generated + * on-the-fly via BlockRefs. If this overload is not present, then the + * const& is picked up, and it is not possible to assign values to the + * parent block anymore. + * + * \note + * Returning a reference is valid as the parent block outlives the block + * slice. + */ + inline constexpr auto parent() && noexcept -> ParentBlock& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is provided for symmetry with the overload above. + * + * \note + * Returning a reference is valid as the parent block outlives the block + * slice. + */ + inline constexpr auto parent() const&& noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Returns the slice index + */ + inline constexpr auto index() const noexcept -> std::size_t { + return index_; + } + //************************************************************************ + + //@} + //************************************************************************ + + //**Data access*********************************************************** + /*!\name Data access */ + //@{ + protected: + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) & { + using RT = BlockVariableSlices; + return RT{parent().template slice(index())...}; + } + + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) const& { + using RT = BlockVariableConstSlices const; + return RT{parent().template slice(index())...}; + } + + public: + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Underlying data + */ + inline constexpr decltype(auto) data() & noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Constant lvalue reference to the underlying data + */ + inline constexpr decltype(auto) data() const& noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //@} + //************************************************************************ + + private: + //**Member variables****************************************************** + /*!\name Member variables */ + //@{ + //! Slice index + std::size_t index_; + //! Reference to the parent block + ParentBlock& parent_block_; + //@} + //************************************************************************ + }; + /*! \endcond */ + //************************************************************************** + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Implementation of a slice/view on a const \elastica Block + * \ingroup blocks + * + * \details + * The ConstBlockSliceFacade template class provides a slice (or) view into + * a constant Block of \a data and \a operations. It also models the + * `ComputationalBlock` concept and hence can be used across places where a + * \elastica Block is used as a template parameter. + * + * Notably, it differs from BlockSliceFacade in one key aspect : the + * underlying data/view is always constant and cannot be modified (it is + * only read only). Hence ConstBlock is useful for propagating + * const-correctness throughout the code. It can be used in places where one + * needs to pass (const)-data to the user which she can then copy and use it + * for her own purposes. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see BlockSliceFacade + */ + template // The computational plugin type + class ConstBlockSliceFacade { + protected: + //**Type definitions****************************************************** + //! Type of the slice + using This = ConstBlockSliceFacade; + //************************************************************************ + + public: + //**Type definitions****************************************************** + //! Type of the plugin + using PluginType = Plugin; + //! List of variables + using Variables = typename PluginType::Variables; + //! Typelist of all variable slices + using VariableSlicesList = typename as_slices::const_slices; + //! Values of all variable slices + using BlockVariableSlices = as_block_variables; + //! Conformant mapping between tags and variables + using VariableMap = VariableMapping; + //! Type of the parent block + using ParentBlock = typename PluginFrom< + ConstBlockSlice>::template to::type; + //************************************************************************ + + // ideally we make the constructors protected, but this interferes with + // nothrow specifications + // https://stackoverflow.com/questions/40441994/usage-of-noexcept-in-derived-classes + protected: + //**Constructors********************************************************** + /*!\name Constructors */ + //@{ + + //************************************************************************ + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param index Index of the slice + */ + ConstBlockSliceFacade(ParentBlock const& parent_block, + std::size_t index) noexcept + : index_(index), parent_block_(parent_block) {} + //************************************************************************ + + //************************************************************************ + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + ConstBlockSliceFacade(ConstBlockSliceFacade const& other) noexcept + : index_(other.index_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //************************************************************************ + /*!\brief The move constructor. + * + * \param other slice to move from + */ + ConstBlockSliceFacade(ConstBlockSliceFacade&& other) noexcept + : index_(other.index_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //@} + //************************************************************************ + + public: + //**Destructor************************************************************ + /*!\name Destructor */ + //@{ + ~ConstBlockSliceFacade() = default; + //@} + //************************************************************************ + + //**Utility methods******************************************************* + /*!\name Utility methods*/ + //@{ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Returns the slice index + */ + inline constexpr auto index() const noexcept -> std::size_t { + return index_; + } + //************************************************************************ + + //@} + //************************************************************************ + + //**Data access*********************************************************** + /*!\name Data access */ + //@{ + protected: + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) const& { + using RT = BlockVariableSlices; + return RT{parent().template slice(index())...}; + } + + public: + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Constant lvalue reference to the underlying data + */ + inline constexpr decltype(auto) data() const& noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //@} + //************************************************************************ + + private: + //**Member variables****************************************************** + /*!\name Member variables */ + //@{ + //! Slice index + std::size_t index_; + //! Reference to the parent block + ParentBlock const& parent_block_; + //@} + //************************************************************************ + }; + /*! \endcond */ + //************************************************************************** + + //========================================================================== + // + // GLOBAL OPERATORS + // + //========================================================================== + + //**Equality operator******************************************************* + /*!\brief Equality comparison between two BlockSliceFacade objects. + * + * \param lhs The left-hand side slice. + * \param rhs The right-hand side slice. + * \return \a true if the slices are same, else \a false + */ + template + inline constexpr auto operator==( + BlockSliceFacade const& lhs, + BlockSliceFacade const& rhs) noexcept -> bool { + return (&lhs.parent() == &rhs.parent()) and lhs.index() == rhs.index(); + } + //************************************************************************** + + //**Equality operator******************************************************* + /*!\brief Equality comparison between two ConstBlockSliceFacade objects. + * + * \param lhs The left-hand side const slice. + * \param rhs The right-hand side const slice. + * \return \a true if the const slices are same, else \a false + */ + template + inline constexpr auto operator==( + ConstBlockSliceFacade const& lhs, + ConstBlockSliceFacade const& rhs) noexcept -> bool { + return (&lhs.parent() == &rhs.parent()) and lhs.index() == rhs.index(); + } + //************************************************************************** + + } // namespace detail + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockView.hpp b/backend/src/Systems/Block/Block/BlockView.hpp new file mode 100644 index 00000000..ea1842eb --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockView.hpp @@ -0,0 +1,708 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include "Systems/Block/Block/Types.hpp" +// +#include "Systems/Block/Block/Concepts.hpp" +// +#include "Systems/Block/Block/AsVariables.hpp" +#include "Systems/Block/Block/BlockViewFacade.hpp" +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/BlockVariables/TypeTraits.hpp" +// +#include "Utilities/AsConst.hpp" +#include "Utilities/End.hpp" +#include "Utilities/TMPL.hpp" +// +#include +#include + +namespace blocks { + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + template + inline constexpr auto operator==(BlockView const& lhs, + BlockView const& rhs) noexcept + -> bool; + template + inline constexpr auto operator==(ConstBlockView const& lhs, + ConstBlockView const& rhs) noexcept + -> bool; + // template + // inline auto size(BlockView const& view) -> bool; + // template + // inline auto size(ConstBlockView const& view) -> bool; + /*! \endcond */ + //**************************************************************************** + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Implementation of a view/view on an \elastica Block + * \ingroup blocks + * + * \details + * The BlockView template class provides a view (or) view into a Block of + * `data` and `operations`. It also models the `ComputationalBlock` concept + * and hence can be used across places where a \elastica Block is used as + * a template parameter. + * + * Similar to Block, BlockView can also be customized (specialized) for any + * Lagrangian entity, but experience suggests that extensive customization is + * not needed. For a concrete example, please see specializations of + * BlockView in @ref cosserat_rod. + * + * BlockView is different from a BlockSlice : a BlockSlice represents a single + * physical entity, meanwhile BlockView may represent a group of entities. + * + * \usage + * The intended usage of a BlockView is when a view is required into the data + * held by a block---this is frequently the case when a user either + * 1. adds an entity into the Simulator, or + * 2. requires access (for e.g. reading/writing to disk) to only a portion of + * the block. + * The pattern that is most commonly seen in the use case (1) is for a + * BlockView templated on some `Plugin` type to be only used with a Block + * of the same `Plugin` type, when adding new entities to the Block. + * For use case (2) we suggest the user to the blocks::slice() function, which + * has an intuitive, explicit view syntax, or even the subscript operator[]. + * We note that we might explicitly disable the subscript operator [] for + * slicing in the future, as the semantics are potentially unclear. + * + * Finally, with no additional coding effort, the BlockView has exactly the + * same operations as the mother Block (aka Block of the same `Plugin` type), + * but now it operates only on that view of the data. This means that a + * BlockView can be used as a small Block in itself which greatly simplifies + * interfacing different components of \elastica---the user need not care or + * even know about whether the data that she has is a Block or a BlockView! + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see Block, blocks::slice + */ + template + class BlockView : public detail::BlockViewFacade, + public Gettable>, + public Spannable>, + public Plugin { + // NOTE : Plugin is inherited after Facade so that indices and references to + // the parent block is filled first. This is because some elements of Plugin + // may require that the slice (and index) be valid, for example to generate + // internal refs/pointers for time-stepping. + protected: + //**Type definitions******************************************************** + //! Type of the parent plugin + using Parent = detail::BlockViewFacade; + //! Type of the view + using This = BlockView; + //! Type of gettable + using GetAffordance = Gettable; + //! Type of sliceable + using SpanAffordance = Spannable; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! Type of the parent plugin + using typename Parent::PluginType; + //! Type of the parent block + using typename Parent::ParentBlock; + //! Type of Variables + using typename Parent::Variables; + //************************************************************************** + + //**Friendships************************************************************* + //! Friend the main block + friend ParentBlock; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief The default constructor. + * + * \param parent_block The parent block that + * \param start_index The start index + * \param region_size The stop index + */ + explicit BlockView(ParentBlock& parent_block, std::size_t start_index, + std::size_t region_size) noexcept + : Parent(parent_block, start_index, region_size), + GetAffordance(), + SpanAffordance(), + PluginType() {} + //************************************************************************** + + //************************************************************************** + /*!\brief The copy constructor. + * + * \param other view to copy + */ + BlockView(BlockView const& other) + : Parent(static_cast(other)), + GetAffordance(), + SpanAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //************************************************************************** + /*!\brief The move constructor. + * + * \param other view to move from + */ + BlockView(BlockView&& other) noexcept + : Parent(static_cast(other)), + GetAffordance(), + SpanAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //@} + //************************************************************************** + + public: + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~BlockView() = default; + //@} + //************************************************************************** + + public: + //**Access operators******************************************************** + /*!\name Access operators */ + //@{ + //! Operator for slicing and viewing + using SpanAffordance::operator[]; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Implementation of a view/view on a const \elastica Block + * \ingroup blocks + * + * \details + * The ConstBlockView template class provides a view (or) view into a + * constant Block of \a data and \a operations. It also models the + * `ComputationalBlock` concept and hence can be used across places where a + * \elastica Block is used as a template parameter. + * + * Notably, it differs from BlockView in one key aspect : the underlying + * data/view is always constant and cannot be modified (it is only read only). + * Hence ConstBlock is useful for propagating const-correctness throughout the + * code. It can be used in places where one needs to pass (const)-data to the + * user which she can then copy and use it for her own purposes. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see BlockView + */ + template + class ConstBlockView : public detail::ConstBlockViewFacade, + public Gettable>, + public Spannable>, + public Plugin { + // NOTE : Plugin is inherited after Facade so that indices and references to + // the parent block is filled first. This is because some elements of Plugin + // may require that the slice (and index) be valid, for example to generate + // internal refs/pointers for time-stepping. + protected: + //**Type definitions******************************************************** + //! Type of the parent plugin + using Parent = detail::ConstBlockViewFacade; + //! Type of the view + using This = ConstBlockView; + //! Type of gettable + using GetAffordance = Gettable; + //! Type of sliceable + using SpanAffordance = Spannable; + //************************************************************************** + + public: + //**Type definitions******************************************************** + //! Type of the parent plugin + using typename Parent::PluginType; + //! Type of the parent block + using typename Parent::ParentBlock; + //! Type of Variables + using typename Parent::Variables; + //************************************************************************** + + //**Friendships************************************************************* + //! Friend the main block + friend ParentBlock; + //************************************************************************** + + public: + //**Constructors************************************************************ + /*!\name Constructors */ + //@{ + + //************************************************************************** + /*!\brief The default constructor. + * + * \param parent_block The parent block that + * \param start_index The start index + * \param region_size The stop index + */ + explicit ConstBlockView(ParentBlock const& parent_block, + std::size_t start_index, + std::size_t region_size) noexcept + : Parent(parent_block, start_index, region_size), + GetAffordance(), + SpanAffordance(), + PluginType() {} + //************************************************************************** + + //************************************************************************** + /*!\brief The copy constructor. + * + * \param other view to copy + */ + ConstBlockView(ConstBlockView const& other) + : Parent(static_cast(other)), + GetAffordance(), + SpanAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //************************************************************************** + /*!\brief The move constructor. + * + * \param other view to move from + */ + ConstBlockView(ConstBlockView&& other) noexcept + : Parent(static_cast(other)), + GetAffordance(), + SpanAffordance(), + PluginType(static_cast(other)){}; + //************************************************************************** + + //@} + //************************************************************************** + + public: + //**Destructor************************************************************** + /*!\name Destructor */ + //@{ + ~ConstBlockView() = default; + //@} + //************************************************************************** + + public: + //**Access operators******************************************************** + /*!\name Access operators */ + //@{ + //! Operator for slicing and viewing + using SpanAffordance::operator[]; + //@} + //************************************************************************** + }; + //**************************************************************************** + + //============================================================================ + // + // GLOBAL OPERATORS + // + //============================================================================ + + //**Equality operator********************************************************* + /*!\brief Equality comparison between two BlockView objects. + * + * \param lhs The left-hand side view. + * \param rhs The right-hand side view. + * \return \a true if the views are same, else \a false + */ + template + inline constexpr auto operator==(BlockView const& lhs, + BlockView const& rhs) noexcept + -> bool { + return static_cast const&>(lhs) == + static_cast const&>(rhs); + } + //**************************************************************************** + + //**Equality operator********************************************************* + /*!\brief Equality comparison between two ConstBlockView objects. + * + * \param lhs The left-hand side const view. + * \param rhs The right-hand side const view. + * \return \a true if the const views are same, else \a false + */ + template + inline constexpr auto operator==(ConstBlockView const& lhs, + ConstBlockView const& rhs) noexcept + -> bool { + return static_cast const&>(lhs) == + static_cast const&>(rhs); + } + //**************************************************************************** + + //============================================================================ + // + // FREE FUNCTIONS + // + //============================================================================ + + //**************************************************************************** + /*!\brief Get number of units from a BlockView + * \ingroup blocks + * + * \details + * Get number of units from a BlockView. By 'units' we mean the number of + * individual BlockSlice(s) composing the View. + * + * \usage + * \code + * BlockView<...> b; + * std::size_t n_units = blocks::n_units(b); + * \endcode + * + * \param block_like The block whose number of units is to be extracted. + * + * \note + * These can be customized for your block type. + */ + template + inline auto n_units(BlockView const& block_view) noexcept + -> std::size_t { + return block_view.region().size; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get number of units from a ConstBlockView + * \ingroup blocks + * + * \details + * Get number of units from a ConstBlockView. By 'units' we mean the number of + * individual BlockSlice(s) composing the View. + * + * \usage + * \code + * ConstBlockView<...> b; + * std::size_t n_units = blocks::n_units(b); + * \endcode + * + * \param block_like The block whose number of units is to be extracted. + * + * \note + * These can be customized for your block type. + */ + template + inline auto n_units(ConstBlockView const& block_view) noexcept + -> std::size_t { + return block_view.region().size; + } + //**************************************************************************** + + //============================================================================ + // + // GET FUNCTIONS + // + //============================================================================ + + namespace detail { + + template + inline constexpr decltype(auto) get_view(ViewLike&& view_like) noexcept { + return std::forward(view_like) + .parent() + .template slice( + std::forward(view_like).region().start, + std::forward(view_like).region().size); + } + + } // namespace detail + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name Get functions for Slice types */ + //@{ + + //**************************************************************************** + /*!\brief Extract element from a BlockView + * \ingroup blocks + * + * \details + * Extracts the element of the BlockView `block_view` whose tag type is `Tag`. + * Fails to compile unless the block has the `Tag` being extracted. + * + * \usage + * The usage is similar to std::get(), shown below + * \code + Block<...> b; + auto my_tag_data = blocks::get(b); + * \endcode + * + * \tparam Tag Tag to extract + * + * \param block_like The block to extract the tag from + */ + template + inline constexpr decltype(auto) get_backend( + BlockView& block_view) noexcept { + return detail::get_view(block_view); + } + + template + inline constexpr decltype(auto) get_backend( + BlockView const& block_view) noexcept { + return detail::get_view(block_view); + } + + template + inline constexpr decltype(auto) get_backend( + BlockView&& block_view) noexcept { + return detail::get_view( + static_cast&&>(block_view)); + } + + template + inline constexpr decltype(auto) get_backend( + BlockView const&& block_view) noexcept { + return detail::get_view( + static_cast const&&>(block_view)); + } + + template + inline constexpr decltype(auto) get_backend( + ConstBlockView const& block_view) noexcept { + return detail::get_view(block_view); + } + //@} + /*! \endcond */ + //**************************************************************************** + + //============================================================================ + // + // VIEW FUNCTIONS + // + //============================================================================ + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name Block view functions */ + //@{ + + //**************************************************************************** + /*!\brief View into blocks + * \ingroup blocks + * + * \details + * Implements contiguous views on Blocks + * + * \param block_like Data-structure modeling the block concept + * \param start_index The start index of the slice (int or from_end) + * \param stop_index The stop index of the slice (int or from_end) + * + * \example + * Given a block or a view `b`, we can obtain a view into it by using + * \code + * auto b_view = block::slice(b, 5UL, 10UL); // gets b[5, 6, 7, 8, 9] + * // Gets b[end - 5, end - 4, end - 3] + * auto b_view_from_end = block::slice(b, elastica::end - 5UL, elastica::end - + * 2UL); + * \endcode + */ + template + inline decltype(auto) slice_backend(Block& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + using ReturnType = + typename PluginFrom>::template to::type; + return ReturnType{block_like, start_index, region_size}; + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(Block const& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + using ReturnType = const typename PluginFrom>::template to< + ConstBlockView>::type; + return ReturnType{block_like, start_index, region_size}; + } + //**************************************************************************** + + //@} + /*! \endcond */ + //**************************************************************************** + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name BlockView view functions */ + //@{ + + //**************************************************************************** + /*!\brief View into views + * \ingroup blocks + * + * \details + * Implements contiguous views on Views + * + * \param block_like Data-structure modeling the block concept + * \param start_index The start index of the slice + * \param region_size The size of the slice + * + * \example + * Given a block or a view `b`, we can obtain a view into it by using + * \code + * auto b_view = block::slice(b, 5UL, 10UL); // gets b[5, 6, 7, 8, 9] + * // Gets b[end - 5, end - 4, end - 3] + * auto b_view_from_end = block::slice(b, elastica::end - 5UL, elastica::end - + * 2UL); + * \endcode + * + */ + + //**************************************************************************** + template + inline decltype(auto) slice_backend(BlockView& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + start_index, region_size); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(BlockView const& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + start_index, region_size); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(BlockView&& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend( + static_cast&&>(block_like).parent(), + static_cast&&>(block_like).region().start + + start_index, + region_size); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(ConstBlockView const& block_like, + std::size_t start_index, + std::size_t region_size) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + start_index, region_size); + } + //**************************************************************************** + + //@} + /*! \endcond */ + //**************************************************************************** + + //============================================================================ + // + // SLICE FUNCTIONS + // + //============================================================================ + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\name BlockView slice functions */ + //@{ + + //**************************************************************************** + /*!\brief Slice into views + * \ingroup block_customization + * + * \details + * Implements slices on entities modeling block concept. + * + * \param block_like Data-structure modeling the block concept + * \param index The index of the slice + * + * \example + * Given a view `b`, we can obtain a slice + * \code + * auto b_slice = block::slice(b, 5UL); // at index 5 from start + * // 2 from end + * auto b_slice_from_end = block::slice(b, elastica::end - 2UL); + * \endcode + * + * \note + * not marked noexcept because we can have out of range indices + */ + template + inline decltype(auto) slice_backend(BlockView& block_like, + std::size_t index) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(BlockView const& block_like, + std::size_t index) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(BlockView&& block_like, + std::size_t index) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend( + static_cast&&>(block_like).parent(), + static_cast&&>(block_like).region().start + index); + } + //**************************************************************************** + + //**************************************************************************** + template + inline decltype(auto) slice_backend(ConstBlockView const& block_like, + std::size_t index) noexcept { + // index is already confirmed within the acceptable limits, so use + // slice_backend instead of slice(parent()) + return slice_backend(block_like.parent(), + block_like.region().start + index); + } + //**************************************************************************** + + //@} + /*! \endcond */ + //**************************************************************************** + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/BlockViewFacade.hpp b/backend/src/Systems/Block/Block/BlockViewFacade.hpp new file mode 100644 index 00000000..e8b3785c --- /dev/null +++ b/backend/src/Systems/Block/Block/BlockViewFacade.hpp @@ -0,0 +1,503 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/Block/Block/Types.hpp" +// +#include "Systems/Block/Block/AsVariables.hpp" +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/BlockVariables/TypeTraits.hpp" + +namespace blocks { + + namespace detail { + + //************************************************************************** + //! Region over a block + struct Region { + //! Start region + std::size_t start; + //! Region size + std::size_t size; + }; + //************************************************************************** + + //**Equality operator******************************************************* + /*!\brief Equality comparison between two Region objects. + * + * \param lhs The left-hand side region. + * \param rhs The right-hand side region. + * \return \a true if the regions are over the same space, else \a false + */ + inline constexpr auto operator==(Region const& lhs, + Region const& rhs) noexcept -> bool { + // Brings in the tuple header + // return std::tie(lhs.start, lhs.size) == std::tie(rhs.start, rhs.size); + return (lhs.start == rhs.start) && (lhs.size == rhs.size); + } + /*! \endcond */ + //**************************************************************************** + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Implementation of a slice/view on an \elastica Block + * \ingroup blocks + * + * \details + * The BlockViewFacade template class provides a slice (or) view into a + * Block of `data` and `operations`. It also models the `ComputationalBlock` + * concept and hence can be used across places where a \elastica Block is + * used as a template parameter. + * + * Similar to Block, BlockViewFacade can also be customized (specialized) + * for any Lagrangian entity, but experience suggests that extensive + * customization is not needed. For a concrete example, please see + * specializations of BlockViewFacade in @ref cosserat_rod + * + * \usage + * The intended usage of a BlockViewFacade is when a view is required into + the data + * held by a block---this is frequently the case when a user either + * 1. adds an entity into the Simulator, or + * 2. requires access (for e.g. reading/writing to disk) to only a portion + of + * the block. + * The pattern that is most commonly seen in the use case (1) is for a + * BlockViewFacade templated on some `Plugin` type to be only used with a + Block + * of the same `Plugin` type, when adding new entities to the Block. + * For use case (2) we suggest the user to the blocks::slice() function, + which + * has an intuitive, explicit slice syntax, or even the subscript + operator[]. + * We note that we might explicitly disable the subscript operator [] for + * slicing in the future, as the semantics are potentially unclear. + * + * Finally, with no additional coding effort, the BlockViewFacade has + exactly the + * same operations as the mother Block (aka Block of the same `Plugin` + type), + * but now it operates only on that slice of the data. This means that a + * BlockViewFacade can be used as a small Block in itself which greatly + simplifies + * interfacing different components of \elastica---the user need not care or + * even know about whether the data that she has is a Block or a + BlockViewFacade! + * For example, + \code + // ... make simulator etc ... + auto my_rod = simulator.emplace_back( * ...args... *); + // the args go and form a Block + // which in turn returns a BlockViewFacade + // which the user gets as my_rod + + // use my_rod like a regular cosserat rod + simulator->constrain(my_rod)->using( *...args... *); + \endcode + * This abstraction helped us constrain \c my_rod, embedded in a Block of + * data, using \c SomeConstraint just like any non-Blocked item of the + * \elastica library. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see Block, blocks::slice + */ + template + class BlockViewFacade { + protected: + //**Type definitions****************************************************** + //! Type of the slice + using This = BlockViewFacade; + //************************************************************************ + + public: + //**Type definitions****************************************************** + //! Type of the plugin + using PluginType = Plugin; + //! List of variables + using Variables = typename PluginType::Variables; + //! Typelist of all variable slices + using VariableSlicesList = typename as_slices::slices; + //! Values of all variable slices + using BlockVariableSlices = as_block_variables; + //! Typelist of all variable slices + using VariableConstSlicesList = + typename as_slices::const_slices; + //! Values of all variable slices + using BlockVariableConstViews = + as_block_variables; + //! Conformant mapping between tags and variables + using VariableMap = VariableMapping; + //! Type of the parent block + using ParentBlock = + typename PluginFrom>::template to::type; + //************************************************************************ + + protected: + //**Constructors********************************************************** + /*!\name Constructors */ + //@{ + + //************************************************************************ + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param region Index of the slice + */ + BlockViewFacade(ParentBlock& parent_block, std::size_t start_index, + std::size_t region_size) noexcept + : region_{start_index, region_size}, parent_block_(parent_block) {} + //************************************************************************ + + //************************************************************************ + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + BlockViewFacade(BlockViewFacade const& other) noexcept + : region_(other.region_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //************************************************************************ + /*!\brief The move constructor. + * + * \param other slice to move from + */ + BlockViewFacade(BlockViewFacade&& other) noexcept + : region_(std::move(other.region_)), + parent_block_(other.parent_block_){}; + //************************************************************************ + + //@} + //************************************************************************ + + public: + //**Destructor************************************************************ + /*!\name Destructor */ + //@{ + ~BlockViewFacade() = default; + //@} + //************************************************************************ + + //**Utility methods******************************************************* + /*!\name Utility methods*/ + //@{ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() & noexcept -> ParentBlock& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const& noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is useful when xvalue block-slices are generated + * on-the-fly via BlockRefs. If this overload is not present, then the + * const& is picked up, and it is not possible to assign values to the + * parent block anymore. + * + * \note + * Returning a reference is valid as the parent block outlives the block + * slice. + */ + inline constexpr auto parent() && noexcept -> ParentBlock& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Gets the parent block + * + * \note + * This method is provided for symmetry with the overload above. + * + * \note + * Returning a reference is valid as the parent block outlives the block + * slice. + */ + inline constexpr auto parent() const&& noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Returns the slice region + */ + inline constexpr auto region() const noexcept -> Region const& { + return region_; + } + //************************************************************************ + + //**Data access*********************************************************** + /*!\name Data access */ + //@{ + protected: + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) & { + using RT = BlockVariableSlices; + return RT{ + parent().template slice(region().start, region().size)...}; + } + + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) const& { + using RT = BlockVariableConstViews const; + return RT{ + parent().template slice(region().start, region().size)...}; + } + + public: + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Underlying data + */ + inline constexpr decltype(auto) data() & noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Constant lvalue reference to the underlying data + */ + inline constexpr decltype(auto) data() const& noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //@} + //************************************************************************ + + private: + //**Member variables****************************************************** + /*!\name Member variables */ + //@{ + //! View region + detail::Region region_; + //! Reference to the parent block + ParentBlock& parent_block_; + //@} + //************************************************************************ + }; + /*! \endcond */ + //************************************************************************** + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Implementation of a slice/view on a const \elastica Block + * \ingroup blocks + * + * \details + * The ConstBlockViewFacade template class provides a slice (or) view into + * a constant Block of \a data and \a operations. It also models the + * `ComputationalBlock` concept and hence can be used across places where a + * \elastica Block is used as a template parameter. + * + * Notably, it differs from BlockViewFacade in one key aspect : the + * underlying data/view is always constant and cannot be modified (it is + * only read only). Hence ConstBlock is useful for propagating + * const-correctness throughout the code. It can be used in places where one + * needs to pass (const)-data to the user which she can then copy and use it + * for her own purposes. + * + * \tparam Plugin The computational plugin modeling a Lagrangian entity + * + * \see BlockViewFacade + */ + template // The computational plugin type + class ConstBlockViewFacade { + protected: + //**Type definitions****************************************************** + //! Type of the slice + using This = ConstBlockViewFacade; + //************************************************************************ + + public: + //**Type definitions****************************************************** + //! Type of the plugin + using PluginType = Plugin; + //! List of variables + using Variables = typename PluginType::Variables; + //! Typelist of all variable slices + using VariableSlicesList = typename as_slices::const_slices; + //! Values of all variable slices + using BlockVariableSlices = as_block_variables; + //! Conformant mapping between tags and variables + using VariableMap = VariableMapping; + //! Type of the parent block + using ParentBlock = + typename PluginFrom>::template to::type; + //************************************************************************ + + protected: + //**Constructors********************************************************** + /*!\name Constructors */ + //@{ + + //************************************************************************ + /*!\brief The default constructor. + * + * \param parent_block Parent block for the slice + * \param region Index of the slice + */ + ConstBlockViewFacade(ParentBlock const& parent_block, + std::size_t start_index, + std::size_t region_size) noexcept + : region_{start_index, region_size}, parent_block_(parent_block) {} + //************************************************************************ + + //************************************************************************ + /*!\brief The copy constructor. + * + * \param other slice to copy + */ + ConstBlockViewFacade(ConstBlockViewFacade const& other) noexcept + : region_(other.region_), parent_block_(other.parent_block_){}; + //************************************************************************ + + //************************************************************************ + /*!\brief The move constructor. + * + * \param other slice to move from + */ + ConstBlockViewFacade(ConstBlockViewFacade&& other) noexcept + : region_(std::move(other.region_)), + parent_block_(other.parent_block_){}; + //************************************************************************ + + //@} + //************************************************************************ + + public: + //**Destructor************************************************************ + /*!\name Destructor */ + //@{ + ~ConstBlockViewFacade() = default; + //@} + //************************************************************************ + + //**Utility methods******************************************************* + /*!\name Utility methods*/ + //@{ + + //************************************************************************ + /*!\brief Gets the parent block + */ + inline constexpr auto parent() const noexcept -> ParentBlock const& { + return parent_block_; + } + //************************************************************************ + + //************************************************************************ + /*!\brief Returns the slice region + */ + inline constexpr auto region() const noexcept -> Region const& { + return region_; + } + //************************************************************************ + + //@} + //************************************************************************ + + //**Data access*********************************************************** + /*!\name Data access */ + //@{ + protected: + template + inline constexpr decltype(auto) generate_data(tmpl::list /*meta*/ + ) const& { + using RT = BlockVariableSlices; + return RT{ + parent().template slice(region().start, region().size)...}; + } + + public: + //************************************************************************ + /*!\brief Access to the underlying data + // + // \return Constant lvalue reference to the underlying data + */ + inline constexpr decltype(auto) data() const& noexcept { + return generate_data(Variables{}); + } + //************************************************************************ + + //@} + //************************************************************************ + + private: + //**Member variables****************************************************** + /*!\name Member variables */ + //@{ + //! View region + detail::Region region_; + //! Reference to the parent block + ParentBlock const& parent_block_; + //@} + //************************************************************************ + }; + /*! \endcond */ + //************************************************************************** + + //========================================================================== + // + // GLOBAL OPERATORS + // + //========================================================================== + + //**Equality operator******************************************************* + /*!\brief Equality comparison between two BlockViewFacade objects. + * + * \param lhs The left-hand side slice. + * \param rhs The right-hand side slice. + * \return \a true if the slices are same, else \a false + */ + template + inline constexpr auto operator==( + BlockViewFacade const& lhs, + BlockViewFacade const& rhs) noexcept -> bool { + return (&lhs.parent() == &rhs.parent()) and lhs.region() == rhs.region(); + } + //************************************************************************** + + //**Equality operator******************************************************* + /*!\brief Equality comparison between two ConstBlockViewFacade objects. + * + * \param lhs The left-hand side const slice. + * \param rhs The right-hand side const slice. + * \return \a true if the const slices are same, else \a false + */ + template + inline constexpr auto operator==( + ConstBlockViewFacade const& lhs, + ConstBlockViewFacade const& rhs) noexcept -> bool { + return (&lhs.parent() == &rhs.parent()) and lhs.region() == rhs.region(); + } + //************************************************************************** + + } // namespace detail + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Metadata.hpp b/backend/src/Systems/Block/Block/Metadata.hpp new file mode 100644 index 00000000..eddb874f --- /dev/null +++ b/backend/src/Systems/Block/Block/Metadata.hpp @@ -0,0 +1,309 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** +#include "Systems/Block/Block/TypeTraits.hpp" +#include "Systems/Block/BlockVariables/Types.hpp" +// #include "Utilities/PrettyType.hpp" +#include "Utilities/Requires.hpp" +#include "Utilities/StdVectorHelpers.hpp" +#include "Utilities/TMPL.hpp" +#include "Utilities/WidthStream.hpp" // TODO : move into CPP file +// +#include +#include +#include // transform +#include // move iterator +#include // move, pair +#include + +namespace blocks { + + //**************************************************************************** + /*!\brief Variable metadata + * \ingroup blocks + * + * Carries meta-data information about variables in a block plugin + */ + struct VariableMetadata { + using VariableAttribute = std::pair; + using VariableAttributes = std::vector; + VariableAttributes data; + }; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Plugin metadata + * \ingroup blocks + * + * Carries meta-data information about plugins + */ + struct PluginMetadata { + using Metadata = std::vector; + //! Name of plugin + std::string name; + //! Metadata associated with the plugin + Metadata data; + }; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Customization point for generating metadata of plugins + * \ingroup blocks + * + * \details + * Metadata provides the customization point for specifying the + * behavior of blocks::metadata() for any `Plugin` type. By default, \elastica + * generates basic metadata about plugin variables : Metadata can be + * customized to add more attributes to this metadata. + * If no customized implementation is provided, \elastica falls back to this + * default Metadata class, which does a no-operation. + * + * \usage + * The no-operation above uses the static apply() function templated on the + * variable + * \code + * auto attributes = Metadata::template apply(os); + * \endcode + * + * \section customization Metadata customization + * As seen from the example above, the Metadata class needs to + * define a static apply() function with the following signature + * \snippet this apply_signature + * + * Hence to customize Metadata for your own plugin, we rely on template + * specialization, typically done in the translation unit where the Plugin + * type is defined. As an example of implementing Metadata for a + * custom plugin, consider the following example + * + * \example + * With the setup for Plugin shown below + * \snippet Test_Metadata.cpp customized_plugin + * the following code demonstrates the customization of Metadata + * \snippet Test_Metadata.cpp customization_for_plugin + * + * \tparam Plugin Plugin for which blocks::metadata() need to be + * customized + * + * \see Block, blocks::protocols::Plugin, blocks::Variable, blocks::metadata() + */ + template + struct Metadata { + /// [apply_signature] + // VariableAttributes is a std::vector> + template + static inline auto apply() noexcept -> + typename VariableMetadata::VariableAttributes { + return {}; + } + /// [apply_signature] + }; + //**************************************************************************** + + namespace detail { + + using VariableAttributeCollection = + std::vector; + + //************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + template + auto collect_attributes(tmpl::type_) { + typename VariableMetadata::VariableAttributes attributes{}; + + attributes.emplace_back("Name", Var::name()); //pretty_type::name()); + // attributes.emplace_back("Parameter", + // pretty_type::get_name>()); + // attributes.emplace_back( + // "Initialized", + // tt::is_detected_v ? "true" : "false"); + // attributes.emplace_back("Rank", pretty_type::get_name>()); + // attributes.emplace_back("Type", + // pretty_type::get_name()); + return attributes; + } + + template > = nullptr> + auto collect_attributes() -> VariableAttributeCollection { + using Variables = variables_t; + using CustomizationPoint = Metadata; + + VariableAttributeCollection metadata; + + tmpl::for_each([&metadata](auto var) { + auto attributes = collect_attributes(var); + append(attributes, CustomizationPoint::template apply< + tmpl::type_from>()); + metadata.emplace_back(std::move(attributes)); + }); + + return metadata; + } + /*! \endcond */ + //************************************************************************** + + } // namespace detail + + //============================================================================ + // + // FREE FUNCTIONS + // + //============================================================================ + + // TODO : Move to CPP file + //**************************************************************************** + /*!\brief Stream operator for VariableMetaData + */ + inline std::ostream& operator<<(std::ostream& os, + VariableMetadata const& metadata) { + // This is a very simple YAML formatting. + // For a better one, see YAML Archive + for (auto const& data : metadata.data) { + os << data.first << ": " + << "\"" << data.second << "\"" + << "\n"; + } + return os; + } + //**************************************************************************** + + // TODO : Move to CPP file + //**************************************************************************** + /*!\brief Stream operator for PluginMetaData + */ + inline std::ostream& operator<<(std::ostream& os, + PluginMetadata const& metadata) { + // This is a very simple YAML formatting. + // For a better one, see YAML Archive + constexpr std::size_t line_length = 120UL; + ::elastica::widthstream stream{line_length, os}; + + stream << "Plugin: " << metadata.name << "\n"; + const std::string information_key("Variables:"); + + stream << information_key + << (metadata.data.empty() ? " No variables!" : "\n"); + + int constexpr tab_width = 2; + stream.indent(tab_width); + + for (auto const& data : metadata.data) { + stream << "- " + << "\n"; + stream.indent(tab_width); + stream << data << "\n"; + stream.indent(-tab_width); + } + + stream.indent(-tab_width); + return os; + } + //**************************************************************************** + + //**************************************************************************** + /*!\brief Generates variable information associated with a Plugin + * \ingroup blocks + * + * \example + * \code + * using Plugin = MyCustomPlugin; + * auto meta = blocks::metadata(); + * \endcode + * + * \tparam Plugin A valid plugin template conforming to protocols::Plugin + */ + template + auto metadata() -> PluginMetadata { + auto raw_metadata = detail::collect_attributes(); + // PluginMetadata metadata{pretty_type::name(), {}}; + PluginMetadata metadata{Plugin::name(), {}}; + metadata.data.reserve(raw_metadata.size()); + + // Convert vec> -> vec + std::transform(std::make_move_iterator(std::begin(raw_metadata)), + std::make_move_iterator(std::end(raw_metadata)), + std::back_inserter(metadata.data), + [](typename VariableMetadata::VariableAttributes&& meta) { + return VariableMetadata{std::move(meta)}; + }); + return metadata; + } + //**************************************************************************** + + //**Metadata functions******************************************************** + /*!\name Metadata functions */ + //@{ + + //**************************************************************************** + /*!\brief Generates variable information associated with a type modeling the + * block concept + * \ingroup blocks + * + * \example + * \code + * auto block_like = ...; // Models the block concept + * auto meta = blocks::metadata(block_like); + * \endcode + * + * \tparam Plugin A valid plugin template conforming to protocols::Plugin + */ + template + auto metadata(Block const&) -> PluginMetadata { + return metadata(); + } + //**************************************************************************** + + //**************************************************************************** + template + auto metadata(BlockSlice const&) -> PluginMetadata { + return metadata(); + } + //**************************************************************************** + + //**************************************************************************** + template + auto metadata(ConstBlockSlice const&) -> PluginMetadata { + return metadata(); + } + //**************************************************************************** + + //**************************************************************************** + template + auto metadata(BlockView const&) -> PluginMetadata { + return metadata(); + } + //**************************************************************************** + + //**************************************************************************** + template + auto metadata(ConstBlockView const&) -> PluginMetadata { + return metadata(); + } + //**************************************************************************** + + //@} + //**************************************************************************** + + namespace detail { + + // TODO : Move to CPP file + inline auto form_map(PluginMetadata metadata) { + using AttributeKey = std::string; + using Name = std::string; + using InnerMapType = std::unordered_map; + using ReturnType = std::unordered_map; + ReturnType um; + + for (auto& vm : metadata.data) { + auto& dst = um[(*vm.data.begin()).second]; + dst.insert(std::make_move_iterator(vm.data.begin() + 1), + std::make_move_iterator(vm.data.end())); + } + + return um; + } + + } // namespace detail + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/Protocols.hpp b/backend/src/Systems/Block/Block/Protocols.hpp new file mode 100644 index 00000000..1db4e852 --- /dev/null +++ b/backend/src/Systems/Block/Block/Protocols.hpp @@ -0,0 +1,93 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +#include + +#include "Aliases.hpp" +#include "Systems/Block/BlockVariables/Protocols.hpp" +#include "Utilities/IgnoreUnused.hpp" +#include "Utilities/ProtocolHelpers.hpp" +#include "Utilities/TMPL.hpp" +#include "Utilities/TypeTraits/IsA.hpp" +#include "Utilities/TypeTraits/IsDetected.hpp" + +namespace blocks { + + namespace protocols { + + //========================================================================== + // + // CLASS DEFINITION + // + //========================================================================== + + //************************************************************************** + /*!\brief Block plugin protocol. + * \ingroup block_protocols + * + * Class to enforce adherence to interface expected of a Plugin in blocks. + * Any valid blocks Plugin within the \elastica library should (publicly) + * conform to this class using + * \code + * tt::ConformsTo + * \endcode + * to indicate it qualifies as a block Plugin. Only in case a class + * expresses such conformance, the ::tt::conforms_to and + * ::tt::assert_conforms_to type traits recognizes the class as valid + * Plugin. + * + * Requires that conforming type has the following types: + * \snippet Block/Aliases.hpp variables_t + * which should be a type list and each type within that list in turn + * conforms to protocols::Variable + * + * The following shows an example of minimal conformance to + * protocols::Plugin. + * + * \example + * \snippet Block/Test_Protocols.cpp plugin_protocol_eg + */ + struct Plugin { + //************************************************************************ + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Auxiliary helper struct for enforcing protocols. + // \ingroup protocols + */ + template + struct test { + public: + // Check for nested types of the ConfirmingType + // static_assert(std::is_same::value, "Failure!"); + static_assert(::tt::is_detected_v, + "Not a conforming Block Plugin, doesn't have a " + "nested type called `Variables`"); + using Variables = variables_t; + // the variables_t should be a tmpl::list + static_assert(::tt::is_a_v, + "Not a conforming Block Plugin, nested `Variables` type " + "is not a `tmpl::list`"); + + // and all of its contents should conform to a block variable protocol + template + struct ReportVariableProtocolConformation : std::true_type { + static_assert( + tt::assert_conforms_to, + "Variable called `Var` above does not conform to the protocols " + "expected by a Variable!"); + }; + + using variable_checks IGNORE_UNUSED = decltype( + tmpl::transform>{}); + }; + /*! \endcond */ + //************************************************************************ + }; + //************************************************************************** + + } // namespace protocols + +} // namespace blocks diff --git a/backend/src/Systems/Block/Block/TypeTraits.hpp b/backend/src/Systems/Block/Block/TypeTraits.hpp new file mode 100644 index 00000000..a38e8425 --- /dev/null +++ b/backend/src/Systems/Block/Block/TypeTraits.hpp @@ -0,0 +1,862 @@ +#pragma once + +//****************************************************************************** +// Includes +//****************************************************************************** + +/// +#include "Systems/Block/Block/Protocols.hpp" +#include "Systems/Block/Block/Types.hpp" +/// +#include "Utilities/ProtocolHelpers.hpp" +#include "Utilities/TypeTraits/Cpp17.hpp" +#include "Utilities/TypeTraits/IsA.hpp" + +namespace blocks { + + //============================================================================ + // + // CLASS DEFINITION + // + //============================================================================ + + //**************************************************************************** + /*!\brief Check whether a given type `B` is a Block + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a template specialization of a + * blocks::Block, otherwise inherits from std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = IsBlock; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of elastica::Block, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_block_example + * + * \tparam B : the type to check + * + * \see Block + */ + template + struct IsBlock : ::tt::is_a {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsBlock type trait. + * \ingroup block_tt + * + * The is_block_v variable template provides a convenient + * shortcut to access the nested \a value of the IsBlock + * class template. For instance, given the type \a T the following two + * statements are identical: + * \example + * \code + * constexpr bool value1 = IsBlock::value; + * constexpr bool value2 = is_block_v; + * \endcode + * + * \tparam B : the type to check + * + * \see IsBlock + */ + template + constexpr bool is_block_v = IsBlock::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `B` is a BlockSlice + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a template specialization of a + * blocks::BlockSlice, otherwise inherits from std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = IsBlockSlice; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of elastica::BlockSlice, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_blockslice_example + * + * \tparam B : the type to check + * + * \see BlockSlice + */ + template + struct IsBlockSlice : ::tt::is_a {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsBlockSlice type trait. + * \ingroup block_tt + * + * The is_block_slice_v variable template provides a convenient + * shortcut to access the nested \a value of the IsBlockSlice + * class template. For instance, given the type \a T the following two + * statements are identical: + * \example + * \code + * constexpr bool value1 = IsBlockSlice::value; + * constexpr bool value2 = is_block_slice_v; + * \endcode + * + * \tparam B : the type to check + * + * \see IsBlockSlice + */ + template + constexpr bool is_block_slice_v = IsBlockSlice::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `B` is a ConstBlockSlice + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a template specialization of a + * blocks::ConstBlockSlice, otherwise inherits from std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = IsConstBlockSlice; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of elastica::ConstBlockSlice, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_const_blockslice_example + * + * \tparam B : the type to check + * + * \see ConstBlockSlice + */ + template + struct IsConstBlockSlice : ::tt::is_a {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsConstBlockSlice type trait. + * \ingroup block_tt + * + * The is_const_block_slice_v variable template provides a convenient + * shortcut to access the nested \a value of the IsConstBlockSlice + * class template. For instance, given the type \a T the following two + * statements are identical: + * \example + * \code + * constexpr bool value1 = IsConstBlockSlice::value; + * constexpr bool value2 = is_const_block_slice_v; + * \endcode + * + * \tparam B : the type to check + * + * \see IsBlockSlice + */ + template + constexpr bool is_const_block_slice_v = IsConstBlockSlice::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `B` is a BlockView + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a template specialization of a + * blocks::BlockView, otherwise inherits from std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = IsBlockView; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of elastica::BlockView, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_blockview_example + * + * \tparam B : the type to check + * + * \see BlockView + */ + template + struct IsBlockView : ::tt::is_a {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsBlockView type trait. + * \ingroup block_tt + * + * The is_block_view_v variable template provides a convenient + * shortcut to access the nested \a value of the IsBlockView + * class template. For instance, given the type \a T the following two + * statements are identical: + * \example + * \code + * constexpr bool value1 = IsBlockView::value; + * constexpr bool value2 = is_block_view_v; + * \endcode + * + * \tparam B : the type to check + * + * \see IsBlockView + */ + template + constexpr bool is_block_view_v = IsBlockView::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `B` is a ConstBlockView + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a template specialization of a + * blocks::ConstBlockView, otherwise inherits from std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = IsConstBlockView; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of elastica::ConstBlockView, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_const_blockview_example + * + * \tparam B : the type to check + * + * \see ConstBlockView + */ + template + struct IsConstBlockView : ::tt::is_a {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsConstBlockView type trait. + * \ingroup block_tt + * + * The is_const_block_view_v variable template provides a convenient + * shortcut to access the nested \a value of the IsConstBlockView + * class template. For instance, given the type \a T the following two + * statements are identical: + * \example + * \code + * constexpr bool value1 = IsConstBlockView::value; + * constexpr bool value2 = is_const_block_view_v; + * \endcode + * + * \tparam B : the type to check + * + * \see IsBlockView + */ + template + constexpr bool is_const_block_view_v = IsConstBlockView::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `P` is a Plugin type + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` is a Plugin type otherwise inherits + * from std::false_type. + * + * \usage + * For any type `P`, + * \code + * using result = blocks::IsPlugin

; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `P` is marked as a Plugin by inheriting from + * blocks::protocols::Plugin, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp is_plugin_example + * + * \tparam P : the type to check + * + * \see blocks::protocols::Plugin + */ + template + struct IsPlugin : public tt::conforms_to {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Auxiliary variable template for the IsPlugin type trait. + * \ingroup block_tt + * + * The is_plugin_v variable template provides a convenient shortcut to access + * the nested `value` of the IsPlugin class template. For instance, given the + * type `T` the following two statements are identical: + * + * \example + * \code + * constexpr bool value1 = IsPlugin::value; + * constexpr bool value2 = is_plugin_v; + * \endcode + * + * \tparam P : the type to check + * + * \see IsPlugin + */ + template + constexpr bool is_plugin_v = IsPlugin

::value; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Check whether a given type `B` models the \a Block concept + * \ingroup block_tt + * + * \details + * Inherits from std::true_type if `B` models the \a Block concept, that is + * `B` is one of blocks::Block, blocks::BlockSlice, otherwise inherits from + * std::false_type. + * + * \usage + * For any type `B`, + * \code + * using result = ModelsBlockConcept; + * \endcode + * + * \metareturns + * cpp17::bool_constant + * + * \semantics + * If the type `B` is an instantiation of either a blocks::Block or a blocks + * ::BlockSlice, then + * \code + * typename result::type = std::true_type; + * \endcode + * otherwise + * \code + * typename result::type = std::false_type; + * \endcode + * + * \example + * \snippet Block/Test_TypeTraits.cpp models_block_concept_example + * + * \tparam B : the type to check + * + * \see Block, BlockSlice, ConstBlockSlice, BlockView + */ + template + struct ModelsBlockConcept + : ::cpp17::disjunction, IsBlockSlice, IsConstBlockSlice, + IsBlockView, IsConstBlockView> {}; + //**************************************************************************** + + //**************************************************************************** + /*!\brief Get the plugin type `P` of a block type `B` + * \ingroup block_tt + * + * \details + * The PluginTrait type trait has a nested member `type` representing the + * plugin `P` when the meta-argument `B` models block concept + * + * \usage + * For any type `B` such that ModelsBlockConcept::type = std::true_type, + * \code + * using result = PluginTrait; + * \endcode + * \metareturns + * the type `T = P` such that `B = X

` where `X` is the type modeling block + * concept + * + * \example + * \snippet Block/Test_TypeTraits.cpp plugin_trait_example + * + * \tparam B Type (modeling block concept) whose `PluginTrait` is to be + * retrieved + * + * \see ModelsBlockConcept + */ + template + struct PluginTrait; + //**************************************************************************** + + //**************************************************************************** + /*! \cond ELASTICA_INTERNAL */ + /*!\brief Specialization of PluginTrait for a plain plugin + * \ingroup block_tt + */ + template + struct PluginTrait> { + template