0

I'm writing something to allocate objects/memory, and I ran into a problem because C++ seems to forbid moving types that are not trivially_copyable. I assume that sometimes a memory allocator needs to move memory around, and if it does so cannot return direct pointers or direct handles, but indirect pointers or handles to indices that can then be changed when the memory is moved. I'm guessing this is what's done when memory is defragmented/compacted, but how can I move memory when C++ forbids me from memcpying certain objects? For example:

char* buffer = new char[128];

unsigned long long createMyType() {
    
    // Lets say I find space for MyType at offset 28
    new (buffer + 28) MyType;

    return 28;
}

void destroyMyType(unsigned long long idx)
{
    ((MyType*)(buffer + idx))->~MyType();
}

int main()
{

    
}

Now for whatever reason I want to move a certain part of that buffer, to close a gap (or something). I'm pretty sure I can't memcpy the area that has a MyType object, because it's not trivially_copyable and possibly because it's not an implicit lifetime type. How is it that memory is moved around if there is no knowledge of the objects that exist in that memory? I've read that one reason you can't memcpy certain types is because non_trivially_copyable and non standard layout types are not guaranteed to be contiguous in memory. But I don't really understand how it could not be contiguous. If it's possible it's not contiguous how can something like the operating system shift memory around? Can I not fill a buffer with objects and move the memory and expect it to have the same data/objects?

Zebrafish
  • 11,682
  • 3
  • 43
  • 119
  • The C++ memory allocator can't move live memory around, only free memory. Your concern is unfounded. – user207421 Apr 08 '21 at 04:24
  • @user207421 Can you explain what you mean by live memory? You can request memory, fill it with data and move it around. What do you mean by 'live' memory? – Zebrafish Apr 08 '21 at 04:27
  • Do you want to memcpy or move? To move something in C++ means something very specific that I don't think you're actually asking about. – sweenish Apr 08 '21 at 04:38
  • @sweenish I want to move a buffer from one place to another, memcpy is technically a copy, but I'm moving the buffer from one area to another because I'm not interested in the old data. The buffer can have anything in it. But I can't supposedly do that because certain types are not trivially_copyable and memcpy can't be used on those types. I don't understand why it's forbidden to do this. Ordinarily the reasons given for why memcpy is a bad idea for non_trivially_copyable_types is that if a move or assignment operator is defined memcpy doesn't copy/move the object correctly because it... – Zebrafish Apr 08 '21 at 04:53
  • doesn't call these functions. However with moving a buffer from one area to another I don't see the difference this makes. The other argument is that memcpy also can't be used because you can't assume the object occupied contiguous memory, however I don't know how this can be the case. Other systems move memory from one place to another right? And this is just done with a dumb byte for byte copy. – Zebrafish Apr 08 '21 at 04:54
  • You don't demonstrate that `MyType` can't be `memcpy`'d. If your type allocates data, it doesn't occupy contiguous memory. I don't have a ton of experience with this aspect of C++, my contribution was just fleshing out what you want, and it's to `memcpy` and not [move](https://stackoverflow.com/a/3109981/6119582). – sweenish Apr 08 '21 at 05:03
  • By live memory I mean memory that has been allocated and returned to the application by `malloc()/calloc()/realloc()` or `new` and not yet deleted or `free()`d. It cannot be moved. – user207421 Apr 08 '21 at 05:54
  • @user207421 What I mean is you get a pointer back from malloc, and that maps to an area of memory. If I construct an object in that memory that has move and copy constructors, vtable pointers and all sorts of things, what's stopping that memory from being moved and mapped to another address by another system, say the OS? Unless the compiler does something to recognize that an area of memory contains an unmemcopyable object and prevents that memory from being moved/copied. How is it that an unmemcopyable object can be moved, byte for byte to another address and still work ? – Zebrafish Apr 08 '21 at 06:35
  • The OS might swap a memory page to a different **physical address** but it doesn't change the **virtual address**. In your user process objects don't appear to mysteriously move around in memory and change their address. When you talk about moves and copies in your C++ code that's something else and it has to do with if a type can be bitwise copied or if it's non-trivial. A non-trivial type has operators defined to copy/move/assign correctly and maintain the object's invariants. – Blastfurnace Apr 08 '21 at 07:21
  • @Blastfurnace Right, but how can any object not be bitwise copied when for example the OS could do it and the object will still work? So struct Foo { /*move constructor defined*/ };, and OS can bitwise copy that and still have it work when I access it from my code, but I can't bitwise copy it in my code and have a guarantee that it'll work? – Zebrafish Apr 08 '21 at 07:31
  • I'm not sure I understand your question. If the OS is swapping memory around the physical address might change but the **virtual address** doesn't. The object(s) still have the same address as far as your code knows. That's how virtual addresses work and it's completely transparent to you. This has nothing to do with objects in your program and whether they're trivially-copyable or not. – Blastfurnace Apr 08 '21 at 07:55
  • Are you thinking about virtual memory as described in [this wikipedia article](https://en.wikipedia.org/wiki/Virtual_memory)? – Blastfurnace Apr 08 '21 at 07:58
  • 1
    A simple example of a non-trivial type is one that holds a pointer or reference to one of its own member variables inside the object. The type should have operators defined to do the right thing when the object is copied/moved/assigned. It would be a disaster to memcpy such an an object, the internal pointer would point to the wrong location. You shouldn't bitwise copy it and correct C++ code and the standard library won't bitwise copy it because it's not a trivial type. The answers in [this other question](https://stackoverflow.com/q/4178175) might help. – Blastfurnace Apr 08 '21 at 08:13
  • What do you mean with _"move"_? Do you mean to copy it to a new memory location and delete it at the old location? –  Apr 08 '21 at 08:22
  • @jabaa Let's just say you get a pointer to memory from malloc, and the memory looks like ! ! ! ! - - - - ! ! ! ! , those hyphens/dashes are a gap in that memory, I want to move/copy the second series of exclamation points four bytes back to close the gap. I say move/copy, I'm basically talking about memcpy. But I can't do that because that data may be a non-trivially-copyable type, and it's supposedly illegal to do that. You'd have to call the move or copy constructor. But you may not know what type is in that buffer. You may only know where things are located, ie., gaps. – Zebrafish Apr 08 '21 at 08:42
  • If you don't know the type of that memory, how could you know it doesn't contain internal pointers that get broken by this copy, e.g. an object of type `struct A {int a; int &b = a;};`? The new `A::b` would still reference the old `A::a` but it doesn't exist anymore. –  Apr 08 '21 at 08:45
  • @jabaa Yeah that's a good point. I suppose for that class you'd provide a custom move/copy constructors, whereas memcpy fails. Is it common for an object to reference itself? Also, how do systems do this? Like the OS moving memory around. At that stage it's just bytes, isn't it, there's no way it knows an object references itself, right? – Zebrafish Apr 08 '21 at 13:12
  • You should read Blastfurnace's links. The system moves memory pages and the virtual memory addresses keep unchanged. E.g. you have a `int i = 4` at memory position `0x12345678`. After the system moved the page the virtual address is still `0x12345678` and all pointers and references keep valid. Your program can't do that. After you move a memory block all pointers and references to this block are invalidated. There are also constructs like this: https://stackoverflow.com/a/36734227/15388024 where related meta information is before the pointer. You would lose this information. –  Apr 08 '21 at 13:14

0 Answers0