Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transfer array from Singe #48

Merged
merged 1 commit into from
Aug 29, 2024
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
10 changes: 10 additions & 0 deletions doc/sphinx/src/using.rst
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,13 @@ data pointer. It accepts anywhere between 1 and 6 sizes.

``PortableMDArray`` also supports some simple boolean comparitors,
such as ``==`` and arithmetic such as ``+``, and ``-``.

array.hpp
^^^^^^^^^

``PortsOfCall::array`` is intended to be a drop-in replacement for ``std::array``, with the
exception that it works on GPUs. As of C++17, ``std::array::fill`` and ``std::array::swap`` are
not yet ``constexpr``, so even with the "relaxed ``constexpr``" compilation mode ``std::array`` is
not feature-complete on GPUs. This will change when those member functions become ``constexpr`` in
C++20.

270 changes: 270 additions & 0 deletions ports-of-call/array.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
#ifndef _PORTS_OF_CALL_ARRAY_HPP_
#define _PORTS_OF_CALL_ARRAY_HPP_

#include "portability.hpp"
#include "portable_errors.hpp"

#include <cstddef>
#include <iterator>
#include <tuple>

namespace PortsOfCall {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Yurlungur, is this namespacing correct?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep 👍


//! The array class is a constexpr (mostly) replacement for std::array
/*!
std::array is not fully constexpr until C++20. Currently we are relying on
"constexpr"-ness to offload to GPUs. PortsOfCall::array provides a
constexpr replacement for std::array with the following notable exceptions:
- the reverse iterators are not provided because std::reverse_iterator is
not constexpr until C++17 (could be provided with modest effort)

- the `at` function is not provided because it bounds checks and throws an
exception which we cannot do on a GPU (it could be added and made
non-constexpr)
BrendanKKrueger marked this conversation as resolved.
Show resolved Hide resolved

- no std::get (cannot be provided without undefined behavior)
*/

namespace array_detail {

template <typename T, std::size_t N>
struct bits {
BrendanKKrueger marked this conversation as resolved.
Show resolved Hide resolved
using type = T[N];
BrendanKKrueger marked this conversation as resolved.
Show resolved Hide resolved

template <typename IntT>
PORTABLE_FORCEINLINE_FUNCTION static constexpr T &ref_access(type &array, IntT index) {
return array[index];
}

template <typename IntT>
PORTABLE_FORCEINLINE_FUNCTION static constexpr T const &ref_access(type const &array,
IntT index) {
return array[index];
}

PORTABLE_FORCEINLINE_FUNCTION static constexpr T *ptr_access(type &array) {
return static_cast<T *>(array);
}

PORTABLE_FORCEINLINE_FUNCTION static constexpr T const *ptr_access(type const &array) {
return static_cast<T const *>(array);
}
};

template <typename T>
struct bits<T, 0> {
using type = T[1];

template <typename IntT>
PORTABLE_FORCEINLINE_FUNCTION static constexpr T &ref_access(type &arr, IntT) {
return arr[0];
}

template <typename IntT>
PORTABLE_FORCEINLINE_FUNCTION static constexpr T const &ref_access(type const &arr,
IntT) {
return arr[0];
}

PORTABLE_FORCEINLINE_FUNCTION static constexpr T *ptr_access(type &arr) {
return (T *)&arr;
}

PORTABLE_FORCEINLINE_FUNCTION static constexpr T const *ptr_access(type const &arr) {
return (T const *)&arr;
}
};
} // namespace array_detail

template <typename T, std::size_t N>
struct array {
// member types
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = T &;
using const_reference = T const &;
using pointer = value_type *;
using const_pointer = value_type const *;
using iterator = pointer;
using const_iterator = const_pointer;

//! The array being wrapped by this class
typename array_detail::bits<T, N>::type arr{};

//! Index operator that returns a reference to the desired element.
PORTABLE_FUNCTION constexpr reference operator[](size_type index) {
return array_detail::bits<T, N>::ref_access(arr, index);
}

//! bounds-checked equivalent to index operator
PORTABLE_FUNCTION constexpr reference at(size_type index) {
if (index >= size()) {
PORTABLE_ALWAYS_THROW_OR_ABORT("invalid index.");
}
return array_detail::bits<T, N>::ref_access(arr, index);
}

//! Index operator that returns a const reference to the desired element.
PORTABLE_FUNCTION constexpr const_reference operator[](size_type index) const {
return array_detail::bits<T, N>::ref_access(arr, index);
}

//! bounds-checked equivalent to index operator
PORTABLE_FUNCTION constexpr const_reference at(size_type index) const {
if (index >= size()) {
PORTABLE_ALWAYS_THROW_OR_ABORT("invalid index.");
}
return array_detail::bits<T, N>::ref_access(arr, index);
}

//! access first element of array
PORTABLE_FUNCTION constexpr reference front() {
return array_detail::bits<T, N>::ref_access(arr, 0);
}

//! access first element of const array
PORTABLE_FUNCTION constexpr const_reference front() const {
return array_detail::bits<T, N>::ref_access(arr, 0);
}

//! access last element of array
PORTABLE_FUNCTION constexpr reference back() {
return N ? array_detail::bits<T, N>::ref_access(arr, N - 1)
: array_detail::bits<T, N>::ref_access(arr, 0);
}

//! access first element of const array
PORTABLE_FUNCTION constexpr const_reference back() const {
return N ? array_detail::bits<T, N>::ref_access(arr, N - 1)
: array_detail::bits<T, N>::ref_access(arr, 0);
}

//! get pointer to underlying data of array
PORTABLE_FUNCTION constexpr pointer data() {
return array_detail::bits<T, N>::ptr_access(arr);
}

//! get pointer to underlying data of const array
PORTABLE_FUNCTION constexpr const_pointer data() const {
return array_detail::bits<T, N>::ptr_access(arr);
}

//! Provides an iterator pointing to the beginning of the array.
PORTABLE_FUNCTION constexpr iterator begin() { return iterator{data()}; }

//! Provides a const iterator pointing to the beginning of the const array.
PORTABLE_FUNCTION constexpr const_iterator begin() const {
return const_iterator{data()};
}

//! Provides a const iterator pointing to the beginning of the const array.
PORTABLE_FUNCTION constexpr const_iterator cbegin() const {
return const_iterator{data()};
}

//! Provides an iterator pointing to the end of the array.
PORTABLE_FUNCTION constexpr iterator end() { return iterator{data() + N}; }

//! Provides a const iterator pointing to the end of the const array.
PORTABLE_FUNCTION constexpr const_iterator end() const {
return const_iterator{data() + N};
}

//! Provides a const iterator pointing to the end of the const array.
PORTABLE_FUNCTION constexpr const_iterator cend() const {
return const_iterator{data() + N};
}

//! test if array is empty, i.e., N==0
PORTABLE_FUNCTION constexpr bool empty() const { return N == 0; }

//! return the size of an array
PORTABLE_FUNCTION constexpr size_type size() const { return N; }

//! return the maximum size an array can hold
PORTABLE_FUNCTION constexpr size_type max_size() const { return N; }

//! fill the array with a single value
PORTABLE_FUNCTION constexpr void fill(const_reference value) {
for (auto &element : *this) {
element = value;
}
}

//! swap the array contents with another
PORTABLE_FUNCTION constexpr void swap(array &other) {
using std::swap;
for (size_type i = 0; i < N; ++i) {
swap(arr[i], other.arr[i]);
}
}
};

//! specialization of swap for array
template <typename T, std::size_t N>
PORTABLE_FUNCTION constexpr void swap(array<T, N> &left, array<T, N> &right) {
left.swap(right);
}

//! make_array function to return a deduced type array
// This function was proposed for standardization but superceded by
// C++17 automatic template parameter deduction. It can still be
// useful at C++14 and earlier.
// * This implementation does not support the special handling of
// of reference wrapper

namespace array_detail {

// determine the element type of the array
// * if explicitly specified D
// * if not explicitly specified std::common_type_t<ArgTs...>

template <typename D, typename... ArgTs>
struct array_element {
using type = D;
};

template <typename... ArgTs>
struct array_element<void, ArgTs...> : std::common_type<ArgTs...> {};

} // namespace array_detail

template <typename D = void, typename... ArgTs>
PORTABLE_FUNCTION constexpr array<typename array_detail::array_element<D, ArgTs...>::type,
sizeof...(ArgTs)>
make_array(ArgTs &&...args) {
return {{std::forward<ArgTs>(args)...}};
}

} // end namespace PortsOfCall

// provide std specializations
namespace std {
/* libc++ and libstdc++ define tuple_size/tuple_element as a class and struct,
* respectively. To prevent clang from issuing a warning that these
* customization points are mismatched, we are temporarily disabling that
* diagnostic. */
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmismatched-tags"
#endif

template <typename T, std::size_t N>
struct tuple_size<PortsOfCall::array<T, N>>
: public std::integral_constant<std::size_t, N> {};

template <std::size_t I, typename T, std::size_t N>
struct tuple_element<I, PortsOfCall::array<T, N>> {
public:
static_assert(I < N, "index must be less than number of elements");

using type = T;
};

#if defined(__clang__)
#pragma clang diagnostic pop
#endif
} // namespace std

#endif // #ifndef _PORTS_OF_CALL_ARRAY_HPP_
2 changes: 1 addition & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ target_link_libraries(test_portsofcall
include(Catch)
catch_discover_tests(test_portsofcall)

target_sources(test_portsofcall PRIVATE test_portability.cpp)
target_sources(test_portsofcall PRIVATE test_portability.cpp test_array.cpp)
Loading
Loading