Skip to content
Merged
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ CMakeUserPresets.json
out
build*
Makefile
_codeql_build_dir/
_codeql_detected_source_root
16 changes: 14 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ option(MDSPAN_GENERATE_STD_NAMESPACE_TARGETS "Whether to generate and install ta

# Option to override which C++ standard to use
set(MDSPAN_CXX_STANDARD DETECT CACHE STRING "Override the default CXX_STANDARD to compile with.")
set_property(CACHE MDSPAN_CXX_STANDARD PROPERTY STRINGS DETECT 14 17 20 23)
set_property(CACHE MDSPAN_CXX_STANDARD PROPERTY STRINGS DETECT 14 17 20 23 26)

option(MDSPAN_ENABLE_CONCEPTS "Try to enable concepts support by giving extra flags." On)

option(MDSPAN_IMPL_ENABLE_P3663 "Enable implementation of P3663 (Future-proof submdspan_mapping)." On)

################################################################################

# Decide on the standard to use
Expand Down Expand Up @@ -63,8 +65,18 @@ elseif(MDSPAN_CXX_STANDARD STREQUAL "23")
else()
message(FATAL_ERROR "Requested MDSPAN_CXX_STANDARD \"23\" not supported by provided C++ compiler")
endif()
elseif(MDSPAN_CXX_STANDARD STREQUAL "26")
Copy link
Contributor

Choose a reason for hiding this comment

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

I've tested this with GCC trunk (16.0.1). It emits the warning but accepts the CMake option nevertheless.

if("cxx_std_26" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
message(STATUS "Using C++26 standard")
set(CMAKE_CXX_STANDARD 26)
else()
message(WARNING "Requested MDSPAN_CXX_STANDARD \"26\" not supported by provided C++ compiler")
endif()
else()
if("cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
if("cxx_std_26" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
set(CMAKE_CXX_STANDARD 26)
message(STATUS "Detected support for C++26 standard")
elseif("cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
set(CMAKE_CXX_STANDARD 23)
message(STATUS "Detected support for C++23 standard")
elseif("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
Expand Down
4 changes: 4 additions & 0 deletions include/experimental/__p0009_bits/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,7 @@ static_assert(MDSPAN_IMPL_CPLUSPLUS >= MDSPAN_CXX_STD_14, "mdspan requires C++14
# define MDSPAN_IMPL_OP5(mds, a, b, c, d, e) mds(a,b,c,d,e)
# define MDSPAN_IMPL_OP6(mds, a, b, c, d, e, f) mds(a,b,c,d,e,f)
#endif

#if ! defined(MDSPAN_IMPL_ENABLE_P3663)
# define MDSPAN_IMPL_ENABLE_P3663 1
#endif
157 changes: 157 additions & 0 deletions include/experimental/__p2630_bits/constant_wrapper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//@HEADER
// ************************************************************************
//
// Kokkos v. 4.0
// Copyright (2022) National Technology & Engineering
// Solutions of Sandia, LLC (NTESS).
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions.
// See https://kokkos.org/LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//@HEADER

#pragma once

#include "../__p0009_bits/utility.hpp"
#include <type_traits>

namespace MDSPAN_IMPL_STANDARD_NAMESPACE {
namespace detail {

// ============================================================
// constant_wrapper, cw, increment, is_constant_wrapper
// ============================================================

#if defined(__cpp_lib_constant_wrapper)

using std::constant_wrapper;
using std::cw;

template<class T>
constexpr bool is_constant_wrapper = false;

template<class T>
constexpr bool is_constant_wrapper<const T> = is_constant_wrapper<T>;

template<auto Value>
constexpr bool is_constant_wrapper<constant_wrapper<Value>> = true;

#else // back-port: constant_wrapper = detail::integral_constant

template<auto Value, class T = decltype(Value)>
using constant_wrapper = integral_constant<T, Value>;

template<auto Value>
constexpr auto cw = constant_wrapper<Value>{};

template<class T>
constexpr bool is_constant_wrapper = false;

template<class T>
constexpr bool is_constant_wrapper<const T> = is_constant_wrapper<T>;

// integral_constant is the underlying type of the back-port constant_wrapper
// (alias templates can't be used in partial specialization patterns)
template<class Type, Type Value>
constexpr bool is_constant_wrapper<integral_constant<Type, Value>> = true;

#endif // __cpp_lib_constant_wrapper

// ============================================================
// increment function for constant wrapper
// ============================================================

template<auto Value>
MDSPAN_INLINE_FUNCTION
constexpr auto
increment([[maybe_unused]] constant_wrapper<Value> x) {
using value_type = typename decltype(x)::value_type;
return cw< value_type(Value) + value_type(1) >;
}


// ============================================================
// Generic divide / multiply (scalar fall-through)
// ============================================================

template <class IndexT, class T0, class T1>
MDSPAN_INLINE_FUNCTION
constexpr auto divide(const T0 &v0, const T1 &v1) {
return IndexT(v0) / IndexT(v1);
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this is what the current code does, so I'm not complaining : - ) . Let me just think out loud for a bit.

The casting behavior here looks not consistent with the behavior for constant_wrapper or integral_constant, where the cast happens after the division instead of before. I want to reason through whether this should be of concern.

In the P3663 case, v0 and v1 should have already been canonicalized, that is, converted to index_type or constant_wrapper thereof. Thus, T0 (or T0::value_type), T1 (or T1::value_type), and IndexT should all be the same.

In the no-P3663 case, I'm guessing that T0 and T1 are any types convertible to index_type (the current implementation says "convertible to size_t" which isn't quite right). The arithmetic then happens after the conversion. That's needed if the input type isn't integral, but otherwise it's different behavior than the integral_constant overload. I don't think it's worth fixing this because the no-P3663 case is nonconforming anyway.

}

template <class IndexT, class T0, class T1>
MDSPAN_INLINE_FUNCTION
constexpr auto multiply(const T0 &v0, const T1 &v1) {
return IndexT(v0) * IndexT(v1);
}

// ============================================================
// Compile-time-preserving overloads for std::integral_constant
// (used when strided_slice template parameters are std::integral_constant)
// ============================================================

template <class IndexT, class T0, T0 v0, class T1, T1 v1>
MDSPAN_INLINE_FUNCTION
constexpr auto divide(const std::integral_constant<T0, v0> &,
const std::integral_constant<T1, v1> &) {
// Short-circuit division by zero
// (used for strided_slice with zero extent/stride)
return integral_constant<IndexT, v0 == 0 ? 0 : v0 / v1>();
}

template <class IndexT, class T0, T0 v0, class T1, T1 v1>
MDSPAN_INLINE_FUNCTION
constexpr auto multiply(const std::integral_constant<T0, v0> &,
const std::integral_constant<T1, v1> &) {
return integral_constant<IndexT, v0 * v1>();
}

// ============================================================
// Compile-time-preserving overloads for constant_wrapper
// ============================================================

#if defined(__cpp_lib_constant_wrapper)

// std::constant_wrapper takes a single NTTP <auto V>
template <class IndexT, auto v0, auto v1>
MDSPAN_INLINE_FUNCTION
constexpr auto divide(const constant_wrapper<v0> &,
const constant_wrapper<v1> &) {
constexpr IndexT result =
IndexT(v0) == IndexT(0) ? IndexT(0) : IndexT(v0) / IndexT(v1);
return cw<result>;
}

template <class IndexT, auto v0, auto v1>
MDSPAN_INLINE_FUNCTION
constexpr auto multiply(const constant_wrapper<v0> &,
const constant_wrapper<v1> &) {
constexpr IndexT result = IndexT(v0) * IndexT(v1);
return cw<result>;
}

#else // back-port: constant_wrapper<v, T> = integral_constant<T, v>

template <class IndexT, class T0, T0 v0, class T1, T1 v1>
MDSPAN_INLINE_FUNCTION
constexpr auto divide(const constant_wrapper<v0, T0> &,
const constant_wrapper<v1, T1> &) {
return integral_constant<IndexT, v0 == 0 ? 0 : v0 / v1>();
}

template <class IndexT, class T0, T0 v0, class T1, T1 v1>
MDSPAN_INLINE_FUNCTION
constexpr auto multiply(const constant_wrapper<v0, T0> &,
const constant_wrapper<v1, T1> &) {
return integral_constant<IndexT, v0 * v1>();
}

#endif // __cpp_lib_constant_wrapper

} // namespace detail
} // namespace MDSPAN_IMPL_STANDARD_NAMESPACE
31 changes: 1 addition & 30 deletions include/experimental/__p2630_bits/submdspan_extents.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <complex>

#include "constant_wrapper.hpp"
#include "strided_slice.hpp"
#include "../__p0009_bits/utility.hpp"

Expand Down Expand Up @@ -261,36 +262,6 @@ stride_of(const strided_slice<OffsetType, ExtentType, StrideType> &r) {
return r.stride;
}

// divide which can deal with integral constant preservation
template <class IndexT, class T0, class T1>
MDSPAN_INLINE_FUNCTION
constexpr auto divide(const T0 &v0, const T1 &v1) {
return IndexT(v0) / IndexT(v1);
}

template <class IndexT, class T0, T0 v0, class T1, T1 v1>
MDSPAN_INLINE_FUNCTION
constexpr auto divide(const std::integral_constant<T0, v0> &,
const std::integral_constant<T1, v1> &) {
// cutting short division by zero
// this is used for strided_slice with zero extent/stride
return integral_constant<IndexT, v0 == 0 ? 0 : v0 / v1>();
}

// multiply which can deal with integral constant preservation
template <class IndexT, class T0, class T1>
MDSPAN_INLINE_FUNCTION
constexpr auto multiply(const T0 &v0, const T1 &v1) {
return IndexT(v0) * IndexT(v1);
}

template <class IndexT, class T0, T0 v0, class T1, T1 v1>
MDSPAN_INLINE_FUNCTION
constexpr auto multiply(const std::integral_constant<T0, v0> &,
const std::integral_constant<T1, v1> &) {
return integral_constant<IndexT, v0 * v1>();
}

// compute new static extent from range, preserving static knowledge
template <class Arg0, class Arg1> struct StaticExtentFromRange {
constexpr static size_t value = dynamic_extent;
Expand Down
10 changes: 9 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ function(mdspan_add_test name)
target_compile_definitions(${name}
PUBLIC
MDSPAN_IMPL_CHECK_PRECONDITION=$<BOOL:${ARGUMENT_ENABLE_PRECONDITIONS}>
)
)
if(MDSPAN_IMPL_ENABLE_P3663)
target_compile_definitions(${name}
PUBLIC
MDSPAN_IMPL_ENABLE_P3663=1
)
endif()
endfunction()

if(MDSPAN_USE_SYSTEM_GTEST)
Expand Down Expand Up @@ -105,3 +111,5 @@ if((CMAKE_CXX_COMPILER_ID STREQUAL Clang) OR ((CMAKE_CXX_COMPILER_ID STREQUAL GN
add_subdirectory(libcxx-backports)
endif()
endif()

mdspan_add_test(test_constant_wrapper)
Loading