Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions cxx/include/tensorwrapper/buffer/buffer_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,23 @@ class BufferBase : public BufferBaseCommon<BufferBase>,
bool approximately_equal_(const BufferViewBase<BufferBaseType>& rhs,
double tol) const;

/** @brief Returns the element with the offsets specified by @p index.
*
* @param[in] index The offsets into each mode of *this for the desired
* element.
*
* @return A const reference to the element at the specified offsets.
*/
virtual const_element_reference get_elem_(index_vector index) const = 0;

/** @brief Sets the specified element to @p new_value.
*
* @param[in] index The offsets into each mode of *this for the desired
* element.
* @param[in] new_value The new value for the specified element.
*/
virtual void set_elem_(index_vector index, element_type new_value) = 0;

private:
template<typename FxnType>
dsl_reference binary_op_common_(FxnType&& fxn, label_type this_labels,
Expand Down
104 changes: 104 additions & 0 deletions cxx/include/tensorwrapper/buffer/buffer_base_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <stdexcept>
#include <tensorwrapper/buffer/buffer_fwd.hpp>
#include <tensorwrapper/types/buffer_traits.hpp>
#include <tuple>
#include <utility>

namespace tensorwrapper::buffer {

Expand Down Expand Up @@ -45,6 +47,11 @@ class BufferBaseCommon {
using layout_pointer = typename traits_type::layout_pointer;
using const_layout_reference = typename traits_type::const_layout_reference;
using rank_type = typename traits_type::rank_type;
using size_type = typename traits_type::size_type;
using element_type = typename traits_type::element_type;
using const_element_reference =
typename traits_type::const_element_reference;
using index_vector = typename traits_type::index_vector;
///@}

// -------------------------------------------------------------------------
Expand Down Expand Up @@ -93,6 +100,91 @@ class BufferBaseCommon {
return has_layout() ? layout().rank() : 0;
}

/** @brief Returns the element with the offsets specified by @p index.
*
* @param[in] index The offsets into each mode of *this for the desired
* element. The length of @p index must equal rank().
*
* @return A const reference to the element at the specified offsets.
*
* @throw std::out_of_range if the length of @p index does not equal
* rank() or if any entry in @p index is out of
* bounds. Strong throw guarantee.
* @throw std::runtime_error if *this does not support element access
* (e.g. *this is a view with no such
* implementation). Strong throw guarantee.
*/
const_element_reference get_element(index_vector index) const {
return derived_().get_elem_(std::move(index));
}

/** @brief Sets the element with the offsets specified by @p index to
* @p value.
*
* @param[in] index The offsets into each mode of *this for the desired
* element. The length of @p index must equal rank().
* @param[in] value The new value for the specified element.
*
* @throw std::out_of_range if the length of @p index does not equal
* rank() or if any entry in @p index is out of
* bounds. Strong throw guarantee.
* @throw std::runtime_error if *this does not support element access
* (e.g. *this is a view with no such
* implementation). Strong throw guarantee.
*/
void set_element(index_vector index, element_type value) {
derived_().set_elem_(std::move(index), std::move(value));
}

/** @brief Returns the element with the offsets given by @p offsets.
*
* This overload allows the offsets to be provided as an arbitrary
* number of arguments (one per mode) instead of as an index_vector. It
* is implemented in terms of get_element(index_vector) const.
*
* @tparam Offsets The types of the offsets. Expected to be integral
* types implicitly convertible to size_type.
*
* @param[in] offsets The offsets into each mode of *this for the
* desired element.
*
* @return A const reference to the element at the specified offsets.
*
* @throw std::out_of_range if the number of offsets does not equal
* rank() or if any offset is out of bounds.
* Strong throw guarantee.
*/
template<typename... Offsets>
const_element_reference get_element(Offsets... offsets) const {
return get_element(index_vector{static_cast<size_type>(offsets)...});
}

/** @brief Sets the element with the offsets given by @p offsets to
* @p value.
*
* This overload allows the offsets to be provided as an arbitrary
* number of arguments (one per mode) instead of as an index_vector. It
* is implemented in terms of set_element(index_vector, element_type).
*
* @tparam Offsets The types of the offsets. Expected to be integral
* types implicitly convertible to size_type.
*
* @param[in] offsets The offsets into each mode of *this for the
* desired element.
* @param[in] value The new value for the specified element.
*
* @throw std::out_of_range if the number of offsets does not equal
* rank() or if any offset is out of bounds.
* Strong throw guarantee.
*/
template<typename... Args>
void set_element(Args... args) {
static_assert(sizeof...(Args) >= 1,
"set_element requires at least a value argument");
set_element_unpack_(std::make_index_sequence<sizeof...(Args) - 1>{},
std::make_tuple(args...));
}

// -------------------------------------------------------------------------
// -- Utility methods
// -------------------------------------------------------------------------
Expand Down Expand Up @@ -150,6 +242,18 @@ class BufferBaseCommon {
template<typename OtherDerived>
friend class BufferBaseCommon;

/// Splits the last element of @p values off as the new value and
/// forwards the rest as the offsets for set_element(index_vector,
/// element_type). Used to implement the variadic overload of
/// set_element, since a function parameter pack can not be followed by
/// a deduced fixed parameter.
template<std::size_t... Is, typename Tuple>
void set_element_unpack_(std::index_sequence<Is...>, Tuple&& values) {
set_element(
index_vector{static_cast<size_type>(std::get<Is>(values))...},
static_cast<element_type>(std::get<sizeof...(Is)>(values)));
}

Derived& derived_() noexcept { return static_cast<Derived&>(*this); }

/// Access derived for CRTP
Expand Down
40 changes: 40 additions & 0 deletions cxx/include/tensorwrapper/buffer/buffer_view_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ namespace tensorwrapper::buffer {
* constructed or moved-from views have no layout (has_layout() is false,
* layout() throws).
*
* BufferViewBase is abstract: it declares get_elem_()/set_elem_() (required
* by BufferBaseCommon for get_element()/set_element()) as pure virtual,
* since it has no notion of how to access an aliased buffer's data on its
* own. Derived classes that do (e.g. ReplicatedView) must implement them.
*
* @tparam BufferBaseType Either BufferBase or const BufferBase.
*/
template<typename BufferBaseType>
Expand All @@ -57,7 +62,10 @@ class BufferViewBase : public BufferBaseCommon<BufferViewBase<BufferBaseType>> {
using const_pimpl_reference = const pimpl_type&;

public:
using typename my_base_type::const_element_reference;
using typename my_base_type::const_layout_reference;
using typename my_base_type::element_type;
using typename my_base_type::index_vector;
using typename my_base_type::layout_pointer;
using typename my_base_type::layout_reference;
using typename my_base_type::layout_type;
Expand Down Expand Up @@ -152,6 +160,18 @@ class BufferViewBase : public BufferBaseCommon<BufferViewBase<BufferBaseType>> {
return !(*this == rhs);
}

/** @brief Defaulted (virtual) dtor.
*
* BufferViewBase is made polymorphic so that derived classes (e.g.
* ReplicatedView) can override get_elem_()/set_elem_() and have that
* override reached even when called through the CRTP
* BufferBaseCommon<BufferViewBase<BufferBaseType>>::get_element()/
* set_element(), whose static type is frozen at BufferViewBase.
*
* @throw None No throw guarantee.
*/
virtual ~BufferViewBase() noexcept = default;

protected:
friend my_base_type;
friend class BufferBase;
Expand Down Expand Up @@ -180,6 +200,26 @@ class BufferViewBase : public BufferBaseCommon<BufferViewBase<BufferBaseType>> {
return *this == rhs;
}

/** @brief Implements the get_elem_() hook required by BufferBaseCommon.
*
* BufferViewBase itself has no notion of how to retrieve an element (it
* only aliases a layout, not a buffer's data), so this is left for
* derived classes that do have access to the aliased buffer's data
* (e.g. ReplicatedView) to implement.
*
* @param[in] index The offsets into each mode for the desired element.
*/
virtual const_element_reference get_elem_(index_vector index) const = 0;

/** @brief Implements the set_elem_() hook required by BufferBaseCommon.
*
* @see get_elem_() for why this is left to derived classes.
*
* @param[in] index The offsets into each mode for the desired element.
* @param[in] value The new value for the specified element.
*/
virtual void set_elem_(index_vector index, element_type value) = 0;

private:
void assert_pimpl_() const {
if(!m_pimpl_) {
Expand Down
11 changes: 7 additions & 4 deletions cxx/include/tensorwrapper/buffer/replicated.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,15 @@ class Replicated : public ReplicatedCommon<Replicated>, public Local {
friend my_base_type;
friend my_base_type::sliceable_base;

virtual const_element_reference get_elem_(index_vector index) const = 0;
virtual void set_elem_(index_vector index, element_type value) = 0;
using typename my_base_type::const_element_reference;
using typename my_base_type::element_reference;
using typename my_base_type::element_type;
using typename my_base_type::index_vector;

virtual slice_type slice_(index_vector first_elem,
index_vector last_elem) = 0;
index_vector last_elem) = 0;
virtual const_slice_type slice_(index_vector first_elem,
index_vector last_elem) const = 0;
index_vector last_elem) const = 0;
};

} // namespace tensorwrapper::buffer
4 changes: 2 additions & 2 deletions cxx/include/tensorwrapper/buffer/replicated_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,10 @@ class ReplicatedView
friend typename common_base_type::sliceable_base;

/// Implements get_elem for the view.
const_element_reference get_elem_(index_vector index) const;
const_element_reference get_elem_(index_vector index) const override;

/// Implements set_elem for the view.
void set_elem_(index_vector index, element_type value);
void set_elem_(index_vector index, element_type value) override;

/// Implements slice for the view.
slice_type slice_(index_vector first_elem, index_vector last_elem);
Expand Down
112 changes: 112 additions & 0 deletions cxx/include/tensorwrapper/tensor/tensor_class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <tensorwrapper/detail_/dsl_base.hpp>
#include <tensorwrapper/detail_/polymorphic_base.hpp>
#include <tensorwrapper/tensor/detail_/tensor_input.hpp>
#include <tuple>
#include <utility>

namespace tensorwrapper {
namespace detail_ {
Expand Down Expand Up @@ -85,6 +87,18 @@ class Tensor : public detail_::DSLBase<Tensor>,
/// Type of a pointer to a read-only buffer
using const_buffer_pointer = input_type::const_buffer_pointer;

/// Type of an individual element of the tensor's buffer
using element_type = input_type::buffer_base::element_type;

/// Type of a read-only reference to an individual element of the
/// tensor's buffer
using const_element_reference =
input_type::buffer_base::const_element_reference;

/// Type used to index a single element of the tensor's buffer (one
/// offset per mode, in the buffer's physical index space)
using index_vector = input_type::buffer_base::index_vector;

/// Type used to convey rank
using rank_type = typename logical_layout_type::size_type;

Expand Down Expand Up @@ -319,6 +333,92 @@ class Tensor : public detail_::DSLBase<Tensor>,
*/
rank_type rank() const;

/** @brief Retrieves an individual element of the tensor's buffer.
*
* @note This operates in the buffer's (physical) index space, not the
* tensor's logical index space (see buffer() for context on the
* distinction).
*
* @param[in] index One offset per mode of the buffer. The length of
* @p index must equal the buffer's rank.
*
* @return The requested element.
*
* @throw std::runtime_error if *this is an empty tensor. Strong throw
* guarantee.
* @throw std::out_of_range if @p index has the wrong length or contains
* an out-of-bounds offset. Strong throw
* guarantee.
*/
const_element_reference get_element(index_vector index) const;

/** @brief Retrieves an individual element of the tensor's buffer.
*
* This overload allows the offsets to be provided as an arbitrary
* number of arguments (one per mode) instead of as an index_vector. It
* is implemented in terms of get_element(index_vector) const.
*
* @tparam Offsets The types of the offsets.
*
* @param[in] offsets The offsets into each mode of the buffer.
*
* @return The requested element.
*
* @throw std::runtime_error if *this is an empty tensor. Strong throw
* guarantee.
* @throw std::out_of_range if the number of offsets does not equal the
* buffer's rank or if any offset is out of
* bounds. Strong throw guarantee.
*/
template<typename... Offsets>
const_element_reference get_element(Offsets... offsets) const {
return get_element(index_vector{
static_cast<typename index_vector::value_type>(offsets)...});
}

/** @brief Sets an individual element of the tensor's buffer.
*
* @note This operates in the buffer's (physical) index space, not the
* tensor's logical index space (see buffer() for context on the
* distinction).
*
* @param[in] index One offset per mode of the buffer. The length of
* @p index must equal the buffer's rank.
* @param[in] value The new value for the specified element.
*
* @throw std::runtime_error if *this is an empty tensor. Strong throw
* guarantee.
* @throw std::out_of_range if @p index has the wrong length or contains
* an out-of-bounds offset. Strong throw
* guarantee.
*/
void set_element(index_vector index, element_type value);

/** @brief Sets an individual element of the tensor's buffer.
*
* This overload allows the offsets to be provided as an arbitrary
* number of arguments (one per mode) instead of as an index_vector. It
* is implemented in terms of set_element(index_vector, element_type).
*
* @tparam Offsets The types of the offsets.
*
* @param[in] offsets The offsets into each mode of the buffer.
* @param[in] value The new value for the specified element.
*
* @throw std::runtime_error if *this is an empty tensor. Strong throw
* guarantee.
* @throw std::out_of_range if the number of offsets does not equal the
* buffer's rank or if any offset is out of
* bounds. Strong throw guarantee.
*/
template<typename... Args>
void set_element(Args... args) {
static_assert(sizeof...(Args) >= 1,
"set_element requires at least a value argument");
set_element_unpack_(std::make_index_sequence<sizeof...(Args) - 1>{},
std::make_tuple(args...));
}

// -------------------------------------------------------------------------
// -- Utility methods
// -------------------------------------------------------------------------
Expand Down Expand Up @@ -417,6 +517,18 @@ class Tensor : public detail_::DSLBase<Tensor>,
/// Throws if *this does not have a PIMPL.
void assert_pimpl_() const;

/// Splits the last element of @p values off as the new value and
/// forwards the rest as the offsets for set_element(index_vector,
/// element_type). Used to implement the variadic overload of
/// set_element, since a function parameter pack can not be followed by
/// a deduced fixed parameter.
template<std::size_t... Is, typename Tuple>
void set_element_unpack_(std::index_sequence<Is...>, Tuple&& values) {
set_element(index_vector{static_cast<index_vector::value_type>(
std::get<Is>(values))...},
static_cast<element_type>(std::get<sizeof...(Is)>(values)));
}

/// Object actually implementing *this
pimpl_pointer m_pimpl_;
};
Expand Down
Loading
Loading