Skip to content

Commit

Permalink
replace constexpr if in p0009 bits
Browse files Browse the repository at this point in the history
Replace use of constexpr if in headers used to implement p0009 in
order to allow compilation for C++14 without C++17 extensions.
  • Loading branch information
oliverlee committed Feb 13, 2024
1 parent 72b180f commit 430506a
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 97 deletions.
13 changes: 5 additions & 8 deletions include/experimental/__p0009_bits/extents.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@

#pragma once
#include "dynamic_extent.hpp"
#include "utility.hpp"

#ifdef __cpp_lib_span
#include <span>
#endif
#include <array>
#include <type_traits>

#include <cinttypes>

Expand Down Expand Up @@ -537,14 +539,9 @@ template <class IndexType, size_t... Extents> class extents {
MDSPAN_INLINE_FUNCTION friend constexpr bool
operator==(const extents &lhs,
const extents<OtherIndexType, OtherExtents...> &rhs) noexcept {
if constexpr (rank() != extents<OtherIndexType, OtherExtents...>::rank()) {
return false;
} else {
using common_t = std::common_type_t<index_type, OtherIndexType>;
for (size_type r = 0; r < m_rank; r++)
if(static_cast<common_t>(rhs.extent(r)) != static_cast<common_t>(lhs.extent(r))) return false;
}
return true;
return
rank() == extents<OtherIndexType, OtherExtents...>::rank() and
detail::rankwise_equal(detail::with_rank<rank()>{}, rhs, lhs, detail::extent);
}

#if !(MDSPAN_HAS_CXX_20)
Expand Down
19 changes: 6 additions & 13 deletions include/experimental/__p0009_bits/layout_left.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
#include "macros.hpp"
#include "trait_backports.hpp"
#include "extents.hpp"
#include "layout_stride.hpp"
#include "utility.hpp"
#include "../__p2642_bits/layout_padded_fwd.hpp"
#include <cassert>
#include <type_traits>

namespace MDSPAN_IMPL_STANDARD_NAMESPACE {
Expand Down Expand Up @@ -133,11 +134,11 @@ class layout_left::mapping {
: __extents(__other.extents())
{
MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::
check_padded_layout_converting_constructor_mandates<extents_type,
_Mapping>();
check_padded_layout_converting_constructor_mandates<
extents_type, _Mapping>(detail::with_rank<extents_type::rank()>{});
MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::
check_padded_layout_converting_constructor_preconditions<
extents_type>(__other);
extents_type>(detail::with_rank<extents_type::rank()>{}, __other);
}
#endif

Expand All @@ -157,15 +158,7 @@ class layout_left::mapping {
* other.required_span_size() is a representable value of type index_type
*/
#if !defined(_MDSPAN_HAS_CUDA) && !defined(_MDSPAN_HAS_HIP) && !defined(NDEBUG)
if constexpr (extents_type::rank() > 0) {
index_type stride = 1;
using common_t = std::common_type_t<index_type, typename OtherExtents::index_type>;
for(rank_type r=0; r<__extents.rank(); r++) {
if(static_cast<common_t>(stride) != static_cast<common_t>(other.stride(r)))
std::abort(); // ("Assigning layout_stride to layout_left with invalid strides.");
stride *= __extents.extent(r);
}
}
detail::terminate_if_invalid_strides(detail::with_rank<extents_type::rank()>{}, layout_left{}, __extents, other);
#endif
}

Expand Down
19 changes: 5 additions & 14 deletions include/experimental/__p0009_bits/layout_right.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
#include "macros.hpp"
#include "trait_backports.hpp"
#include "extents.hpp"
#include <stdexcept>
#include "layout_stride.hpp"
#include "utility.hpp"
#include "../__p2642_bits/layout_padded_fwd.hpp"

namespace MDSPAN_IMPL_STANDARD_NAMESPACE {
Expand Down Expand Up @@ -134,11 +134,11 @@ class layout_right::mapping {
: __extents(__other.extents())
{
MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::
check_padded_layout_converting_constructor_mandates<extents_type,
_Mapping>();
check_padded_layout_converting_constructor_mandates<
extents_type, _Mapping>(detail::with_rank<extents_type::rank()>{});
MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::
check_padded_layout_converting_constructor_preconditions<
extents_type>(__other);
extents_type>(detail::with_rank<extents_type::rank()>{}, __other);
}
#endif

Expand All @@ -158,16 +158,7 @@ class layout_right::mapping {
* other.required_span_size() is a representable value of type index_type
*/
#if !defined(_MDSPAN_HAS_CUDA) && !defined(_MDSPAN_HAS_HIP) && !defined(NDEBUG)
if constexpr (extents_type::rank() > 0) {
index_type stride = 1;
for(rank_type r=__extents.rank(); r>0; r--) {
if(stride != static_cast<index_type>(other.stride(r-1))) {
// Note this throw will lead to a terminate if triggered since this function is marked noexcept
throw std::runtime_error("Assigning layout_stride to layout_right with invalid strides.");
}
stride *= __extents.extent(r-1);
}
}
detail::terminate_if_invalid_strides(detail::with_rank<extents_type::rank()>{}, layout_right{}, __extents, other);
#endif
}

Expand Down
124 changes: 85 additions & 39 deletions include/experimental/__p0009_bits/layout_stride.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@
#include "extents.hpp"
#include "trait_backports.hpp"
#include "compressed_pair.hpp"
#include "utility.hpp"

#if !defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS)
# include "no_unique_address.hpp"
#endif

#include <algorithm>
#include <numeric>
#include <array>
#include <cstdlib>
#include <numeric>

#ifdef __cpp_lib_span
#include <span>
#endif
Expand All @@ -38,11 +41,11 @@ namespace MDSPAN_IMPL_STANDARD_NAMESPACE {

struct layout_left {
template<class Extents>
class mapping;
class mapping;
};
struct layout_right {
template<class Extents>
class mapping;
class mapping;
};

namespace detail {
Expand Down Expand Up @@ -79,6 +82,7 @@ namespace detail {
std::bool_constant<M::is_always_unique()>::value;
};
#endif

} // namespace detail

struct layout_stride {
Expand Down Expand Up @@ -225,7 +229,11 @@ struct layout_stride {
// Can't use defaulted parameter in the __deduction_workaround template because of a bug in MSVC warning C4348.
using __impl = __deduction_workaround<std::make_index_sequence<Extents::rank()>>;

static constexpr __strides_storage_t strides_storage(std::true_type) {
static constexpr __strides_storage_t strides_storage(detail::with_rank<0>) {
return {};
}
template <std::size_t N>
static constexpr __strides_storage_t strides_storage(detail::with_rank<N>) {
__strides_storage_t s{};

extents_type e;
Expand All @@ -237,9 +245,6 @@ struct layout_stride {

return s;
}
static constexpr __strides_storage_t strides_storage(std::false_type) {
return {};
}

//----------------------------------------------------------------------------

Expand All @@ -262,7 +267,7 @@ struct layout_stride {
: __base_t(__base_t{__member_pair_t(
#endif
extents_type(),
__strides_storage_t(strides_storage(std::integral_constant<bool, (extents_type::rank() > 0)>{}))
__strides_storage_t(strides_storage(detail::with_rank<extents_type::rank()>{}))
#if defined(_MDSPAN_USE_ATTRIBUTE_NO_UNIQUE_ADDRESS)
}
#else
Expand Down Expand Up @@ -444,32 +449,48 @@ struct layout_stride {
MDSPAN_INLINE_FUNCTION static constexpr bool is_always_strided() noexcept { return true; }

MDSPAN_INLINE_FUNCTION static constexpr bool is_unique() noexcept { return true; }
MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 bool is_exhaustive() const noexcept {
if constexpr (extents_type::rank() == 0)
return true;
else {
index_type span_size = required_span_size();
if (span_size == static_cast<index_type>(0)) {
if constexpr (extents_type::rank() == 1) {
return stride(0) == 1;
} else {
rank_type r_largest = 0;
for (rank_type r = 1; r < extents_type::rank(); r++) {
if (stride(r) > stride(r_largest)) {
r_largest = r;
}
}
for (rank_type r = 0; r < extents_type::rank(); r++) {
if (extents().extent(r) == 0 && r != r_largest) {
return false;
}
}
return true;
}
} else {
return required_span_size() == __get_size(extents(), std::make_index_sequence<extents_type::rank()>());

private:
constexpr bool exhaustive_for_nonzero_span_size() const
{
return required_span_size() == __get_size(extents(), std::make_index_sequence<extents_type::rank()>());
}

constexpr bool is_exhaustive_impl(detail::with_rank<0>) const
{
return true;
}
constexpr bool is_exhaustive_impl(detail::with_rank<1>) const
{
if (required_span_size() != static_cast<index_type>(0)) {
return exhaustive_for_nonzero_span_size();
}
return stride(0) == 1;
}
template <std::size_t N>
constexpr bool is_exhaustive_impl(detail::with_rank<N>) const
{
if (required_span_size() != static_cast<index_type>(0)) {
return exhaustive_for_nonzero_span_size();
}

rank_type r_largest = 0;
for (rank_type r = 1; r < extents_type::rank(); r++) {
if (stride(r) > stride(r_largest)) {
r_largest = r;
}
}
for (rank_type r = 0; r < extents_type::rank(); r++) {
if (extents().extent(r) == 0 && r != r_largest) {
return false;
}
}
return true;
}

public:
MDSPAN_INLINE_FUNCTION _MDSPAN_CONSTEXPR_14 bool is_exhaustive() const noexcept {
return is_exhaustive_impl(detail::with_rank<extents_type::rank()>{});
}
MDSPAN_INLINE_FUNCTION static constexpr bool is_strided() noexcept { return true; }

Expand Down Expand Up @@ -498,15 +519,9 @@ struct layout_stride {
#endif
MDSPAN_INLINE_FUNCTION
friend constexpr bool operator==(const mapping& x, const StridedLayoutMapping& y) noexcept {
bool strides_match = true;
if constexpr (extents_type::rank() > 0) {
using common_t = std::common_type_t<index_type, typename StridedLayoutMapping::index_type>;
for(rank_type r = 0; r < extents_type::rank(); r++)
strides_match = strides_match && (static_cast<common_t>(x.stride(r)) == static_cast<common_t>(y.stride(r)));
}
return (x.extents() == y.extents()) &&
(__impl::__OFFSET(y) == static_cast<typename StridedLayoutMapping::index_type>(0)) &&
strides_match;
detail::rankwise_equal(detail::with_rank<extents_type::rank()>{}, x, y, detail::stride);
}

// This one is not technically part of the proposal. Just here to make implementation a bit more optimal hopefully
Expand Down Expand Up @@ -560,4 +575,35 @@ struct layout_stride {
};
};

namespace detail {

template <class Layout, class Extents, class Mapping>
constexpr void terminate_if_invalid_strides(with_rank<0>, Layout, const Extents&, const Mapping&)
{}

template <std::size_t N, class Layout, class Extents, class Mapping>
constexpr void terminate_if_invalid_strides(with_rank<N>, Layout, const Extents& ext, const Mapping& other)
{
static_assert(std::is_same<typename Mapping::layout_type, layout_stride>::value and
(std::is_same<Layout, layout_left>::value or
std::is_same<Layout, layout_right>::value)
, "This function is only intended to validate construction of "
"a layout_left or layout_right mapping from a layout_stride mapping.");

constexpr auto is_left = std::is_same<Layout, layout_left>::value;

typename Extents::index_type stride = 1;

for (std::size_t r = 0; r < N; r++) {
const std::size_t s = is_left ? r : N - 1 - r;

if (not common_integral_compare(stride, other.stride(s))) {
// assigning to layout_{left,right} with invalid strides
std::abort();
}
stride *= ext.extent(s);
}
}

} // namespace detail
} // end namespace MDSPAN_IMPL_STANDARD_NAMESPACE
62 changes: 62 additions & 0 deletions include/experimental/__p0009_bits/utility.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#pragma once

#include <cstddef>
#include <type_traits>

namespace MDSPAN_IMPL_STANDARD_NAMESPACE {
namespace detail {

// type alias used for rank-based tag dispatch
//
// this is used to enable alternatives to constexpr if when building for C++14
//
template <std::size_t N>
using with_rank = std::integral_constant<std::size_t, N>;

template <class I1, class I2>
constexpr bool common_integral_compare(I1 x, I2 y)
{
static_assert(std::is_integral<I1>::value and
std::is_integral<I2>::value, "");

using I = std::common_type_t<I1, I2>;
return static_cast<I>(x) == static_cast<I>(y);
}

template <class T1, class T2, class F>
constexpr bool rankwise_equal(with_rank<0>, const T1&, const T2&, F)
{
return true;
}
template <std::size_t N, class T1, class T2, class F>
constexpr bool rankwise_equal(with_rank<N>, const T1& x, const T2& y, F func)
{
bool match = true;

for (std::size_t r = 0; r < N; r++) {
match = match && common_integral_compare(func(x, r), func(y, r));
}

return match;
}

constexpr struct
{
template <class T, class I>
constexpr auto operator()(const T& x, I i) const
{
return x.extent(i);
}
} extent;

constexpr struct
{
template <class T, class I>
constexpr auto operator()(const T& x, I i) const
{
return x.stride(i);
}
} stride;

} // namespace detail
} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE
Loading

0 comments on commit 430506a

Please sign in to comment.