Hello,

Thanks for the review!

On 20/02/2025 17:22, Patrick Palka wrote:
On Sun, 16 Feb 2025, Giuseppe D'Angelo wrote:

Hello,

the attached patch implements the C++26 papers that add `constexpr` to the
specialized memory algorithms (the uninitialized_* family). Tested on x86-64
Linux.

Thank you,
--
Giuseppe D'Angelo


Subject: [PATCH] libstdc++: implement constexpr memory algorithms

This commit adds support for C++26's constexpr specialized memory
algorithms, introduced by P2283R2, P3508R0, P3369R0.

The uninitialized_default, value, copy, move and fill algorithms are
affected, in all of their variants (iterator-based, range-based and _n
versions.)

The changes are mostly mechanical -- add `constexpr` to a number of
signatures. I've introduced a helper macro to conditionally expand to
`constexpr` only in C++26 and above modes. The internal helper guard
class for range algorithms instead can be marked unconditionally.

uninitialized_fill is the only algorithm where I had to add a branch to
a constexpr-friendly version (already existing).

Seems the patch also adds code to uninitialized_copy and
uninitialized_fill_n?

Whops, sorry, didn't spot them when I reviewed the diff. You're right.

    template<typename _T1>
+#if __cpp_constexpr >= 202406L // >= C++26
+    _GLIBCXX26_CONSTEXPR
+#endif

Maybe we can get away with unconditionally declaring this
_GLIBCXX26_CONSTEXPR?  If the compiler doesn't support constexpr
placement new then the 'constexpr' would be silently dropped at
instantiation time.  This would be in line with C++23 P2448R2 which
made it no longer IFNDR to declare a constexpr function template
for which no specialization is actually constexpr.

It's fundamentally a judgement / compatibility call.

The patch adding the _GLIBCXX26_CONSTEXPR macro is pending here
https://gcc.gnu.org/pipermail/libstdc++/2025-January/060265.html
and checks for __cplusplus >= 202400L.

According to cppreference only Clang >= 19 fully implements P2448R2, but Clang 17 and 18 already advertise C++26 compatibility under __cplusplus set to 202400.

On the other hand the ad-hoc _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS has a more stringent version check (uses the feature-testing macro, which in turn checks for the right value of __cpp_constexpr).

Is switching to _GLIBCXX26_CONSTEXPR going to create compatibility problems? If not, I'll do the change.


+
  namespace std _GLIBCXX_VISIBILITY(default)
  {
  _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -226,6 +232,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     *  Like std::copy, but does not require an initialized output range.
    */
    template<typename _InputIterator, typename _ForwardIterator>
+    _GLIBCXX26_CONSTEXPR_RAW_MEMORY_ALGORITHMS
      inline _ForwardIterator
      uninitialized_copy(_InputIterator __first, _InputIterator __last,
                       _ForwardIterator __result)
@@ -256,6 +263,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        using _Src = decltype(std::__niter_base(__first));
        using _ValT = typename iterator_traits<_ForwardIterator>::value_type;
+
+      if (__is_constant_evaluated())

We typically call __is_constant_evaluated fully qualified (though I
don't remember why since it's not eligible for ADL?)

Ack, will fix.


+       return std::__do_uninit_copy(__first, __last, __result);

I guess we could instead guard the memcpy code path preceding the
existing call to __do_uninit_copy with !std::__is_constant_evaluated(),
rather than adding this new call.  But that doesn't seem significantly
cleaner and I'm personally OK with your approach :)

Same for uninitialized_fill and uninitialized_fill_n

I'm wagering this is the simplest implementation strategy for most constexpr-ified algorithms -- first thing, check if we're called during constant evaluation, if so dispatch to a constexpr-friendly implementation.


Thank you,

--
Giuseppe D'Angelo

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to