Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do C++ Objects (Standard Definition) persist in memory map files?

Question inspired by Dealing with large data binary files

Link to Object

Program (1) creates a memory-mapped file and writes some Objects (C++ Standard definition) to it, closes the file and exits.

Program (2) maps the above file into memory and tries to access the Objects via reinterpret_cast.

Is this legal by the Standard as the object representations have not changed and the Objects still exist in the file ?

If this was attempted between 2 processes, not with a file, but using shared process memory is this legal ?

Note - this question is not about storing or sharing local virtual addresses as this is obviously a bad thing.

like image 309
Richard Critten Avatar asked Jan 24 '26 07:01

Richard Critten


1 Answers

No, objects do not persist this way.

C++ objects are defined primarily by their lifetime, which is scoped to the program.

So if you want to recycle an object from raw storage, there has to be a brand new object in program (2) with its own lifetime. reinterpret_cast'ing memory does not create a new object, so that doesn't work.

Now, you might think that inplace-newing an object with a trivial constructor at that memory location could do the trick:

struct MyObj {
  int x;
  int y;
  float z;
};

void foo(char* raw_data) {
  // The content of raw_data must be treated as being ignored.
  MyObj* obj = new (raw_data) MyObj();
}

But you can't do that either. The compiler is allowed to (and demonstrably does sometimes) assume that such a construction mangles up the memory. See C++ placement new after memset for more details, as well as a demonstration.

If you want to initialize an object from a given storage representation, you must use memcpy() or an equivalent:

void foo(char* raw_data) {
  MyObj obj;

  static_assert(std::is_standard_layout_v<MyObj>);
  std::memcpy(&obj, raw_data, sizeof(MyObj));
}

Addendum: It is possible to do the equivalent of the desired reinterpret_cast<> by restomping the memory with its original content after creating the object (inspired by the IOC proposal).

#include <type_traits>
#include <cstring>
#include <memory>

template<typename T> 
T* start_lifetime_as(void *p) 
  requires std::is_trivially_copyable_v<T> {
  
  constexpr std::size_t size = sizeof(T);
  constexpr std::size_t align = alignof(T);

  auto aligned_p = std::assume_aligned<align>(p);

  std::aligned_storage_t<size, align> tmp;
  std::memcpy(&tmp, aligned_p, size);

  T* t_ptr = new (aligned_p) T{};
  std::memcpy(t_ptr , &tmp, size);

  return std::launder<T>(t_ptr);
}


void foo(char* raw_data) {
  MyObj* obj = start_lifetime_as<MyObj>(raw_data);
}

This should be well-defined in C++11 and up as long as that memory location only contains raw data and no prior object. Also, from cursory testing, it seems like compilers do a good job at optimizing that away.

see on godbolt


Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!