Generic class generating templated methods based on the std::tuple

Viewed 51

I'm trying to figure out how the template method generation works inside a class. I've found code that describes the functionality which I would like to use but cannot understand how are the methods being generated. Desired functionality is the automatic creation of methods that are templated based on the std::tuple arguments. Code snippet was taken from: https://commschamp.github.io/comms_protocols_cpp/ Here's the code:

template <typename TCommon, typename TAll>
class GenericHandler;

template <typename TCommon, typename TFirst, typename... TRest>
class GenericHandler<TCommon, std::tuple<TFirst, TRest...> > :
                        public GenericHandler<TCommon, std::tuple<TRest...> >
{
    using Base = GenericHandler<TCommon, std::tuple<TRest...> >;
public:
    using Base::handle; // Don't hide all handle() functions from base classes
    virtual void handle(TFirst& msg)
    {
        // By default call handle(TCommon&)
        this->handle(static_cast<TCommon&>(msg));
    }
};

template <typename TCommon>
class GenericHandler<TCommon, std::tuple<> >
{
public:
    virtual ~GenericHandler() {}
    virtual void handle(TCommon&)
    {
        // Nothing to do
    }
};

Example of usage:

class Message
{
    
    public:
    Message() = default;
    virtual ~Message() {};
};


class X : public Message
{
    public:
    X() = default;
    virtual ~X() {}
    int x;
    
};

class Y : public Message
{
    
    public:
    Y() = default;
    virtual ~Y() {}
    int y;
    
};

using AllMessages = std::tuple<X,Y>;


class Handler : public GenericHandler<Message, AllMessages> {};


int main()
{
    X x;
    Handler h;
    h.handle(x);
    std::cout<<"Hello World";

    return 0;
}

As I understand the template <typename TCommon, typename TFirst, typename... TRest> class GenericHandler<TCommon, std::tuple<TFirst, TRest...> > generates the handle() method and when the std::tuple is empty then the template <typename TCommon> class GenericHandler<TCommon, std::tuple<> > is used.

How does using Base::handle; work in this example? How does GenericHandler inheritance work in this case template <typename TCommon, typename TFirst, typename... TRest> class GenericHandler<TCommon, std::tuple<TFirst, TRest...> > : public GenericHandler<TCommon, std::tuple<TRest...> >?

What is the process of generating the template methods?

Edit: I'm bound to C++ 11, but I'm interested in what can be done on the newer versions of C++.

1 Answers

How does using Base::handle; work in this example?

It's explained by its comment:

// Don't hide all handle() functions from base classes

The template class inherits from a parent template class, which also has its implementation of handle(). In the template's handle():

this->handle(static_cast<TCommon&>(msg));

Since this has a handle() (which just happens to be where this function call is made), normally overload resolution would find this handle() and only this handle().

However this ends up calling the parent class's handle(), thanks to the using declaration which has the effect of pulling in the parent class's handle(), which then gets picked by overload resolution. This is one of the fundamental rules of overload resolution, if you have:

pointer->method();

and the class has a method(), overload resolution stops, and will not include any method()s from the parent class. Here the intent is to call the parent class's method. The using declaration has the effect of adding the parent class's method to the subclass's namespace, and include it in any overload resolution.

The same thing can probably be accomplished without the using declaration, and by calling

this->Base::handle(static_cast<TCommon&>(msg));

explicitly.

Edit: I'm bound to the C++ 11, but I'm interested what can be done on the newer versions of C++.

The newer versions of C++ still work the same way.

Related