Jump to content
  • Advertisement
Tim Leijten

C++ Game Engine API, smart pointers or not?

Recommended Posts

12 minutes ago, SyncViews said:

I think the default allocators are a lot better than they were, you probably can't write a general purpose allocator that is outright better now.

I actually did beat an IBM workstation allocator by a large margin back in the '90s (Although I think it had some crazy bug in it)  However, I'm going to assume you are correct.  The point is for most stuff you can do object or size specific allocation.

Share this post


Link to post
Share on other sites
Advertisement
10 hours ago, Tim Leijten said:

The reason I don't like them is because I have to do something like getObjectPointer(handle); when I want to communicate with the class. I think that looks pretty ugly. Also, I don't like that with handles, it becomes harder for the user to create GameObjects themself and add them to the engine.

Oh I thought we were discussing engine architecture, not aesthetic style.

Share this post


Link to post
Share on other sites
21 hours ago, Gnollrunner said:

I believe he should be able to do a custom allocator

This is what I do so I have no use for the STL anyways, instead crafted all my containers by hand and make some book-keeping inside the memory manager using a smart_ptr that dosen't directly free the memory but calls into the memory manager on destructor. My memory manager then does anything else based on the hidden ref-counter in the head of the memory block of that object.

I care on threading because anything in my engine is task based and I utilize a fiber system, it is even context based

Share this post


Link to post
Share on other sites
43 minutes ago, Shaarigan said:

This is what I do so I have no use for the STL anyways,

I was actually referring to the fact that he can use STL smart pointers with a custom allocator.  However yes, I have my own pointer system too for various reasons.  I often don't even use containers per se. I have something I call hangers that attach links to objects using multiple inheritance.  There are lots of ways to skin a cat...

Share this post


Link to post
Share on other sites

As every for every tool: Use, whatever suits your needs.

However, for shared_ptr in combination with weak_ptr, I would recommend to avoid them and only use them if there is no better solution. My concern is not the overhead of the reference counting but the problem that shared ownership can get ugly very fast if you are not careful. They tend to be a little bit like cigarettes. Once you get used to them, you use them way too often. Read here to learn more about the things that you should keep in mind:

http://seanmiddleditch.com/dangers-of-stdshared_ptr/

https://softwareengineering.stackexchange.com/questions/133302/stdshared-ptr-as-a-last-resort

 

Never the less, you also shouldn't get a "avoid as hell" mentality. Use them if they are the best solution for your problem. But reevaluate if this is really the case.

 

As somebody else already described, I also prefer central ownership models were every resource is owned by a related management class (Texture Manager, Object Manager, Scene manager, etc.). This way it is much easier to manage object lifetime and avoid invalid references. Additionally, you have more control over memory usage. For example, if you want objects that are destroyed if no longer needed you could do it as follows:

Each stored object has an associated reference counter and is stored in a suitable container. If you want to create a new object, you call a constructor function which creates the object and returns a special reference class. The reference class is quite simple. It just holds a reference (no pointer) to the data. The constructor increases the reference counter of the referenced object and the destructor decreases it. If the counter decreases to zero, the object is removed. This mechanism is quite similar to the shared_ptr-weak_ptr  combination except that the ownership can never leave the management class (if it is used as intended - you can always get a pointer and call delete on it ;) ). In fact, you can create such a class by using shared_ptr as internal storage and return only weak pointers. The problem here is, that weak_ptr can or need to be converted to a shared_ptr if you want to use your object and then you have shared ownership issues again.

 

With unique_ptr there are no such issues and you can use them as you like. However, you need to get used to the fact, that they can't be copied and some other usage limitations.

 

For the whole allocator discussion:

I use STL containers and unique_ptr with custom allocators/deleters together with custom memory systems in my engine. Works totally fine and since the integration of allocator_traits (C++14 ?) writing an allocator is pretty simple. The only drawback to my knowledge is that memory defragmentation is hard to realize. But as Gnollrunner already said, fragmentation is not as relevant if you use your memory wisely (pool allocators, stack allocators, etc.).

 

One final note:

If you are planning on using custom allocators later, do yourself a favor and use typedefs for containers and smart_ptr

template <typename _type>
using UniquePtr = std::unique_ptr<_type>;

 

This will spare you the pain of searching for every place you used them and to add the allocator you want to use. Additionally, you can easily switch back to the std::allocator if you think that your allocators contain bugs or are too slow. However, if you use multiple allocators, you have to use multiple typedefs.

 

Greetings

 

Share this post


Link to post
Share on other sites

One major difference is that games can potentially have enormous uptimes. A player may leave a game running for days or weeks on end, suspended in the background. Unlike other documents where only a few items are loaded, games can load gigabytes of material in a short time, cycling through levels and worlds in rapid order.

Memory fragmentation is less of an issue now on machines with so many gigabytes available, but can still happen.  Basically what happens is that over time there are little gaps between memory allocations, so while you have a large amount of memory free in aggregate, none of the little gaps is large enough to hold the resource you're trying to allocate space for.

Also as mentioned, memory allocation and cleanup takes time. Allocating from the OS is slow for many reasons. One is that the global pools need time to properly interlock with every process on the computer, since potentially some other thread somewhere will allocate at the same time.  Another is that memory must be set to 0 for security purposes, and if nothing is available allocation can stall while zeros are written.  A carefully written memory manager used in a game can handle those situations.  The same thing with releasing, memory systems can be written in a way where destructors and other cleanup code is never called.  An apt analogy is to not take out the trash if you're going to demolish a building.

Generally these are tasks only needed for major commercial endeavors.  Few hobby developers will ever reach the point where they need their own, although many will implement them for their own reasons.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!