On Fri, Aug 29, 2025 at 10:56:45AM +0200, Tomasz Kaminski wrote: > Yes, that is even better. C++ made me biased towards not using > preprocessing, > so I miss simple solutions.
Anyway, went with your requires case + added tests for starting lifetime on static const variable. 2025-08-29 Jakub Jelinek <[email protected]> * include/bits/version.def: Implement C++23 P2590R2 - Explicit lifetime management. (start_lifetime_as): New. * include/bits/version.h: Regenerate. * include/std/memory (std::start_lifetime_as, std::start_lifetime_as_array): New function templates. * src/c++23/std.cc.in (std::start_lifetime_as, std::start_lifetime_as_array): Export. * testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc: New test. --- libstdc++-v3/include/bits/version.def.jj 2025-08-28 17:06:44.190031458 +0200 +++ libstdc++-v3/include/bits/version.def 2025-08-28 18:44:00.947678928 +0200 @@ -2100,6 +2100,14 @@ ftms = { }; }; +ftms = { + name = start_lifetime_as; + values = { + v = 202207; + cxxmin = 23; + }; +}; + // Standard test specifications. stds[97] = ">= 199711L"; stds[03] = ">= 199711L"; --- libstdc++-v3/include/bits/version.h.jj 2025-08-28 17:06:49.160883915 +0200 +++ libstdc++-v3/include/bits/version.h 2025-08-28 18:44:08.431550790 +0200 @@ -2353,4 +2353,14 @@ #endif /* !defined(__cpp_lib_is_implicit_lifetime) && defined(__glibcxx_want_is_implicit_lifetime) */ #undef __glibcxx_want_is_implicit_lifetime +#if !defined(__cpp_lib_start_lifetime_as) +# if (__cplusplus >= 202100L) +# define __glibcxx_start_lifetime_as 202207L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_start_lifetime_as) +# define __cpp_lib_start_lifetime_as 202207L +# endif +# endif +#endif /* !defined(__cpp_lib_start_lifetime_as) && defined(__glibcxx_want_start_lifetime_as) */ +#undef __glibcxx_want_start_lifetime_as + #undef __glibcxx_want_all --- libstdc++-v3/include/std/memory.jj 2025-08-23 15:00:05.208775420 +0200 +++ libstdc++-v3/include/std/memory 2025-08-29 09:21:59.402103595 +0200 @@ -120,6 +120,7 @@ #define __glibcxx_want_shared_ptr_arrays #define __glibcxx_want_shared_ptr_weak_type #define __glibcxx_want_smart_ptr_for_overwrite +#define __glibcxx_want_start_lifetime_as #define __glibcxx_want_to_address #define __glibcxx_want_transparent_operators #define __glibcxx_want_smart_ptr_owner_equality @@ -172,6 +173,134 @@ _GLIBCXX_END_NAMESPACE_VERSION } // namespace #endif // C++11 to C++20 +#if __cpp_lib_start_lifetime_as >= 202207L // C++ >= 23 +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + template<typename _Tp> + [[__gnu__::__always_inline__]] + inline _Tp* + start_lifetime_as(void* __p) noexcept + { +#if __cpp_lib_is_implicit_lifetime >= 202302L + static_assert(is_implicit_lifetime_v<_Tp>); +#endif + auto __q = reinterpret_cast<_Tp*>(__p); + __asm__ __volatile__("" : "=g" (__q), "=m" (*__q) + : "0" (__q), "m" (*__q)); + return __q; + } + + template<typename _Tp> + [[__gnu__::__always_inline__]] + inline const _Tp* + start_lifetime_as(const void* __p) noexcept + { +#if __cpp_lib_is_implicit_lifetime >= 202302L + static_assert(is_implicit_lifetime_v<_Tp>); +#endif + auto __q = reinterpret_cast<const _Tp*>(__p); + auto __r = reinterpret_cast<_Tp*>(const_cast<void*>(__p)); + __asm__ __volatile__("" : "=g" (__q), "=m" (*__r) + : "0" (__q), "m" (*__q)); + return __q; + } + + template<typename _Tp> + [[__gnu__::__always_inline__]] + inline volatile _Tp* + start_lifetime_as(volatile void* __p) noexcept + { +#if __cpp_lib_is_implicit_lifetime >= 202302L + static_assert(is_implicit_lifetime_v<_Tp>); +#endif + auto __q = reinterpret_cast<volatile _Tp*>(__p); + auto __r = reinterpret_cast<_Tp*>(const_cast<void*>(__p)); + __asm__ __volatile__("" : "=g" (__q), "=m" (*__r) + : "0" (__q), "m" (*__q)); + return __q; + } + + template<typename _Tp> + [[__gnu__::__always_inline__]] + inline const volatile _Tp* + start_lifetime_as(const volatile void* __p) noexcept + { +#if __cpp_lib_is_implicit_lifetime >= 202302L + static_assert(is_implicit_lifetime_v<_Tp>); +#endif + auto __q = reinterpret_cast<const volatile _Tp*>(__p); + auto __r = reinterpret_cast<_Tp*>(const_cast<void*>(__p)); + __asm__ __volatile__("" : "=g" (__q), "=m" (*__r) + : "0" (__q), "m" (*__q)); + return __q; + } + + template<typename _Tp> + [[__gnu__::__always_inline__]] + inline _Tp* + start_lifetime_as_array(void* __p, size_t __n) noexcept + { + auto __q = reinterpret_cast<_Tp*>(__p); + if (!__n) + return __q; + auto __r = (__extension__ reinterpret_cast<_Tp(*)[__n]>(__p)); + __asm__ __volatile__("" : "=g" (__q), "=m" (*__r) + : "0" (__q), "m" (*__r)); + return __q; + } + + template<typename _Tp> + [[__gnu__::__always_inline__]] + inline const _Tp* + start_lifetime_as_array(const void* __p, size_t __n) noexcept + { + auto __q = reinterpret_cast<const _Tp*>(__p); + if (!__n) + return __q; + auto __r = (__extension__ reinterpret_cast<const _Tp(*)[__n]>(__p)); + auto __s = (__extension__ + reinterpret_cast<_Tp(*)[__n]>(const_cast<void*>(__p))); + __asm__ __volatile__("" : "=g" (__q), "=m" (*__s) + : "0" (__q), "m" (*__r)); + return __q; + } + + template<typename _Tp> + [[__gnu__::__always_inline__]] + inline volatile _Tp* + start_lifetime_as_array(volatile void* __p, size_t __n) noexcept + { + auto __q = reinterpret_cast<volatile _Tp*>(__p); + if (!__n) + return __q; + auto __r = (__extension__ reinterpret_cast<volatile _Tp(*)[__n]>(__p)); + auto __s = (__extension__ + reinterpret_cast<_Tp(*)[__n]>(const_cast<void*>(__p))); + __asm__ __volatile__("" : "=g" (__q), "=m" (*__s) + : "0" (__q), "m" (*__r)); + return __q; + } + + template<typename _Tp> + [[__gnu__::__always_inline__]] + inline const volatile _Tp* + start_lifetime_as_array(const volatile void* __p, size_t __n) noexcept + { + auto __q = reinterpret_cast<const volatile _Tp*>(__p); + if (!__n) + return __q; + auto __r = (__extension__ reinterpret_cast<const volatile _Tp(*)[__n]>(__p)); + auto __s = (__extension__ + reinterpret_cast<_Tp(*)[__n]>(const_cast<void*>(__p))); + __asm__ __volatile__("" : "=g" (__q), "=m" (*__s) + : "0" (__q), "m" (*__r)); + return __q; + } +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif + #ifdef __cpp_lib_parallel_algorithm // C++ >= 17 && HOSTED // Parallel STL algorithms # if _PSTL_EXECUTION_POLICIES_DEFINED --- libstdc++-v3/src/c++23/std.cc.in.jj 2025-08-28 17:10:43.784942152 +0200 +++ libstdc++-v3/src/c++23/std.cc.in 2025-08-29 09:55:46.164636222 +0200 @@ -2004,6 +2004,10 @@ export namespace std using std::owner_equal; using std::owner_hash; #endif +#if __cpp_lib_start_lifetime_as + using std::start_lifetime_as; + using std::start_lifetime_as_array; +#endif } // 20.4 <memory_resource> --- libstdc++-v3/testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc.jj 2025-08-28 19:29:12.865001523 +0200 +++ libstdc++-v3/testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc 2025-08-29 10:53:59.160068890 +0200 @@ -0,0 +1,94 @@ +// { dg-do run { target c++23 } } + +#include <bit> +#include <memory> + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +struct S { int a; int b; }; +struct T { long long c; }; + +template<typename S, typename T> +void +test01() +{ +} + +template<typename S, typename T> +requires (sizeof(S) == sizeof(T)) +void +test01() +{ + union U { unsigned char a[sizeof(S)]; S b; T c; } u; + u.a[0] = 1; + T v = std::bit_cast<T> (S{1, 2}); + union V { unsigned char a[3 * sizeof(S)]; S b[3]; T c[3]; } w; + T x = std::bit_cast<T> (S{3, 4}); + T y = std::bit_cast<T> (S{5, 6}); + S* d = std::start_lifetime_as<S>(reinterpret_cast<void*>(&u.a)); + d->a = 1; + d->b = 2; + T* e = std::start_lifetime_as<T>(reinterpret_cast<void*>(d)); + VERIFY( e->c == v.c ); + const T* f = std::start_lifetime_as<T>(reinterpret_cast<const void*>(d)); + VERIFY( f->c == v.c ); + volatile T* g + = std::start_lifetime_as<T>(reinterpret_cast<volatile void*>(d)); + VERIFY( g->c == v.c ); + const volatile T* h + = std::start_lifetime_as<T>(reinterpret_cast<const volatile void*>(d)); + VERIFY( h->c == v.c ); + S* i = std::start_lifetime_as_array<S>(reinterpret_cast<void*>(&w.a), 3); + i[0].a = 1; + i[0].b = 2; + i[1].a = 3; + i[1].b = 4; + i[2].a = 5; + i[2].b = 6; + T* j = std::start_lifetime_as_array<T>(reinterpret_cast<void*>(i), 3); + VERIFY( j[0].c == v.c && j[1].c == x.c && j[2].c == y.c ); + const T* k + = std::start_lifetime_as_array<T>(reinterpret_cast<const void*>(i), 3); + VERIFY( k[0].c == v.c && k[1].c == x.c && k[2].c == y.c ); + volatile T* l + = std::start_lifetime_as_array<T>(reinterpret_cast<volatile void*>(i), 3); + VERIFY( l[0].c == v.c && l[1].c == x.c && l[2].c == y.c ); + const volatile T* m + = std::start_lifetime_as_array<T>(reinterpret_cast<const volatile void*>(i), + 3); + VERIFY( m[0].c == v.c && m[1].c == x.c && m[2].c == y.c ); + T* n = std::start_lifetime_as_array<T>(static_cast<void*>(nullptr), 0); + VERIFY( n == nullptr ); + const T* o + = std::start_lifetime_as_array<T>(static_cast<const void*>(nullptr), 0); + VERIFY( o == nullptr ); + volatile T* p + = std::start_lifetime_as_array<T>(static_cast<volatile void*>(nullptr), 0); + VERIFY( p == nullptr ); + const volatile T* q + = std::start_lifetime_as_array<T>(static_cast<const volatile void*>(nullptr), + 0); + VERIFY( q == nullptr ); + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<void*>(&w.a), 0) + == &w.c[0] ); + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<const void*>(&w.a), 0) + == static_cast<const T*>(&w.c[0]) ); + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<volatile void*>(&w.a), + 0) + == static_cast<volatile T*>(&w.c[0]) ); + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<const volatile void*>(&w.a), + 0) + == static_cast<const volatile T*>(&w.c[0]) ); + static const S r[] = { { 5, 6 }, { 3, 4 } }; + const T* s = std::start_lifetime_as<T>(&r[1]); + VERIFY( s->c == x.c ); + const T* t = std::start_lifetime_as_array<T>(&r[0], 2); + VERIFY( t[0].c == y.c && t[1].c == x.c ); +} + +int +main() +{ + test01<S, T>(); +} Jakub
