🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

C++17 Allocators

Started by
3 comments, last by Dimples87 3 years, 1 month ago

Hi,

I'm getting back into C++ programming (and game development). I learnt C++ pre C++11, so I'm trying to pick up the new stuff.

I've been looking into memory management of game objects, with a focus of laying game objects out in memory efficiently. I've come across the std::pmr::monotonic_buffer_resource, where you supply it a region of memory for it to use:

union Vec2 {
    struct {
	float x;
	float y;
    };

    struct {
        float w;
        float h;
    };

    float elements[2];
};

struct GameObject {
    Vec2 position;
    Vec2 size;

    GameObject() : position{ 0,0 }, size{ 0,0 } {};
    GameObject(Vec2 _position, Vec2 _size) : position(_position), size(_size) {};
};

int main() {
    GameObject buffer[256];

    const auto GameObjectPrinter = [](const GameObject& gameObject, std::string_view title) {
        std::cout << title << '\n';
        std::cout << "Position: x = " << gameObject.position.x << ", y = " << gameObject.position.y << '\n';
        std::cout << "Size: w = " << gameObject.size.w << ", h = " << gameObject.size.h << '\n';
        std::cout << '\n';
    };

    std::pmr::monotonic_buffer_resource pool{ std::data(buffer), std::size(buffer) };
    std::pmr::vector<GameObject> vec{ &pool };

    vec.reserve(5);

    vec.emplace_back(GameObject({ 0, 0 }, { 32, 32 }));
    vec.push_back({ {1, 1}, {16, 16} });

    for (auto& item : vec) {
        GameObjectPrinter(item, "Game Objects:");
    }
}

On the surface this seems to work fine. However, I was curious and wanted to see what was happening under the hood, and how it was using buffer. Debugging it in Visual Studio shows that the vector puts it's first element in buffer[1] rather than buffer[0], does anyone know why this is (it's more than likely something I have done wrong and I can't see it)?

Advertisement
struct xy 
{
	float x;
	float y;
};
    
struct wh
{
	float w;
	float h;
};
    
struct el 
{ 
   float elements[2];
};
    
struct Vec2 
{
    union 
    {
        xy a;
        wh b;
        el c;
    };
};

struct GameObject {
    Vec2 position;
    Vec2 size;

    GameObject() : position{ 0,0 }, size{ 0,0 } {};
    GameObject(Vec2 _position, Vec2 _size) : position(_position), size(_size) {};
};

int main() {
    GameObject buffer[256];

    const auto GameObjectPrinter = [](const GameObject& gameObject, std::string_view title) {
        std::cout << title << '\n';
        std::cout << "Position: x = " << gameObject.position.a.x << ", y = " << gameObject.position.a.y << '\n';
        std::cout << "Size: w = " << gameObject.size.b.w << ", h = " << gameObject.size.b.h << '\n';
        std::cout << '\n';
    };

    std::pmr::monotonic_buffer_resource pool{ std::data(buffer), std::size(buffer) };
    std::pmr::vector<GameObject> vec{ &pool };

    vec.reserve(5);

    vec.emplace_back(GameObject({ 0, 0 }, { 32, 32 }));
    vec.push_back({ {1, 1}, {16, 16} });

    for (auto& item : vec) {
        GameObjectPrinter(item, "Game Objects:");
    }
}

?

That's not actually what's happening. Your code creates an object with position (0, 0) and size (32, 32). If you look at the debugger, the object at position 1 has position (32, 32) and size (0, 0). So you have the first object constructed to straddle the boundary between positions 0 and 1.

As for why, there are basically two options: either it's overhead from std::pmr::monotonic_buffer_resource (which needs to store information about the memory blocks it has allocated somewhere) or it's overhead from std::vector (which needs to store its length and capacity somewhere). In either case, it's working as designed.

a light breeze said:

That's not actually what's happening. Your code creates an object with position (0, 0) and size (32, 32). If you look at the debugger, the object at position 1 has position (32, 32) and size (0, 0). So you have the first object constructed to straddle the boundary between positions 0 and 1.

As for why, there are basically two options: either it's overhead from std::pmr::monotonic_buffer_resource (which needs to store information about the memory blocks it has allocated somewhere) or it's overhead from std::vector (which needs to store its length and capacity somewhere). In either case, it's working as designed.

Yes! Thank you for pointing that out. I saw numbers that resembled what I was looking for and assumed correct output. Upon closer inspection, the vector has the correct information, but the pool wasn't being used as I expected. I ran through a simpler string exercise and watched the memory window and, it's fitting strings in the buffer provided, whereas I was expecting it to do it much like an array.

Thanks for the help in understanding what was going off, I thought I could use it for pooling game objects, combining the standard library and an area of memory, but I think I will stick to a simpler custom pool object

This topic is closed to new replies.

Advertisement