1

I want to use my function assign below, to store a struct of unknown type at a specific memory address stored in a void pointer. This works fine for some structs (e.g. with Test1), but for other structs it does not work (e.g. with Test2). Unfortunately, I could not find any pattern for what sort of structs it will work, as it has worked for some POD and non-POD structs.

What criteria must be fulfilled for a struct in order to store it this way?

#include <vector>

template <typename T>
void assign(void* p, T obj){
    T* t = reinterpret_cast<T*>(p);
    *t = obj;
}

struct Test1{
    int i1;
    int i2;
};

struct Test2{
    std::vector<int> i;
};

int main()
{
    void* p = new char[100];
    Test1 test1;
    test1.i1 = 0;
    test1.i2 = 0;
    Test2 test2;
    test2.i = std::vector<int>();

    assign(p, test1);
    assign(p, test2);

    delete[] p;
    return 0;
}

Edit:

Why do I want to have this? I am currently implementing a game engine that uses an entity-component-system model (An entity is a game object, a component is kind of its attribute(s) and a system implements the logic to modify components.). There is a class that stores and manages entities (not surprisingly called EntityManager). Some programmer that uses my engine can define components (simply structs, only meant to contain data and no functionality) for an entity. The EntityManager therefore needs to store objects whose type is not known beforehand. A further restriction is that the components should be stored in an efficient way, i.e. behind each other in memory. This is the reason why a simple approach like having a base class Component and then having a member variable vector<vector<Component*>> is not appropriate.

My solution is that the EntityManager allocates enough space to store a certain user-defined maximal number of entities beforehand. A given component can then be stored with the assign function above.

Bluescreen
  • 154
  • 2
  • 9
  • 1
    `assign` doesn't work because the object was never properly constructed. You must first construct it, before assignment will work. Then you must destroy it before you're done. – Mooing Duck Oct 28 '17 at 23:27
  • 1
    Related: https://stackoverflow.com/questions/8918791/how-to-properly-free-the-memory-allocated-by-placement-new/8918942#8918942 – Mooing Duck Oct 28 '17 at 23:31
  • To clarify @MooingDuck's comment, *both* cases are undefined behavior. (In practice, you're more likely to get away with it if the type has a trivial default constructor and trivial destructor, but please don't count on even that.) – aschepler Oct 28 '17 at 23:50
  • Thank you guies! So I changed assign to `void assign(void* p, T obj){T* t = new (p) T; *t = obj; }` and it works! Does this version still result in undefined behavior? – Bluescreen Oct 29 '17 at 00:02
  • That is a valid way of creating an object. (Though you could do just `{ new (p) T(std::move(obj)); }` to copy/move directly into the new object, instead of default-constructing and then assigning.) And don't forget to `template destroy(void* p) { static_cast(p)->~T(); }`. – aschepler Oct 29 '17 at 00:38
  • 4
    What are you trying to do? Using a void* to start is almost never the right thing in C++. – Nir Friedman Oct 29 '17 at 04:13
  • @NirFriedman I have updated my question. – Bluescreen Oct 30 '17 at 10:39
  • 1
    So how are you planning to access your components? Wouldn't you need some kind of array of pointers that point to the beginning of each component? Furthermore, wouln't you have to store some kind of type designator for each component? – n. m. could be an AI Oct 30 '17 at 10:52
  • @n.m. yes, the `EntityManager` stores the size of the components, their alignment and a pointer to the beginning of each component "cluster". About your second part: what do you mean by storing type designators? I currently only allow storing created objects, but that could be improved :). I currently haven't found a better solution than this `void*` one. – Bluescreen Oct 30 '17 at 11:42
  • It is not enough to know the size of the stored object. One needs to know the type in order to do anything with it. How is type information stored? – n. m. could be an AI Oct 30 '17 at 12:09
  • @n.m. Ahh sry, my bad. The good thing is that the `EntityManager` just stores the components and does nothing with their contents. The modifier of the contents are the systems that query the `EntityManager` to obtain the components. The systems are written by the user and therefore have type information. – Bluescreen Oct 30 '17 at 13:32
  • @Bluescreen So, isn't your question just better structured as: how can I make sure that a `vector>` is stored in contiguous memory? Also, it should be clear, that if a `Component` decides to use some heap memory with e.g. `vector`, then there is nothing your EntityManager can do to ensure that all of the memory is contiguously allocated. – Nir Friedman Oct 30 '17 at 14:39
  • It looks like you are providing two separate services (1) memory for storage and (2) a collection of pointers to this storage. Memory for storage is provided by `operator new`. Do you want to provide custom storage? Define a custom `operator new` that allocates contiguous memory and a matching `operator delete`. – n. m. could be an AI Oct 30 '17 at 14:44
  • @NirFriedman `vector>` is not interesting in the slightest. Components themselves should be in contiguous memory, not pointers to them (`vector` does it automatically anyway, no need to do anything special). – n. m. could be an AI Oct 30 '17 at 14:47
  • @n.m. Uhm, it's interesting because it's the most natural interface. You can get the components themselves into contiguous memory by applying a custom allocator to the unique_ptrs, not the vector. – Nir Friedman Oct 30 '17 at 14:51
  • @NirFriedman Neither unique ptr not its make utility use allocators. The best you can do is using an allocator when you create objects you pass to your pointers. However this moves the problem out of the corner where you placed it and back its original position. – skypjack Nov 04 '17 at 20:21
  • @skypjack I never implied otherwise, I simply said you can use allocators in conjunction with unique_ptr. You can do this by writing the unique analogue of `allocate_shared', which is not a big deal (unless you go poking into more advanced characteristics of allocators. If you want your `vector>`, to store objects contiguously, allocate_unique and asuitable allocator will do it. – Nir Friedman Nov 05 '17 at 00:48

0 Answers0