Skip to content

Instantly share code, notes, and snippets.

@GavinRay97
Created December 9, 2022 19:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GavinRay97/b69cb6ebab6a0e13d2cfe74a6ed7cdc6 to your computer and use it in GitHub Desktop.
Save GavinRay97/b69cb6ebab6a0e13d2cfe74a6ed7cdc6 to your computer and use it in GitHub Desktop.
P2590R2: "Explicit lifetime management" (start_lifetime_as) implementation
// How can we make this program well-defined without sacrificing efficiency? If the destination type
// is a trivially-copyable implicit-lifetime type, this can be accomplished by copying the storage
// elsewhere, using placement new of an array of byte-like type, and copying the storage back to its
// original location, then using std::launder to acquire a pointer to the newly-created object, and
// finally relying on the compiler to optimise away all the copying. However, this would be very
// verbose and hard to get right. For expressivity and optimisability, a combined operation to
// create an object of implicit-lifetime type in-place while preserving the object representation
// may be useful. This is exactly what std::start_lifetime_as is designed to do
template<class T>
T*
start_lifetime_as(void* p) noexcept
{
// Copy the storage to a temporary location, using placement new of an array of byte-like type
std::byte tmp[sizeof(T)];
new (tmp) T(*reinterpret_cast<T*>(p));
// Copy the storage back to its original location
std::copy(tmp, tmp + sizeof(T), reinterpret_cast<std::byte*>(p));
// then using std::launder to acquire a pointer to the newly-created object
// and finally relying on the compiler to optimise away all the copying
return std::launder(reinterpret_cast<T*>(p));
}
// The same operation can be performed on an array of implicit-lifetime type
template<class T>
T*
start_lifetime_as_array(void* p, size_t n) noexcept
{
// Copy the storage to a temporary location, using placement new of an array of byte-like type
std::byte tmp[sizeof(T) * n];
for (size_t i = 0; i < n; i++)
{
new (tmp + i * sizeof(T)) T(reinterpret_cast<T*>(p)[i]);
}
// Copy the storage back to its original location
std::copy(tmp, tmp + sizeof(T) * n, reinterpret_cast<std::byte*>(p));
// then using std::launder to acquire a pointer to the newly-created object
// and finally relying on the compiler to optimise away all the copying
return std::launder(reinterpret_cast<T*>(p));
}
template<class T>
const T*
start_lifetime_as(const void* p) noexcept
{
return start_lifetime_as<T>(const_cast<void*>(p));
}
template<class T>
volatile T*
start_lifetime_as(volatile void* p) noexcept
{
return start_lifetime_as<T>(const_cast<void*>(p));
}
template<class T>
const volatile T*
start_lifetime_as(const volatile void* p) noexcept
{
return start_lifetime_as<T>(const_cast<void*>(p));
}
template<class T>
const T*
start_lifetime_as_array(const void* p, size_t n) noexcept
{
return start_lifetime_as_array<T>(const_cast<void*>(p), n);
}
template<class T>
volatile T*
start_lifetime_as_array(volatile void* p, size_t n) noexcept
{
return start_lifetime_as_array<T>(const_cast<void*>(p), n);
}
template<class T>
const volatile T*
start_lifetime_as_array(const volatile void* p, size_t n) noexcept
{
return start_lifetime_as_array<T>(const_cast<void*>(p), n);
}
// Test of the above functions
int
main()
{
// Create an implicit-lifetime object
std::array<std::byte, 100> record;
std::fill(record.begin(), record.end(), std::byte(0x42));
struct Foo
{
int x;
int y;
};
// Create a new object of implicit-lifetime type in-place while preserving the object
// representation
Foo* foo = start_lifetime_as<Foo>(record.data());
printf("%d %d", foo->x, foo->y);
Foo* foos = start_lifetime_as_array<Foo>(record.data(), record.size() / sizeof(Foo));
for (size_t i = 0; i < record.size() / sizeof(Foo); i++)
{
printf("%d %d", foos[i].x, foos[i].y);
}
// Test const version
const std::array<std::byte, 100> record2 = record;
const Foo* foo2 = start_lifetime_as<Foo>(record2.data());
printf("%d %d", foo2->x, foo2->y);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment