On Wed, Aug 13, 2025 at 12:24 PM Luc Grosheintz <luc.groshei...@gmail.com> wrote:
> This is a partial implementation of P2781R8. It adds std::cw and > std::constant_wrapper, but doesn't modify __integral_constant_like for > span/mdspan. > > libstdc++-v3/ChangeLog: > > * include/bits/version.def (constant_wrapper): Add. > * include/bits/version.h: Regenerate. > * include/std/type_traits (_CwFixedValue): New class. > (_IndexSequence): New struct. > (_BuildIndexSequence): New struct. > (_ConstExprParam): New concept. > (_CwOperators): New struct. > (constant_wrapper): New struct. > (cw): New global constant. > * testsuite/20_util/constant_wrapper/adl.cc: New test. > * testsuite/20_util/constant_wrapper/ex.cc: New test. > * testsuite/20_util/constant_wrapper/generic.cc: New test. > * testsuite/20_util/constant_wrapper/version.cc: New test. > > Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com> > --- > > This is a preview to discuss certain points: > > 1. Is there a better way of initializing one C array with > another? > I was thinking about it, but haven't come up with anything better. > > 2. If no, should we refactor <utility>? If we include <utility> from > <type_traits> we're creating a circular dependency (<utility> includes > <type_traits>). One solution is to lift the required code into > a <bits/integer_sequence.h> and then include that file from both > <utility> and <type_traits>. > This seems like a good direction for me. > > 3. Is the type of test that will "check" each operator meaningful? > For me it would be important to test member and non-member, there was a lot of issues in the original wording, where the non-member version was assumed. For test, I would test, if: operator on cw<> produce cw<> operator on mix of cw<> and not-wrapped produce integers The mix of cw and non-wrapped should work for non-assignment operators, but I do not think compound assignment, or subscript are supported. There is interesting case for call operator, where non-cw calls are supported for functions pointers, but not for functors: cw<function-pointer>(1, 2) -> calls function-pointer(1, 2) > > libstdc++-v3/include/bits/version.def | 8 + > libstdc++-v3/include/bits/version.h | 10 + > libstdc++-v3/include/std/type_traits | 354 ++++++++++++++++++ > .../testsuite/20_util/constant_wrapper/adl.cc | 42 +++ > .../testsuite/20_util/constant_wrapper/ex.cc | 45 +++ > .../20_util/constant_wrapper/generic.cc | 78 ++++ > .../20_util/constant_wrapper/version.cc | 11 + > 7 files changed, 548 insertions(+) > create mode 100644 libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc > create mode 100644 libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc > create mode 100644 > libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > create mode 100644 > libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc > > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > index dbe2cb8f175..c8253e52168 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -393,6 +393,14 @@ ftms = { > }; > }; > > +ftms = { > + name = constant_wrapper; > + values = { > + v = 202506; > + cxxmin = 26; > + }; > +}; > + > ftms = { > name = has_unique_object_representations; > values = { > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > index 7bb6016df68..c0512fe6579 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -430,6 +430,16 @@ > #endif /* !defined(__cpp_lib_byte) && defined(__glibcxx_want_byte) */ > #undef __glibcxx_want_byte > > +#if !defined(__cpp_lib_constant_wrapper) > +# if (__cplusplus > 202302L) > +# define __glibcxx_constant_wrapper 202506L > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_constant_wrapper) > +# define __cpp_lib_constant_wrapper 202506L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_constant_wrapper) && > defined(__glibcxx_want_constant_wrapper) */ > +#undef __glibcxx_want_constant_wrapper > + > #if !defined(__cpp_lib_has_unique_object_representations) > # if (__cplusplus >= 201703L) && > (defined(_GLIBCXX_HAVE_BUILTIN_HAS_UNIQ_OBJ_REP)) > # define __glibcxx_has_unique_object_representations 201606L > diff --git a/libstdc++-v3/include/std/type_traits > b/libstdc++-v3/include/std/type_traits > index ff23544fbf0..0c87b5bf444 100644 > --- a/libstdc++-v3/include/std/type_traits > +++ b/libstdc++-v3/include/std/type_traits > @@ -41,6 +41,7 @@ > > #define __glibcxx_want_bool_constant > #define __glibcxx_want_bounded_array_traits > +#define __glibcxx_want_constant_wrapper > #define __glibcxx_want_has_unique_object_representations > #define __glibcxx_want_integral_constant_callable > #define __glibcxx_want_is_aggregate > @@ -4280,6 +4281,359 @@ template<typename _Ret, typename _Fn, typename... > _Args> > > #endif // C++2a > > +#ifdef __cpp_lib_constant_wrapper // C++ >= 26 > + template<typename _Tp> > + struct _CwFixedValue > + { > + using _S_type = _Tp; > + > + constexpr > + _CwFixedValue(_S_type __v) noexcept > + : _M_data(__v) { } > + > + _S_type _M_data; > + }; > + > + template<size_t... _Indices> > + struct _IndexSequence > + { }; > + > + template<size_t _Nm, size_t... _Indices> > + struct _BuildIndexSequence > + { > + using _S_type = _BuildIndexSequence<_Nm, _Indices..., > sizeof...(_Indices)>::_S_type; > + }; > + > + template<size_t _Nm, size_t... _Indices> > + requires (sizeof...(_Indices) == _Nm) > + struct _BuildIndexSequence<_Nm, _Indices...> > + { > + using _S_type = _IndexSequence<_Indices...>; > + }; > + > + template<typename _Tp, size_t _Extent> > + struct _CwFixedValue<_Tp[_Extent]> { > + using _S_type = _Tp[_Extent]; > + > + constexpr > + _CwFixedValue(_Tp (&__arr)[_Extent]) noexcept > + : _CwFixedValue(__arr, typename > _BuildIndexSequence<_Extent>::_S_type()) > + { } > + > + template<size_t... _Indices> > + constexpr > + _CwFixedValue(_Tp (&__arr)[_Extent], _IndexSequence<_Indices...>) > noexcept > + : _M_data{__arr[_Indices]...} > + { } > + > + _Tp _M_data[_Extent]; > + }; > + > + template<typename _Tp, size_t _Extent> > + _CwFixedValue(_Tp (&)[_Extent]) -> _CwFixedValue<_Tp[_Extent]>; > + > + template<_CwFixedValue _Tp, > + typename = typename decltype(_CwFixedValue(_Tp))::_S_type> > + struct constant_wrapper; > + > + > + template<typename _Tp> > + concept _ConstExprParam = requires > + { > + typename constant_wrapper<_Tp::value>; > + }; > + > + struct _CwOperators > + { > + template<_ConstExprParam _Tp> > + friend constexpr auto > + operator+(_Tp) noexcept -> constant_wrapper<(+_Tp::value)> > + { return {}; } > + template<_ConstExprParam _Tp> > + friend constexpr auto > + operator-(_Tp) noexcept -> constant_wrapper<(-_Tp::value)> > + { return {}; } > + template<_ConstExprParam _Tp> > + friend constexpr auto > + operator~(_Tp) noexcept -> constant_wrapper<(~_Tp::value)> > + { return {}; } > + template<_ConstExprParam _Tp> > + friend constexpr auto > + operator!(_Tp) noexcept -> constant_wrapper<(!_Tp::value)> > + { return {}; } > + template<_ConstExprParam _Tp> > + friend constexpr auto > + operator&(_Tp) noexcept -> constant_wrapper<(&_Tp::value)> > + { return {}; } > + template<_ConstExprParam _Tp> > + friend constexpr auto > + operator*(_Tp) noexcept -> constant_wrapper<(*_Tp::value)> > + { return {}; } > + > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator+(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value + _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator-(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value - _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator*(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value * _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator/(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value / _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator%(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value % _Right::value)> > + { return {}; } > + > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator<<(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value << _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator>>(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value >> _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator&(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value & _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator|(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value | _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator^(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value ^ _Right::value)> > + { return {}; } > + > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + requires (!is_constructible_v<bool, decltype(_Left::value)> > + || !is_constructible_v<bool, decltype(_Right::value)>) > + friend constexpr auto > + operator&&(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value && _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + requires (!is_constructible_v<bool, decltype(_Left::value)> > + || !is_constructible_v<bool, decltype(_Right::value)>) > + friend constexpr auto > + operator||(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value || _Right::value)> > + { return {}; } > + > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator<=>(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value <=> _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator<(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value < _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator<=(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value <= _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator==(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value == _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator!=(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value != _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator>(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value > _Right::value)> > + { return {}; } > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator>=(_Left, _Right) noexcept > + -> constant_wrapper<(_Left::value >= _Right::value)> > + { return {}; } > + > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator,(_Left, _Right) noexcept = delete; > + template<_ConstExprParam _Left, _ConstExprParam _Right> > + friend constexpr auto > + operator->*(_Left, _Right) noexcept > + -> constant_wrapper<_Left::value->*(_Right::value)> > + { return {}; } > + template<_ConstExprParam _Tp, _ConstExprParam... _Args> > + constexpr auto > + operator()(this _Tp, _Args...) noexcept > + requires > + requires(_Args...) { > constant_wrapper<_Tp::value(_Args::value...)>(); } > + { return constant_wrapper<_Tp::value(_Args::value...)>{}; } > + template<_ConstExprParam _Tp, _ConstExprParam... _Args> > + constexpr auto > + operator[](this _Tp, _Args...) noexcept > + -> constant_wrapper<(_Tp::value[_Args::value...])> > + { return {}; } > + > + // pseudo-mutators > + template<_ConstExprParam _Tp> > + constexpr auto > + operator++(this _Tp) noexcept > + requires requires(_Tp::value_type __x) { ++__x; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return ++__x; }()>{}; > + } > + template<_ConstExprParam _Tp> > + constexpr auto > + operator++(this _Tp, int) noexcept > + requires requires(_Tp::value_type __x) { __x++; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x++; }()>{}; > + } > + > + template<_ConstExprParam _Tp> > + constexpr auto > + operator--(this _Tp) noexcept > + requires requires(_Tp::value_type __x) { --__x; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return --__x; }()>{}; > + } > + template<_ConstExprParam _Tp> > + constexpr auto > + operator--(this _Tp, int) noexcept > + requires requires(_Tp::value_type __x) { __x--; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x--; }()>{}; > + } > + > + template<_ConstExprParam _Tp, _ConstExprParam _Right> > + constexpr auto > + operator+=(this _Tp, _Right) noexcept > + requires requires(_Tp::value_type __x) { __x += _Right::value; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x += _Right::value; }()>{}; > + } > + template<_ConstExprParam _Tp, _ConstExprParam _Right> > + constexpr auto > + operator-=(this _Tp, _Right) noexcept > + requires requires(_Tp::value_type __x) { __x -= _Right::value; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x -= _Right::value; }()>{}; > + } > + template<_ConstExprParam _Tp, _ConstExprParam _Right> > + constexpr auto > + operator*=(this _Tp, _Right) noexcept > + requires requires(_Tp::value_type __x) { __x *= _Right::value; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x *= _Right::value; }()>{}; > + } > + template<_ConstExprParam _Tp, _ConstExprParam _Right> > + constexpr auto > + operator/=(this _Tp, _Right) noexcept > + requires requires(_Tp::value_type __x) { __x /= _Right::value; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x /= _Right::value; }()>{}; > + } > + template<_ConstExprParam _Tp, _ConstExprParam _Right> > + constexpr auto > + operator%=(this _Tp, _Right) noexcept > + requires requires(_Tp::value_type __x) { __x %= _Right::value; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x %= _Right::value; }()>{}; > + } > + template<_ConstExprParam _Tp, _ConstExprParam _Right> > + constexpr auto > + operator&=(this _Tp, _Right) noexcept > + requires requires(_Tp::value_type __x) { __x &= _Right::value; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x &= _Right::value; }()>{}; > + } > + template<_ConstExprParam _Tp, _ConstExprParam _Right> > + constexpr auto > + operator|=(this _Tp, _Right) noexcept > + requires requires(_Tp::value_type __x) { __x |= _Right::value; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x |= _Right::value; }()>{}; > + } > + template<_ConstExprParam _Tp, _ConstExprParam _Right> > + constexpr auto > + operator^=(this _Tp, _Right) noexcept > + requires requires(_Tp::value_type __x) { __x ^= _Right::value; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x ^= _Right::value; }()>{}; > + } > + template<_ConstExprParam _Tp, _ConstExprParam _Right> > + constexpr auto > + operator<<=(this _Tp, _Right) noexcept > + requires requires(_Tp::value_type __x) { __x <<= _Right::value; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x <<= _Right::value; }()>{}; > + } > + template<_ConstExprParam _Tp, _ConstExprParam _Right> > + constexpr auto > + operator>>=(this _Tp, _Right) noexcept > + requires requires(_Tp::value_type __x) { __x >>= _Right::value; } > + { > + return constant_wrapper< > + [] { auto __x = _Tp::value; return __x >>= _Right::value; }()>{}; > + } > + }; > + > + template<_CwFixedValue _X, typename> > + struct constant_wrapper : _CwOperators { > + static constexpr const auto& value = _X._M_data; > + using type = constant_wrapper; > + using value_type = typename decltype(_X)::_S_type; > + > + template<_ConstExprParam _Right> > + constexpr auto > + operator=(_Right) const noexcept > + requires requires(value_type __x) { __x = _Right::value; } > + { > + return constant_wrapper< > + [] { auto __x = value; return __x = _Right::value; }()>{}; > + } > + > + constexpr > + operator decltype(auto)() const noexcept > + { return value; } > + }; > + > + template<_CwFixedValue _Tp> > + constexpr auto cw = constant_wrapper<_Tp>{}; > +#endif > + > /// @} group metaprogramming > > _GLIBCXX_END_NAMESPACE_VERSION > diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc > b/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc > new file mode 100644 > index 00000000000..7ee754fe4b1 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc > @@ -0,0 +1,42 @@ > +// { dg-do run { target c++26 } } > +#include <type_traits> > + > +#include <testsuite_hooks.h> > + > +namespace adl > +{ > + struct Addable > + { > + double x; > + > + friend constexpr Addable > + operator+(Addable lhs, Addable rhs) > + { return Addable{lhs.x + rhs.x}; } > + > + friend constexpr bool > + operator==(Addable lhs, Addable rhs) > + { return lhs.x == rhs.x; } > + }; > +} > + > +constexpr void > +test_addable() > +{ > + auto check = [](auto a, auto b) > + { > + if constexpr (a + b == adl::Addable{5.0}) > + return true; > + else > + return false; > + }; > + > + constexpr adl::Addable a{2.0}, b{3.0}; > + VERIFY(check(std::cw<a>, std::cw<b>)); > +} > + > +int > +main() > +{ > + test_addable(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc > b/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc > new file mode 100644 > index 00000000000..f46af929030 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc > @@ -0,0 +1,45 @@ > +// { dg-do run { target c++26 } } > +#include <type_traits> > + > +#include <testsuite_hooks.h> > + > +constexpr auto > +initial_phase(auto quantity_1, auto quantity_2) > +{ return quantity_1 + quantity_2; } > + > +constexpr auto > +middle_phase(auto tbd) > +{ return tbd; } > + > +constexpr bool > +final_phase(auto gathered, auto available) > +{ > + if constexpr (gathered == available) > + return true; > + else > + return false; > +} > + > +void > +impeccable_underground_planning() > +{ > + auto gathered_quantity = middle_phase(initial_phase(std::cw<42>, > std::cw<13>)); > + static_assert(gathered_quantity == 55); > + auto all_available = std::cw<55>; > + VERIFY(final_phase(gathered_quantity, all_available)); > +} > + > +// void > +// deeply_flawed_underground_planning() > +// { > +// constexpr auto gathered_quantity = middle_phase(initial_phase(42, > 13)); > +// constexpr auto all_available = 55; > +// final_phase(gathered_quantity, all_available); > +// } > + > +int > +main() > +{ > + impeccable_underground_planning(); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > new file mode 100644 > index 00000000000..4381ac43639 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc > @@ -0,0 +1,78 @@ > +// { dg-do run { target c++26 } } > +#include <type_traits> > +#include <utility> > + > +#include <testsuite_hooks.h> > + > +constexpr void > +test_c_arrays() > +{ > + constexpr double x[] = {1.1, 2.2, 3.3}; > + auto access = [](auto x, size_t i) > + { > + return x[i]; > + }; > + > + VERIFY(access(std::cw<x>, 0) == x[0]); > + VERIFY(access(std::cw<x>, 1) == x[1]); > + VERIFY(access(std::cw<x>, 2) == x[2]); > +} > + > +constexpr void > +test_ints() > +{ > + std::constant_wrapper<2> two; > + VERIFY(two + 3 == 5); > +} > + > +template<int OpId> > + struct Int > + { > + friend constexpr int > + operator+(Int) noexcept requires(OpId == 0) > + { return OpId; } > + > + friend constexpr int > + operator-(Int) noexcept requires(OpId == 1) > + { return OpId; } > + > + int value; > + }; > + > +template<int OpId> > + constexpr void > + test_unary_operator() > + { > + auto x = std::cw<Int<OpId>{-1}>; > + if constexpr (OpId == 0) > + VERIFY((+x).value == OpId); > + if constexpr (OpId == 1) > + VERIFY((-x).value == OpId); > + } > + > +constexpr void > +test_unary_operator_all() > +{ > + auto run_all = []<size_t... Idx>(std::integer_sequence<size_t, Idx...>) > + { > + (test_unary_operator<Idx>(), ...); > + }; > + run_all(std::make_index_sequence<2>()); > +} > + > +constexpr bool > +test_all() > +{ > + test_c_arrays(); > + test_ints(); > + test_unary_operator_all(); > + return true; > +} > + > +int > +main() > +{ > + test_all(); > + static_assert(test_all()); > + return 0; > +} > diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc > b/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc > new file mode 100644 > index 00000000000..4fee6159141 > --- /dev/null > +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc > @@ -0,0 +1,11 @@ > +// { dg-do preprocess { target c++26 } } > +// { dg-add-options no_pch } > + > +#include <type_traits> > + > +#ifndef __cpp_lib_constant_wrapper > +#error "Feature test macro __cpp_lib_constant_wrapper is missing for > <type_traits>" > +#if __cpp_lib_constant_wrapper < 202506L > +#error "Feature test macro __cpp_lib_constant_wrapper has the wrong value" > +#endif > +#endif > -- > 2.50.0 > >