diff --git a/src/common/ref_array.hh b/src/common/ref_array.hh new file mode 100644 index 0000000..31d5fa0 --- /dev/null +++ b/src/common/ref_array.hh @@ -0,0 +1,99 @@ +/** + * file ref_array.hh + * + * @author Till Junge + * + * @date 04 Dec 2018 + * + * @brief convenience class to simulate an array of references + * + * Copyright © 2018 Till Junge + * + * µSpectre is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3, or (at + * your option) any later version. + * + * µSpectre is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with GNU Emacs; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining it + * with proprietary FFT implementations or numerical libraries, containing parts + * covered by the terms of those libraries' licenses, the licensors of this + * Program grant you additional permission to convey the resulting work. + */ + +#ifndef REF_ARRAY_H +#define REF_ARRAY_H + +#include +#include +#include "common/iterators.hh" + +namespace muSpectre { + namespace internal { + + template + struct TypeChecker { + constexpr static bool value{ + std::is_same>::value + and TypeChecker::value}; + }; + + template + struct TypeChecker { + constexpr static bool value{ + std::is_same>::value}; + }; + + } // internal + + template + class RefArray { + public: + //! Default constructor + RefArray() = delete; + + template + RefArray(Vals&... vals): + values{&vals...} { + static_assert(internal::TypeChecker::value, + "Only refs to type T allowed"); + } + + //! Copy constructor + RefArray(const RefArray &other) = default; + + //! Move constructor + RefArray(RefArray &&other) = default; + + //! Destructor + virtual ~RefArray() = default; + + //! Copy assignment operator + RefArray& operator=(const RefArray &other) = default; + + //! Move assignment operator + RefArray& operator=(RefArray &&other) = delete; + + T& operator[](size_t index) { return *this->values[index];} + constexpr T& operator[](size_t index) const { return *this->values[index];} + + protected: + std::array values{}; + private: + }; + + +} // muSpectre + +#endif /* REF_ARRAY_H */ diff --git a/src/common/statefield.hh b/src/common/statefield.hh index e5945a4..2a5850c 100644 --- a/src/common/statefield.hh +++ b/src/common/statefield.hh @@ -1,682 +1,692 @@ /** * file statefield.hh * * @author Till Junge * * @date 28 Feb 2018 * * @brief A state field is an abstraction of a field that can hold * current, as well as a chosen number of previous values. This is * useful for instance for internal state variables in plastic laws, * where a current, new, or trial state is computed based on its * previous state, and at convergence, this new state gets cycled into * the old, the old into the old-1 etc. The state field abstraction * helps doing this safely (i.e. only const references to the old * states are available, while the current state can be assigned * to/modified), and efficiently (i.e., no need to copy values from * new to old, we just cycle the labels). This file implements the * state field as well as state maps using the Field, FieldCollection * and FieldMap abstractions of µSpectre * * Copyright © 2018 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with µSpectre; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef STATEFIELD_H #define STATEFIELD_H -#include "common/field_helpers.hh" -#include "common/field.hh" -#include "common/utilities.hh" +#include "field_helpers.hh" +#include "field.hh" +#include "ref_array.hh" #include #include #include namespace muSpectre { /** * Forward-declaration */ template class TypedField; /** * Base class for state fields, useful for storing polymorphic references */ template class StateFieldBase { public: //! get naming prefix const std::string & get_prefix() const {return this->prefix;} //! get a ref to the `StateField` 's field collection const FieldCollection & get_collection() const { return this->collection;} virtual ~StateFieldBase() = default; /** * returns number of old states that are stored */ size_t get_nb_memory() const {return this->nb_memory;} //! return type_id of stored type virtual const std::type_info & get_stored_typeid() const = 0; /** * cycle the fields (current becomes old, old becomes older, * oldest becomes current) */ virtual void cycle() = 0; protected: //! constructor StateFieldBase(std::string unique_prefix, const FieldCollection & collection, size_t nb_memory=1): prefix{unique_prefix}, nb_memory{nb_memory}, collection{collection} {} /** * the unique prefix is used as the first part of the unique name * of the subfields belonging to this state field */ std::string prefix; /** * number of old states to store, defaults to 1 */ const size_t nb_memory; //! reference to the collection this statefield belongs to const FieldCollection & collection; }; /* ---------------------------------------------------------------------- */ template class TypedStateField: public StateFieldBase { public: //! Parent class using Parent = StateFieldBase; //! Typed field using TypedField_t = TypedField; //! returns a TypedField ref to the current value of this state field virtual TypedField_t & get_current_field() = 0; //! returns a const TypedField ref to an old value of this state field virtual const TypedField_t & get_old_field(size_t nb_steps_ago=1) const = 0; //! returns a `StateField` reference if `other is a compatible state field inline static TypedStateField& check_ref(Parent& other) { // the following triggers and exception if the fields are incompatible if (typeid(T).hash_code() != other.get_stored_typeid().hash_code()) { std::stringstream err_str{}; err_str << "Cannot create a rerference of requested type " << "for statefield '" << other.get_prefix() << "' of type '" << other.get_stored_typeid().name() << "'"; throw std::runtime_error(err_str.str()); } return static_cast (other); } //! return type_id of stored type const std::type_info & get_stored_typeid() const override final { return typeid(T); }; virtual ~TypedStateField() = default; protected: //! constructor TypedStateField(const std::string & unique_prefix, const FieldCollection & collection, size_t nb_memory): Parent{unique_prefix, collection, nb_memory} {} }; /* ---------------------------------------------------------------------- */ template class TypedSizedStateField: public TypedStateField { public: //! Parent class using Parent = TypedStateField; //! the current (historically accurate) ordering of the fields using index_t = std::array; //! get the current ordering of the fields inline const index_t & get_indices() const {return this->indices;} //! destructor virtual ~TypedSizedStateField() = default; protected: //! constructor TypedSizedStateField(std::string unique_prefix, const FieldCollection& collection, index_t indices): Parent{unique_prefix, collection, nb_memory}, indices{indices}{}; index_t indices; ///< these are cycled through }; //! early declaration template class StateFieldMap; namespace internal { template inline decltype(auto) build_fields_helper(std::string prefix, typename Field::Base::collection_t & collection, std::index_sequence) { auto get_field{[&prefix, &collection](size_t i) -> Field& { std::stringstream name_stream{}; name_stream << prefix << ", sub_field index " << i; return make_field(name_stream.str(), collection); }}; - return std::tie(get_field(I)...); + return RefArray(get_field(I)...); } /* ---------------------------------------------------------------------- */ template inline decltype(auto) build_indices(std::index_sequence) { return std::array{(size-I)%size...}; } } // internal /** * A statefield is an abstraction around a Field that can hold a * current and `nb_memory` previous values. There are useful for * history variables, for instance. */ template class StateField: public TypedSizedStateField { public: //! the underlying field's collection type using FieldCollection_t = typename Field_t::Base::collection_t; //! base type for fields using Scalar = typename Field_t::Scalar; //! Base class for all state fields of same memory using Base = TypedSizedStateField; /** * storage of field refs (can't be a `std::array`, because arrays * of refs are explicitely forbidden */ - using Fields_t = tuple_array; + using Fields_t = RefArray; //! Typed field using TypedField_t = TypedField; //! Default constructor StateField() = delete; //! Copy constructor StateField(const StateField &other) = delete; //! Move coonstructor StateField(StateField &&other) = delete; //! Destructor virtual ~StateField() = default; //! Copy assignment operator StateField& operator=(const StateField &other) = delete; //! Move assignment operator StateField& operator=(StateField &&other) = delete; //! get (modifiable) current field inline Field_t& current() { return this->fields[this->indices[0]]; } //! get (constant) previous field template inline const Field_t& old() { static_assert(nb_steps_ago <= nb_memory, "you can't go that far inte the past"); static_assert(nb_steps_ago > 0, "Did you mean to call current()?"); return this->fields[this->indices.at(nb_steps_ago)]; } //! returns a TypedField ref to the current value of this state field TypedField_t & get_current_field() override final { return this->current(); } //! returns a const TypedField ref to an old value of this state field const TypedField_t & get_old_field(size_t nb_steps_ago=1) const override final { return this->fields[this->indices.at(nb_steps_ago)]; } //! factory function template friend StateFieldType& make_statefield(const std::string & unique_prefix, CollectionType & collection); //! returns a `StateField` reference if `other is a compatible state field inline static StateField& check_ref(Base& other) { // the following triggers and exception if the fields are incompatible Field_t::check_ref(other.fields[0]); return static_cast (other); } //! returns a const `StateField` reference if `other` is a compatible state field inline static const StateField& check_ref(const Base& other) { // the following triggers and exception if the fields are incompatible Field_t::check_ref(other.fields[0]); return static_cast (other); } //! get a ref to the `StateField` 's fields Fields_t & get_fields() { return this->fields; } /** * Pure convenience functions to get a MatrixFieldMap of * appropriate dimensions mapped to this field. You can also * create other types of maps, as long as they have the right * fundamental type (T), the correct size (nbComponents), and * memory (nb_memory). */ inline decltype(auto) get_map() { - using FieldMap = decltype(std::get<0>(this->fields).get_map()); + using FieldMap = decltype(this->fields[0].get_map()); return StateFieldMap(*this); } /** * Pure convenience functions to get a MatrixFieldMap of * appropriate dimensions mapped to this field. You can also * create other types of maps, as long as they have the right * fundamental type (T), the correct size (nbComponents), and * memory (nb_memory). */ inline decltype(auto) get_const_map() { - using FieldMap = decltype(std::get<0>(this->fields).get_const_map()); + using FieldMap = decltype(this->fields[0].get_const_map()); return StateFieldMap(*this); } /** * cycle the fields (current becomes old, old becomes older, * oldest becomes current) */ inline void cycle() override final { for (auto & val: this->indices) { val = (val+1)%(nb_memory+1); } } protected: /** * Constructor. @param unique_prefix is used to create the names * of the fields that this abstraction creates in the background * @param collection is the field collection in which the * subfields will be stored */ inline StateField(const std::string & unique_prefix, FieldCollection_t & collection) : Base{unique_prefix, collection, internal::build_indices (std::make_index_sequence{})}, fields{internal::build_fields_helper (unique_prefix, collection, std::make_index_sequence{})} {} Fields_t fields; //!< container for the states private: }; namespace internal { template inline decltype(auto) build_maps_helper(Fields & fields, std::index_sequence) { - return std::array{FieldMap(std::get(fields))...}; + return std::array{FieldMap(fields[I])...}; } } // internal /* ---------------------------------------------------------------------- */ template inline StateFieldType & make_statefield(const std::string & unique_prefix, CollectionType & collection) { std::unique_ptr ptr { new StateFieldType(unique_prefix, collection)}; auto & retref{*ptr}; collection.register_statefield(std::move(ptr)); return retref; } /** * extends the StateField <-> Field equivalence to StateFieldMap <-> FieldMap */ template class StateFieldMap { public: /** * iterates over all pixels in the `muSpectre::FieldCollection` and * dereferences to a proxy giving access to the appropriate iterates * of the underlying `FieldMap` type. */ class iterator; //! stl conformance using reference = typename iterator::reference; //! stl conformance using value_type = typename iterator::value_type; //! stl conformance using size_type = typename iterator::size_type; //! field collection type where this state field can be stored using FieldCollection_t= typename FieldMap::Field::collection_t; //! Fundamental type stored using Scalar = typename FieldMap::Scalar; //! base class (must be at least sized) using TypedSizedStateField_t = TypedSizedStateField; //! for traits access using FieldMap_t = FieldMap; //! for traits access using ConstFieldMap_t = typename FieldMap::ConstMap; //! Default constructor StateFieldMap() = delete; //! constructor using a StateField template StateFieldMap(StateField & statefield) :collection{statefield.get_collection()}, statefield{statefield}, maps{internal::build_maps_helper (statefield.get_fields(), std::make_index_sequence{})}, const_maps{internal::build_maps_helper (statefield.get_fields(), std::make_index_sequence{})} { static_assert(std::is_base_of::value, "Not the right type of StateField ref"); } //! Copy constructor StateFieldMap(const StateFieldMap &other) = delete; //! Move constructor StateFieldMap(StateFieldMap &&other) = default; //! Destructor virtual ~StateFieldMap() = default; //! Copy assignment operator StateFieldMap& operator=(const StateFieldMap &other) = delete; //! Move assignment operator StateFieldMap& operator=(StateFieldMap &&other) = delete; //! access the wrapper to a given pixel directly value_type operator[](size_type index) { return *iterator(*this, index); } /** * return a ref to the current field map. useful for instance for * initialisations of `StateField` instances */ FieldMap& current() { return this->maps[this->statefield.get_indices()[0]]; } //! stl conformance iterator begin() { return iterator(*this, 0);} //! stl conformance iterator end() { return iterator(*this, this->collection.size());} protected: const FieldCollection_t & collection; //!< collection holding the field TypedSizedStateField_t & statefield; //!< ref to the field itself std::array maps;//!< refs to the addressable maps; //! const refs to the addressable maps; std::array const_maps; private: }; /** * Iterator class used by the `StateFieldMap` */ template class StateFieldMap::iterator { public: class StateWrapper; using Ccoord = typename FieldMap::Ccoord; //!< cell coordinates type using value_type = StateWrapper; //!< stl conformance using const_value_type = value_type; //!< stl conformance using pointer_type = value_type*; //!< stl conformance using difference_type = std::ptrdiff_t; //!< stl conformance using size_type = size_t; //!< stl conformance using iterator_category = std::random_access_iterator_tag; //!< stl conformance using reference = StateWrapper; //!< stl conformance //! Default constructor iterator() = delete; //! constructor iterator(StateFieldMap& map, size_t index = 0) :index{index}, map{map} {}; //! Copy constructor iterator(const iterator &other) = default; //! Move constructor iterator(iterator &&other) = default; //! Destructor virtual ~iterator() = default; //! Copy assignment operator iterator& operator=(const iterator &other) = default; //! Move assignment operator iterator& operator=(iterator &&other) = default; //! pre-increment inline iterator & operator++() { this->index++; return *this;} //! post-increment inline iterator operator++(int) { iterator curr{*this}; this->index++; return curr;} //! dereference inline value_type operator*() { return value_type(*this);} //! pre-decrement inline iterator & operator--() { this->index--; return *this;} //! post-decrement inline iterator operator--(int) { iterator curr{*this}; this->index--; return curr;} //! access subscripting inline value_type operator[](difference_type diff) { return value_type{iterator{this->map, this->index+diff}};} //! equality inline bool operator==(const iterator & other) const { return this->index == other.index; } //! inequality inline bool operator!=(const iterator & other) const { return this->index != other.index;} //! div. comparisons inline bool operator<(const iterator & other) const { return this->index < other.index; } //! div. comparisons inline bool operator<=(const iterator & other) const { return this->index <= other.index; } //! div. comparisons inline bool operator>(const iterator & other) const { return this->index > other.index; } //! div. comparisons inline bool operator>=(const iterator & other) const { return this->index >= other.index; } //! additions, subtractions and corresponding assignments inline iterator operator+(difference_type diff) const { return iterator{this->map, this-index + diff}; } //! additions, subtractions and corresponding assignments inline iterator operator-(difference_type diff) const { return iterator{this->map, this-index - diff};} //! additions, subtractions and corresponding assignments inline iterator& operator+=(difference_type diff) { this->index += diff; return *this;} //! additions, subtractions and corresponding assignments inline iterator& operator-=(difference_type diff) { this->index -= diff; return *this; } //! get pixel coordinates inline Ccoord get_ccoord() const { return this->map.collection.get_ccoord(this->index); } //! access the index inline const size_t & get_index() const {return this->index;} protected: size_t index; //!< current pixel this iterator refers to StateFieldMap& map; //!< map over with `this` iterates private: }; namespace internal { //! FieldMap is an `Eigen::Map` or `Eigen::TensorMap` here - template - inline decltype(auto) + inline Array build_old_vals_helper(iterator& it, maps_t & maps, indices_t & indices, std::index_sequence) { - return tuple_array(std::forward_as_tuple(maps[indices[I+1]][it.get_index()]...)); + return Array{maps[indices[I+1]][it.get_index()]...}; } - template - inline decltype(auto) + template + inline Array build_old_vals(iterator& it, maps_t & maps, indices_t & indices) { - return tuple_array{build_old_vals_helper - (it, maps, indices, std::make_index_sequence{})}; + return build_old_vals_helper + (it, maps, indices, std::make_index_sequence{}); } } // internal /** * Light-weight resource-handle representing the current and old * values of a field at a given pixel identified by an iterator * pointing to it */ template class StateFieldMap::iterator::StateWrapper { public: //! short-hand using iterator = typename StateFieldMap::iterator; //! short-hand using Ccoord = typename iterator::Ccoord; //! short-hand using Map = typename FieldMap::reference; //! short-hand using ConstMap = typename FieldMap::const_reference; + /** + * storage type differs depending on whether Map is a Reference type (in the + * C++ sense) or not, because arrays of references are forbidden + */ + using Array_t = std::conditional_t + ::value, + RefArray, nb_memory>, + std::array>; + //! Default constructor StateWrapper() = delete; //! Copy constructor StateWrapper(const StateWrapper &other) = default; //! Move constructor StateWrapper(StateWrapper &&other) = default; //! construct with `StateFieldMap::iterator` StateWrapper(iterator & it) :it{it}, current_val{it.map.maps[it.map.statefield.get_indices()[0]][it.index]}, old_vals(internal::build_old_vals - (it, it.map.const_maps, - it.map.statefield.get_indices())) + (it, it.map.const_maps, + it.map.statefield.get_indices())) { } //! Destructor virtual ~StateWrapper() = default; //! Copy assignment operator StateWrapper& operator=(const StateWrapper &other) = default; //! Move assignment operator StateWrapper& operator=(StateWrapper &&other) = default; //! returns reference to the currectly mapped value inline Map& current() { return this->current_val; } //! recurnts reference the the value that was current `nb_steps_ago` ago template inline const ConstMap & old() const{ static_assert (nb_steps_ago <= nb_memory, "You have not stored that time step"); static_assert (nb_steps_ago > 0, "Did you mean to access the current value? If so, use " "current()"); - return std::get(this->old_vals); + return this->old_vals[nb_steps_ago-1]; } //! read the coordinates of the current pixel inline Ccoord get_ccoord() const { return this->it.get_ccoord(); } protected: iterator& it; //!< ref to the iterator that dereferences to `this` Map current_val; //!< current value - tuple_array old_vals; //!< all stored old values + Array_t old_vals; //!< all stored old values private: }; } // muSpectre #endif /* STATEFIELD_H */ diff --git a/src/common/utilities.hh b/src/common/utilities.hh index 7e1a1c4..7861ec1 100644 --- a/src/common/utilities.hh +++ b/src/common/utilities.hh @@ -1,301 +1,178 @@ /** * @file utilities.hh * * @author Till Junge * * @date 17 Nov 2017 * * @brief additions to the standard name space to anticipate C++17 features * * Copyright © 2017 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with µSpectre; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef UTILITIES_H #define UTILITIES_H #include #include #ifdef NO_EXPERIMENTAL # include #else # include #endif namespace std_replacement { namespace detail { template struct is_reference_wrapper : std::false_type {}; template struct is_reference_wrapper> : std::true_type {}; //! from cppreference template auto INVOKE(T Base::*pmf, Derived&& ref, Args&&... args) noexcept(noexcept((std::forward(ref).*pmf)(std::forward(args)...))) -> std::enable_if_t::value && std::is_base_of>::value, decltype((std::forward(ref).*pmf)(std::forward(args)...))> { return (std::forward(ref).*pmf)(std::forward(args)...); } //! from cppreference template auto INVOKE(T Base::*pmf, RefWrap&& ref, Args&&... args) noexcept(noexcept((ref.get().*pmf)(std::forward(args)...))) -> std::enable_if_t::value && is_reference_wrapper>::value, decltype((ref.get().*pmf)(std::forward(args)...))> { return (ref.get().*pmf)(std::forward(args)...); } //! from cppreference template auto INVOKE(T Base::*pmf, Pointer&& ptr, Args&&... args) noexcept(noexcept(((*std::forward(ptr)).*pmf)(std::forward(args)...))) -> std::enable_if_t::value && !is_reference_wrapper>::value && !std::is_base_of>::value, decltype(((*std::forward(ptr)).*pmf)(std::forward(args)...))> { return ((*std::forward(ptr)).*pmf)(std::forward(args)...); } //! from cppreference template auto INVOKE(T Base::*pmd, Derived&& ref) noexcept(noexcept(std::forward(ref).*pmd)) -> std::enable_if_t::value && std::is_base_of>::value, decltype(std::forward(ref).*pmd)> { return std::forward(ref).*pmd; } //! from cppreference template auto INVOKE(T Base::*pmd, RefWrap&& ref) noexcept(noexcept(ref.get().*pmd)) -> std::enable_if_t::value && is_reference_wrapper>::value, decltype(ref.get().*pmd)> { return ref.get().*pmd; } //! from cppreference template auto INVOKE(T Base::*pmd, Pointer&& ptr) noexcept(noexcept((*std::forward(ptr)).*pmd)) -> std::enable_if_t::value && !is_reference_wrapper>::value && !std::is_base_of>::value, decltype((*std::forward(ptr)).*pmd)> { return (*std::forward(ptr)).*pmd; } //! from cppreference template auto INVOKE(F&& f, Args&&... args) noexcept(noexcept(std::forward(f)(std::forward(args)...))) -> std::enable_if_t>::value, decltype(std::forward(f)(std::forward(args)...))> { return std::forward(f)(std::forward(args)...); } } // namespace detail //! from cppreference template< class F, class... ArgTypes > auto invoke(F&& f, ArgTypes&&... args) // exception specification for QoI noexcept(noexcept(detail::INVOKE(std::forward(f), std::forward(args)...))) -> decltype(detail::INVOKE(std::forward(f), std::forward(args)...)) { return detail::INVOKE(std::forward(f), std::forward(args)...); } namespace detail { //! from cppreference template constexpr decltype(auto) apply_impl(F &&f, Tuple &&t, std::index_sequence) { return std_replacement::invoke(std::forward(f), std::get(std::forward(t))...); } } // namespace detail //! from cppreference template constexpr decltype(auto) apply(F &&f, Tuple &&t) { return detail::apply_impl (std::forward(f), std::forward(t), std::make_index_sequence>::value>{}); } } //namespace std_replacement namespace muSpectre { - namespace internal { - - /** - * helper struct template to compute the type of a tuple with a - * given number of entries of the same type - */ - template - struct tuple_array_helper { - //! underlying tuple - using type = typename tuple_array_helper::type; - }; - - /** - * helper struct template to compute the type of a tuple with a - * given number of entries of the same type - */ - template< typename T, typename... tail> - struct tuple_array_helper<0, T, tail...> { - //! underlying tuple - using type = std::tuple; - }; - - /** - * helper struct for runtime index access to - * tuples. RecursionLevel indicates how much more we can recurse - * down - */ - template - struct Accessor { - using Stored_t = typename TupArr::Stored_t; - - inline static Stored_t - get(const size_t & index, TupArr & container) { - if (index == Index) { - return std::get(container); - } else { - return Accessor::get(index, container); - } - } - inline static const Stored_t - get(const size_t & index, const TupArr & container) { - if (index == Index) { - return std::get(container); - } else { - return Accessor::get(index, container); - } - } - }; - - /** - * specialisation for recursion end - */ - template - struct Accessor { - using Stored_t = typename TupArr::Stored_t; - - inline static Stored_t - get(const size_t & index, TupArr & container) { - if (index == Index) { - return std::get(container); - } else { - std::stringstream err{}; - err << "Index " << index << "is out of range."; - throw std::runtime_error(err.str()); - } - } - - inline static const Stored_t - get(const size_t & index, const TupArr & container) { - if (index == Index) { - return std::get(container); - } else { - std::stringstream err{}; - err << "Index " << index << "is out of range."; - throw std::runtime_error(err.str()); - } - } - }; - - /** - * helper struct that provides the tuple_array. - */ - template - struct tuple_array_provider { - //! tuple type that can be used (almost) like an `std::array` - class type: public tuple_array_helper::type { - public: - //! short-hand - using Parent = typename tuple_array_helper::type; - using Stored_t = T; - constexpr static size_t Size{size}; - - //! constructor - inline type(Parent && parent):Parent{parent}{}; - - //! element access - T operator[] (const size_t & index) { - return Accessor::get(index, *this); - } - - //! element access - const T operator[](const size_t & index) const { - return Accessor::get(index, *this); - } - protected: - }; - }; - } // internal - - /** - * This is a convenience structure to create a tuple of `nb_elem` - * entries of type `T`. It is named tuple_array, because it is - * somewhat similar to an `std::array`. The reason for - * this structure is that the `std::array` is not allowed by the - * standard to store references (8.3.2 References, paragraph 5: - * "There shall be no references to references, no arrays of - * references, and no pointers to references.") use this, if you - * want to have a statically known number of references to store, - * and you wish to do so efficiently. - */ - template - using tuple_array = typename internal::tuple_array_provider::type; - using std_replacement::apply; /** * emulation `std::optional` (a C++17 feature) */ template #ifdef NO_EXPERIMENTAL using optional = typename boost::optional; #else using optional = typename std::experimental::optional; #endif } // muSpectre #endif /* UTILITIES_H */ diff --git a/tests/header_test_ref_array.cc b/tests/header_test_ref_array.cc new file mode 100644 index 0000000..2616348 --- /dev/null +++ b/tests/header_test_ref_array.cc @@ -0,0 +1,53 @@ +/** + * file header_test_ref_array.cc + * + * @author Till Junge + * + * @date 04 Dec 2018 + * + * @brief tests for the RefArray convenience struct + * + * Copyright © 2018 Till Junge + * + * µSpectre is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3, or (at + * your option) any later version. + * + * µSpectre is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with GNU Emacs; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining it + * with proprietary FFT implementations or numerical libraries, containing parts + * covered by the terms of those libraries' licenses, the licensors of this + * Program grant you additional permission to convey the resulting work. + */ + + +#include "common/ref_array.hh" +#include "tests.hh" + +namespace muSpectre { + + BOOST_AUTO_TEST_SUITE(RefArray_tests); + + BOOST_AUTO_TEST_CASE(two_d_test) { + std::array values{2, 3}; + RefArray refs{values[0], values[1]}; + + BOOST_CHECK_EQUAL(values[0], refs[0]); + BOOST_CHECK_EQUAL(values[1], refs[1]); + } + + BOOST_AUTO_TEST_SUITE_END(); + +} // muSpectre