How to handle circular dependencies with templates in C++

By , last updated November 12, 2019

Clean code (ideal)

Here is the fixed code where it will compile in both MSVC and g++.

// This file compiles in both Visual Studio 2010 (and newer) and G++ with C++11.

template<typename T>
class Message
{
    public:
        typedef T       type;

        static const int MsgId   = 0;

        explicit Message( const T & t )
            : data(t)
        {
        }

        type        data;
};

typedef Message<int>    MsgInt;
typedef Message<long>   MsgLong;
typedef Message<float>  MsgFloat;
typedef Message<double> MsgDouble;

class MessageHandler
{
    public:

        template<typename MSG, typename T>
        void setHandler( const T & msghandler )
        {
            /* do stuff */
            auto id = MSG::MsgId; // Make sure this is a message type
        }
};

// Forward declaration of classes
class Component;
class Entity;

class Component
{
    public:

        template<typename MSG>
        void setEntityHandler( Entity & entity );

        MessageHandler  m_MessageHandler;

};

class Entity
{
    public:

        template<typename MSG>
        void setComponentHandler( Component & component );

        MessageHandler  m_MessageHandler;
};

// Implementation of circular dependencies
template<typename MSG>
void Component::setEntityHandler( Entity & entity )
{
    auto handleMsg = [&] ( const MSG & msg )
    {
        /* do stuff */
    };

    entity.m_MessageHandler.setHandler<MSG>( handleMsg );
}

template<typename MSG>
void Entity::setComponentHandler( Component & component )
{
    auto handleMsg = [&] ( const MSG & msg )
    {
        /* do stuff */
    };

    component.m_MessageHandler.setHandler<MSG>( handleMsg );
}

// Main
int main()
{
    Component c;
    Entity e;

    e.setComponentHandler<MsgDouble>(c);

	return 0;
}

This file will compile in both MSVC and G++, and with MSVC it’ll produce identical assembly code for both files.

The clue to solving this problem is to declare both classes before providing the definitions (implementations). It’s not possible to split the declaration and definition into separate files, but you can structure them as if they were in separate files. Here both Entity and Component are declared before any implementation code.

In the end, this might or might not be a problem you will encounter. But when you encounter it, you’ll know about it and remember there are solutions available.

All code in the post (and build scripts) are available at�GitHub.

Professional Software Developer, doing mostly C++. Connect with Kent on Twitter.