Object pools, variadic templates and reference forwarding

Right, so after my last post it seemed pretty clear that I need to overhaul how I manage the creation and deletion of my components, and that object pooling looked like the best way to do that. So I referred back to Cameron’s comment from ages ago and started to write some object pooling classes for each of my components. However I very quickly worked out that I’d be writing the same thing over and over again.

So, clearly I needed to write a templated version. Once again Cameron had an amazing example that I could have a look at. I decided it was quite a bit more complex than what I needed for now, so I started making a simplified version. I can’t believe how much I’d forgotten about templates – it’s actually a bit embarrassing. It’s really driving home how much more I should be practising on my own C++ projects if I don’t want to forget everything I’ve ever learned while I am stuck using C# at work every day.

I converted the original PhysicsComponentPool I’d started to write into a ComponentPool pretty quickly. I had written a “create” function that accepted the parameters needed to initialise a PhysicsComponent so that I could do just that in the function and return the newly activated and initialised PhysicsComponent. Converting this into a more generic function stumped me for a while.

My options were:

  • write a number of overloaded “create” functions that would suit every component type I had made.
  • leave the responsibility of initialisation up to the class that called the “create” function. The create function would then just be responsible for activating a component and maintaining the state of the object pool.
  • work out how to send variable parameters into my “create” function and how to set up the “init” functions of my components to work with this.

The latter idea was based on Cameron’s “new_object” function in the FixedObjectPool class, but there was a lot going on in his implementation that I didn’t understand and it didn’t quite seem to fit what I wanted. So I did some research to both understand Cameron’s implementation and to find a solution to my problem.

I first worked out that template is an implementation of variadic templates which were introduced in C++11. Variadic templates allow me to send any number of parameters of any type(s) into a function, like so:

template<typename T>
template<typename... Args>
T* ComponentPool::Create(Args... args)
{

}

So, that’s great! Now I can just send these arguments along to the Init function for the component like this:

template
template
T* ComponentPool::Create(Args... args)
{
    for (unsigned int i = 0; i < m_iSize; ++i)
    {
        if (m_pActive[i] != true)
        {
            m_pComponent[i].Init(args);
            m_pActive[i] = true;
            return &m_pComponent[i];
        }
    }
    return nullptr;
}

Uh, no. That does not compile: argserror.

So I then looked at how to expand variadic parameters. It was in a recursive-like fashion where each recursive call to the function unpacked one more argument. Not exactly what I wanted. I just wanted to send ALL the arguments along to my init function. I took another look at Cameron’s example and discovered that he was using something called std::forward. Let’s try that:

template
template
T* ComponentPool::Create(Args... args)
{
    for (unsigned int i = 0; i < m_iSize; ++i)
    {
        if (m_pActive[i] != true)
        {
            m_pComponent[i].Init(std::forward(args)...);
            m_pActive[i] = true;
            return &m_pComponent[i];
        }
    }
    return nullptr;
}

That worked perfectly! I still didn’t really know what std::forward was really doing though. The documentation online was not very easy to understand, but I found a great blog post that made things a bit clearer. std::forward returns an r-value reference to its parameter and placing the ellipses (…) outside of the call to std::forward means that std::forward will be called for each argument in args, which effectively unpacks args while also preserving the lvalue or rvalue status of the arg.

Preserving an argument’s lvalue or rvalue nature is known as perfect forwarding. It’s explained quite well in the post I linked above, as well as in this stack overflow post, so I’m not going to try and re-explain it.

After reading those two posts it was clear that my Create function was not forwarding references properly, so I needed to change it a bit:

template<typename... Args>
T* Create(Args&&... args);

Much better 🙂

Midway through that research I did realise that std::forward wasn’t actually responsible for unpacking the argument list. It was the ellipses. So just having:

m_pComponent[i].Init(args...);

also worked the way I wanted. However it was probably a good idea to preserving the references (though I did try the non forwarded version with both arguments that I had thought were lvalues and arguments I thought were rvalues and everything worked correctly. Perhaps perfect forwarding is just to prevent lvalue copy inefficiencies?)

As always you can have a look at my code on github (still in the component-pattern-setup branch for the moment).

Thanks for all the responses to my last post – they gave me a lot to think about.

Advertisements

One thought on “Object pools, variadic templates and reference forwarding

  1. Oh hey, glad you found that useful!

    My pool is using a free list of unused blocks, so most of what new_object is doing is finding the next free entry on the free list, then doing a placement new on the memory. It’s similar in purpose to your active flag but in theory means there’s no linear search to find free memory.

    I think perfect forwarding fixes some problems that existed with this kind of pattern pre C++11. You couldn’t safely do this before perfect forwarding because the reference parameter type info would be lost, e.g. if you pass a object& it would be forwarded as an object value, which means it got copy constructed, which is not what you want and usually caused mysterious bugs. It worked OK for const object& or object* but not object&.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s