High Performance Linux



> Try Tempesta FW, a high performance open source application delivery controller for the Linux/x86-64 platform.

> Or check custom high-performance solutions from Tempesta Technologies, INC.

> Careers: if you love low-level C/C++ hacking and Linux, we'll be happy to hear from you.


Saturday, July 27, 2013

C++ Variadic Templates For Multiple Inheritance

C++ variadic templates take variable number of arguments. C++ also allows to create a template class which inherits from template base class. These two allows us to inherit from variable number of base classes. When it's needed? Let's have a look at simple example which I faced recently.

Suppose you need class Messenger which receives raw messages from a socket, assembles messages of particular types and passes them into appropriate queue. Frequently the message queues are implemented as template like

    template<class T>
    struct Queue {
        // some class body
    };

So you has following queues for each type of message:

    Queue<MsgA> q_a;
    Queue<MsgB> q_b;
    Queue<MsgC> q_c;

The queues have to be members of class Messenger. Probably, it is not a big deal to copy paste 3, as in the example, members. However, the uglinesses raises from necessity to have registering interfaces for each queue (classes who uses the Messenger need to register on particular queue to receive messages from it), serialized push() interfaces, probably the queue accessors and some other methods specific for the queue (accessing the queues directly as to public members isn't a good idea). C++ meta-programming could help to generate the queues automatically with all required interfaces. Let's have see how we can use it for this task.

Messenger provides interfaces to the queues, so it "is-a" QueueHandler. QueueHandler handles queue of particular type as a member and provides interfaces to it. So you should generate set of QueueHander classes for each queue type and inherit Messenger from all of the classes:

    template<class T>
    struct QueueHandler {
        void register(Queue<T> *q) { /* some method body */ }
        void push(T *msg) { /* some other method body */ }
    private:

        Queue<T> q_;
    };


It's also worse to specify explicitly all the base QueueHandler classes for Messenger class. So you can introduce helping class GenericMessenger, which template specialization is Messenger, and use C++ variadic template to write the class independent on particular number of serviced queues:

    template<class... Args>
    struct GenericMessenger : QueueHandler<Args>... {
        // some struct body
    };

    typedef GenericMessenger<MsgA, MsgB, MsgC> Messenger;


Therefore, if you need to support one more type of messages (and their queue of course), then you just need to add the type to Messenger definition and there is no copy paste code!

The only one ugly thing is that you need to specify explicitly base class on accessing particular queue (this is because GenericMessenger has many base classes with the same methods' names, so we need to explicitly call method of particular base):

    Messenger *m = new Messenger();
    

    m->QueueHandler<A>::register(new Queue<MsgA>);
    m->QueueHandler<A>::push(new MsgA);



1 comment:

  1. I've made research how to solve explict calls problem. Solution is pretty easy: the using keyword.
    Here is example of usage: http://pastebin.com/c9SwNyxh

    #include
    using namespace std;

    template
    struct A;

    template
    struct A : public A
    {
    // the derived class member hides or overrides (doesn't conflict with) the member that is introduced from the base class
    // http://en.cppreference.com/w/cpp/language/using_declaration
    using A::foo;

    void foo(head& val)
    {
    std::cout << "ref " << typeid(head).name() << "\n";
    }
    void foo(head&& val)
    {
    std::cout << "rval " << typeid(head).name() << "\n";
    }
    };

    template<>
    struct A<>
    {
    void foo()
    {
    std::cout << typeid(void).name() << "\n";
    }
    };



    int main()
    {
    int q;
    A a;

    a.foo(10); // calls foo(int&&)
    a.foo(q); // calls foo(int&)
    a.foo(10.0); // calls foo(double&&)
    a.foo(10u); // calls foo(uint32_t&&)
    a.foo(); // calls final definiton, foo()
    return 0;
    }

    ReplyDelete

Note: Only a member of this blog may post a comment.