Skip to content

Add constant_wrapper header with divide/multiply/increment and C++26 CMake support (P3663 piece 1 of N)#449

Merged
crtrott merged 9 commits intostablefrom
copilot/split-pr-into-subpieces
Feb 20, 2026
Merged

Add constant_wrapper header with divide/multiply/increment and C++26 CMake support (P3663 piece 1 of N)#449
crtrott merged 9 commits intostablefrom
copilot/split-pr-into-subpieces

Conversation

Copy link
Contributor

Copilot AI commented Feb 19, 2026

First reviewable slice of PR #408. Establishes the constant_wrapper building block and consolidates compile-time arithmetic helpers in preparation for the P3663 submdspan canonicalization work.

CMake

  • MDSPAN_CXX_STANDARD=26 with manual selection and auto-detection (prefers 26 > 23 > 20 > 17 > 14)
  • MDSPAN_IMPL_ENABLE_P3663 option (default ON); propagated to all tests as a compile definition
  • config.hpp default-defines MDSPAN_IMPL_ENABLE_P3663=1 for header-only / Kokkos-snapshot usage

constant_wrapper.hpp (new)

All symbols in MDSPAN_IMPL_STANDARD_NAMESPACE::detail. Two paths:

Condition constant_wrapper / cw
__cpp_lib_constant_wrapper using std::constant_wrapper; using std::cw;
otherwise back-port aliasing detail::integral_constant<T, V>

Provides in both paths:

  • increment(cw<V>)cw<V+1> (compile-time)
  • is_constant_wrapper<T> trait (handles const-qualified types from constexpr auto)
  • divide<IndexT>(a, b) / multiply<IndexT>(a, b) — three overload tiers:
    1. Generic scalar (fall-through)
    2. std::integral_constant × std::integral_constantdetail::integral_constant (preserves compile-time value; divide short-circuits on zero numerator)
    3. constant_wrapper × constant_wrappercw<result> (C++26: returns std::constant_wrapper; back-port: returns detail::integral_constant)
using namespace Kokkos::detail;
constexpr auto c6 = cw<size_t(6)>;
constexpr auto c3 = cw<size_t(3)>;

static_assert(size_t(increment(c6)) == 7u);
static_assert(is_constant_wrapper<decltype(increment(c6))>);

static_assert(size_t(divide<size_t>(c6, c3)) == 2u);
static_assert(is_constant_wrapper<decltype(divide<size_t>(c6, c3))>);

static_assert(size_t(multiply<size_t>(c6, c3)) == 18u);
static_assert(is_constant_wrapper<decltype(multiply<size_t>(c6, c3))>);

submdspan_extents.hpp

  • Removed divide and multiply definitions (now in constant_wrapper.hpp)
  • Added #include "constant_wrapper.hpp"

Tests

test_constant_wrapper.cpp — registered unconditionally:

  • IntegerPlus: basic construction, value access, and (under __cpp_lib_constant_wrapper) arithmetic with std::constant_wrapper
  • Arithmetic: increment, divide (including zero-numerator), and multiply with size_t; verifies is_constant_wrapper holds for all results

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 19, 2026 19:55
… integral_constant_like

- Add C++26 CMake standard support and MDSPAN_ENABLE_P3663 option (default ON)
- Add utility headers: remove_cvref.hpp, equality_comparable.hpp,
  integral_constant_like.hpp, constant_wrapper.hpp
- Fix strided_slice static_assert to use conforming is_integral_constant_like_v
- Fix one_slice_out_of_bounds to handle convertible-to-index-type slices
- Add config.hpp default for MDSPAN_ENABLE_P3663
- Add tests: test_strided_slice, test_constant_wrapper, test_convertible_to_index_type

Co-authored-by: crtrott <9490481+crtrott@users.noreply.github.com>
Co-authored-by: crtrott <9490481+crtrott@users.noreply.github.com>
Copilot AI changed the title [WIP] Split PR into manageable review pieces Piece 1: foundational infrastructure for conforming strided_slice and integral_constant_like Feb 19, 2026
Copilot AI requested a review from crtrott February 19, 2026 19:57
Remove the extra utility headers (remove_cvref, equality_comparable,
integral_constant_like), revert strided_slice.hpp and
one_slice_out_of_bounds to original, remove test_strided_slice and
test_convertible_to_index_type. Keep only:
- CMakeLists.txt: C++26 standard + MDSPAN_ENABLE_P3663 option
- config.hpp: MDSPAN_ENABLE_P3663 default
- constant_wrapper.hpp: new header
- test_constant_wrapper.cpp: test for constant_wrapper

Co-authored-by: crtrott <9490481+crtrott@users.noreply.github.com>
Copilot AI changed the title Piece 1: foundational infrastructure for conforming strided_slice and integral_constant_like Add constant_wrapper header and C++26 CMake support (P3663 piece 1 of N) Feb 19, 2026
Copy link
Member

@crtrott crtrott left a comment

Choose a reason for hiding this comment

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

The option should be IMPL only, and constant wrapper can be unconditionally there.

…stant_wrapper unconditional

- CMakeLists.txt: option renamed MDSPAN_IMPL_ENABLE_P3663
- tests/CMakeLists.txt: define renamed MDSPAN_IMPL_ENABLE_P3663;
  test_constant_wrapper registered unconditionally (not under if-guard)
- config.hpp: default macro renamed MDSPAN_IMPL_ENABLE_P3663
- test_constant_wrapper.cpp: unconditional include of constant_wrapper.hpp
  (no #if MDSPAN_ENABLE_P3663 / #error guard)

Co-authored-by: crtrott <9490481+crtrott@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 19, 2026

The option should be IMPL only, and constant wrapper can be unconditionally there.

Done in 8cc9103. Renamed to MDSPAN_IMPL_ENABLE_P3663 throughout, and test_constant_wrapper is now registered unconditionally so constant_wrapper.hpp is always tested.

Copilot AI requested a review from crtrott February 19, 2026 21:09
Copy link
Member

@crtrott crtrott left a comment

Choose a reason for hiding this comment

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

The test added compile definition and the config change are not technically necessary in this PR but it's fine. Establishes for fellow up work the option. So leave it like it is.

@crtrott crtrott marked this pull request as ready for review February 19, 2026 21:11
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.

Co-authored-by: Mark Hoemmen <mhoemmen@users.noreply.github.com>
Copy link
Member

@crtrott crtrott left a comment

Choose a reason for hiding this comment

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

Move constant_wrapper to detail namespace.

- constant_wrapper.hpp: move constant_wrapper, cw, increment, and
  is_constant_wrapper all into MDSPAN_IMPL_STANDARD_NAMESPACE::detail
  (previously constant_wrapper/cw were in the enclosing namespace);
  in the back-port path, constant_wrapper now aliases integral_constant
  directly (no full qualification needed inside detail)
- test_constant_wrapper.cpp: update all using-declarations to
  MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw / ::constant_wrapper

Co-authored-by: crtrott <9490481+crtrott@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 19, 2026

Move constant_wrapper to detail namespace.

Done in 48aa88c. constant_wrapper, cw, increment, and is_constant_wrapper are all now inside MDSPAN_IMPL_STANDARD_NAMESPACE::detail. The back-port path also simplifies to aliasing integral_constant directly (no full qualification needed since both live in detail).

…rithmetic test

- constant_wrapper.hpp: restructure into #if/__cpp_lib_constant_wrapper sections;
  fix increment/is_constant_wrapper to use single-NTTP form for std path and
  correct back-port form; add const-stripping specialization for is_constant_wrapper
  (constexpr variables are const, so is_constant_wrapper<const T> must work);
  move generic divide/multiply + std::integral_constant overloads here from
  submdspan_extents.hpp; add constant_wrapper overloads for both paths
- submdspan_extents.hpp: add #include "constant_wrapper.hpp"; remove divide/multiply
- test_constant_wrapper.cpp: add TestConstantWrapper.Arithmetic testing
  increment, divide, and multiply with size_t, including is_constant_wrapper checks

Co-authored-by: crtrott <9490481+crtrott@users.noreply.github.com>
Copilot AI changed the title Add constant_wrapper header and C++26 CMake support (P3663 piece 1 of N) Add constant_wrapper header with divide/multiply/increment and C++26 CMake support (P3663 piece 1 of N) Feb 19, 2026
Comment on lines +34 to +40
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) >;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't need to duplicate the definition, as it's the same on both sides -- unless you want the std::constant_wrapper side to use the overloaded arithmetic operators!

Copy link
Contributor

@mhoemmen mhoemmen left a comment

Choose a reason for hiding this comment

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

The point of increment, divide, etc. is that we don't have to implement them twice. If they have exactly the same definitions on both sides, then it's just one definition that we can lift outside the macro branch.

Signed-off-by: Christian Trott <crtrott@sandia.gov>
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.

@crtrott crtrott merged commit bd0f709 into stable Feb 20, 2026
30 checks passed
@crtrott crtrott deleted the copilot/split-pr-into-subpieces branch February 20, 2026 18:28
Copilot stopped work on behalf of crtrott due to an error February 20, 2026 18:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants