Created
          December 9, 2022 19:00 
        
      - 
      
- 
        Save GavinRay97/b69cb6ebab6a0e13d2cfe74a6ed7cdc6 to your computer and use it in GitHub Desktop. 
Revisions
- 
        GavinRay97 created this gist Dec 9, 2022 .There are no files selected for viewingThis file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,118 @@ // 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); }