1

I'm trying to get some simple template-based design working, and stumbled onto template inter-dependence. Now I know that I can resolve this using virtual functions and (in this particular artificial case) by turning EventHandler methods into templates instead of whole class. But is there some way to have two inter-dependent templates, providing that they use only pointers to each other?

This is simplified example:

typename MySocket;

template<typename SocketClass> struct EventHandler {
    void receiveCallback(SocketClass *s) {
    }
};

template <typename HandlerType> class Socket {
    HandlerType *handler;
};

typedef EventHandler<MySocket> MyHandler ;
typedef Socket<MyHandler> MySocket ;

MyHandler h;
MySocket socket;

int main() {
    return 0;
}

For this code compiler gives an error stating that Socket is redefined. Any ideas? C++11/14 is fine for me.

1 Answers1

3

What you're tying to do is impossible given the syntax you're using.

That is, MySocket as a type doesn't exist until you've passed an appropriate parameter to your template.

Since MySocket depends on MyHandler, which in turn depends on MySocket you've got a circular dependency, so that won't work.

I suspect you then tried to solve this by forward declaring MySocket as a typename, which isn't valid C++.

There is a way to fix this though, and that is to use template template parameters.

Template template parameters allow you to pass another template as the parameter to a template

template<template<typename> class SocketT>
struct EventHandlerT;

Here SocketT is a template itself, taking 1 template parameter (hence the name template template parameter)

Inside EventHandlerT you can then define a concrete type, Socket, which uses EventHandlerT as template parameter which SocketT requires.

template<template<typename> class SocketT>
struct EventHandlerT;
{
    using Socket = SocketT<EventHandlerT>; // Socket is now a concrete type
};

When you then want to create an instance of Socket, you use the one defined inside EventHandlerT

using EventHandler = EventHandlerT<SocketT>;
using Socket       = EventHandler::Socket; 

Below is a working example:

template<template<typename> class SocketT>
struct EventHandlerT
{
    using Socket = SocketT<EventHandlerT>;

    void receiveCallback(Socket* s)
    {
    }
};

template <typename HandlerT>
struct SocketT
{
    HandlerT* handler;
};

int main()
{
    using EventHandler = EventHandlerT<SocketT>;
    using Socket       = EventHandler::Socket;

    EventHandler handler;
    Socket       socket;

    socket.handler = &handler;
    handler.receiveCallback(&socket);

    return 0;
}
Community
  • 1
  • 1
Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213