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:
processMessage
for every templated type incomingMessage
for every templated type 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.