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? 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>. 3. Is the type of test that will "check" each operator meaningful? 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