简体   繁体   中英

Variadic template inheritence, member function overloading

I am trying to rewrite the templated class http://docs.ros.org/hydro/api/rviz/html/c++/message__filter__display_8h_source.html to be used with multiple Message types, using variadic templates.

My first problem was how I can rewrite the example code below using variadic templates so it can be used with an arbitrary number of template arguments, not just 2.

What I need in the parent class:

  • a virtual member function processMessage for every templated type
  • a member function for every incomingMessage for every templated type
  • a member variable for every templated type. (will later be the Subsriber for the topic of that MessageType in ROS)

So if invoked for example with 2 templated types, the variadic base class should compile to something like this:

Includes:

#include<string>
#include<sstream>
#include<iostream>
using namespace std;

Working code (usual templates):

template<class MessageType1,class MessageType2> class Parent{
public:

    Parent() : messages_received_(0){}

    virtual void processMessage(MessageType1 msg) = 0;
    virtual void processMessage(MessageType2 msg) = 0;

    void incomingMessage(MessageType1 msg){
        processMessage(msg);
        incr();
    }

    void incomingMessage(MessageType2 msg){
        processMessage(msg);
        incr();
    }

private:
    void incr(){
        cout<<"received "<<++messages_received_<<endl;;
    }

    MessageType1 sub1_;
    MessageType2 sub2_;

    int messages_received_;
};

Not working (variadic):

template<class... Elements> class Parent;
template<> class Parent<>{};

template<class Head, class... Tail> class Parent<Head, Tail...> : public Parent<Tail...> {
public:
    Parent() : messages_received_(0){}

    virtual void processMessage(Head msg) = 0;

    void incomingMessage(Head msg){
        processMessage(msg);
        incr();
    }

private:
    void incr(){
        cout<<"received "<<++messages_received_<<endl;;
    }

    Head sub1_;

    int messages_received_;
};

the compilation failed with:

g++ variadic.cpp --std=c++0x
variadic.cpp: In function ‘int main()’:
variadic.cpp:52:33: error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]
/usr/include/c++/4.6/bits/basic_string.h:485:7: error:   initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]’ [-fpermissive]

So I guess that somehow, the member function processMessage is only compiled to processMessage(std::string s) and not to the overloaded version processMessage(int a) ;

Example usage:

class Child : public Parent<std::string, int> {
public:
    void processMessage(std::string msg){
        cout<<"string: "<<msg<<endl;
    }

    void processMessage(int msg){
        cout<<"int: "<<msg<<endl;
    }
};

int main()
{
        Child myMfd;

        myMfd.incomingMessage(42);
        myMfd.incomingMessage("abc");

        return 0;
}

How do I fix this issue?

I haven't tested this, but it should be somewhere along these lines:

template<typename ...Args> class Parent;

template<> class Parent<> {

public:
    void incr();
    void incomingMessage() {}
};

template<typename MessageType, typename ...Args>
class Parent<MessageType, Args...> : public Parent<Args...> {

public:
    virtual void processMessage(MessageType msg)=0;

    using Parent<Args...>::incomingMessage;
    void incomingMessage(MessageType msg)
    {
        processMessage(msg);
        this->incr();
    }
};

This is not perfect, you need to "propagate up" incomingMessage from the previous class, in order for it to resolve correctly in the "top level scope", so an ugly incomingMessage() in the root superclass is needed. With a bit more work, there's probably a way around that, too.

The problem is that the declaration of incomingMessage in one specialisation of Parent hides the declaration in the base class specialisation; so the only available overload in your Child class is that for string in the immediate base class.

The simplest solution is to add a using declaration to Parent to make all overloads available:

using Parent<Tail...>::incomingMessage;

You'll also need a declaration in the "root" specialisation to support this:

template<> struct Parent<>{
    void incomingMessage(); // No need for a definition
};

You may also want to move messages_received_ into the "root" specialisation, so that there's a single counter for all message types rather than one for each type. If you do that, remember that it's a dependent name, so derived specialisations will have to refer to it as this->messages_received_ or Parent<>::messages_received_ .

Child inherits Parent<std::string, int> , which is instantiated from template <class... Elements> class Parent . Um, let's see how compiler would instantinate it.

template<>
class Parent<int>
    : public Parent<>
{
    // ...
    virtual void processMessage(int msg) = 0;

    void incomingMessage(int msg)
    {
        processMessage(msg);
        incr();
    }
    // ...
};

template<>
class Parent<std::string, int>
    : public Parent<int>
{
    // ...
    virtual void processMessage(std::string msg) = 0;

    void incomingMessage(std::string msg)
    {
        processMessage(msg);
        incr();
    }
    // ...
};

class Child : public Parent<std::string, int>
{
    // ...

Parent<int> , the grandparent class, has void incomingMessage(int) of course, but Parent<std::string>, int> , the parent class, has void incomingMessage(std::string) and it hides Parent<int>::incomingMessage(int) .

So what to do? - just un-hide incomingMessage of the super class by "using" it.

template<class Head, class... Tail>
class Parent<Head, Tail...>
    : public Parent<Tail...>
{
public:
    using Parent<Tail...>::incomingMessage;
    // ...

of course, the dummy root Parent should also have incomingMessage .

template <> class Parent<>
{
public:
    // to forbid call dummy `incomingMessage`.
    class dummy_ { private: dummy_() = delete; };
    void incomingMessage(dummy_);
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM