I'm having trouble using template member functions and template variables. The goal is to create an template for an interface which can be inherited and linked to one or more services initialized at runtime. This interface would also pass data to these specialized services (e.g. MyService). For a simpler example, with this code:
#include <string>
class Interface
{
public:
template <class T> bool init(T *_Service) {AttachService(*_Service);} //This function does other stuff too.
template <typename T> void AttachService(T _Service) { m_AttachedService<T> = *_Service; }
template <typename T> T AttachedService() { return m_AttachedService; }
protected:
template<typename T> static T m_AttachedService;
class InterfaceListener
{
void Received()
{
int a = 1;
std::string b = "hello";
AttachedService().setA(a);
m_AttachedService.setB(b);
};
};
};
class Service
{
Service();
~Service();
virtual void init() = 0;
};
class MyService : public Service, public Interface
{
MyService();
~MyService();
private:
int A;
std::string B;
protected:
Interface x;
public:
void init() { x.init(this);}
void setA(int a) { A = a; }
void setB(std::string b) { B = b; }
};
int main();
{
MyService myserv;
myserv.init();
}
I get the following errors:
C2672: 'Interface::AttachedService': no matching overloaded function found
C2783: 'T Interface::AttachedService(void)': could not deduce template argument for 'T'
C2228: left of '.setA' must have class/struct/union
C3245: 'Interface::m_AttachedService': use of a variable template requires template argument list
Any help understanding proper usage of templates would be appreciated!
Let's start with a minimal program that builds and runs without doing anything useful.
#include <string>
class Interface
{
};
class Service
{
protected:
Service() {}
~Service() {}
virtual void init() = 0;
};
class MyService : public Service, public Interface
{
private:
int A;
std::string B;
protected:
Interface x;
public:
MyService() {}
~MyService() {}
void init() {}
void setA(int a) { A = a; }
void setB(std::string b) { B = b; }
};
int main()
{
MyService myserv;
myserv.init();
}
You'll notice that there are few changes from your posted code to get to that baseline.
Interface is empty.
The member functions of Service are in public section. The default constructor and the destructor of Service have an empty implementation.
The default constructor and the destructor of MyService are in the public section of the class and they have empty implementations.
MyService::init() has an empty implementation.
Now we can start adding more code.
I changed MyService::init() to:
void init() { x.init(this); }
With no other changes, I got the following compiler error.
socc.cc: In member function ‘virtual void MyService::init()’:
socc.cc:25:23: error: ‘class Interface’ has no member named ‘init’
void init() { x.init(this);}
Now, Interface needs to be updated with an init function.
I added a dummy implementation to move the process forward.
class Interface
{
public:
template <class T> bool init(T *_Service) { return true; }
};
Time to add something useful to Interface::init(). Changing it to
template <class T> bool init(T *_Service)
{
AttachService(*_Service);
return true;
}
produces the following compiler error, which is not surprising.
socc.cc: In instantiation of ‘bool Interface::init(T*) [with T = MyService]’:
socc.cc:31:32: required from here
socc.cc:8:23: error: ‘AttachService’ was not declared in this scope
AttachService(*_Service);
~~~~~~~~~~~~~^~~~~~~~~~~
Time to add AttachService. Changing Interface to (closing matching what you have)
class Interface
{
public:
template <class T> bool init(T *_Service)
{
AttachService(*_Service);
return true;
}
template <typename T> void AttachService(T _Service)
{
m_AttachedService<T> = *_Service;
}
protected:
template<typename T> static T m_AttachedService;
};
produces the following compiler error.
socc.cc: In instantiation of ‘void Interface::AttachService(T) [with T = MyService]’:
socc.cc:8:10: required from ‘bool Interface::init(T*) [with T = MyService]’
socc.cc:39:32: required from here
socc.cc:14:33: error: no match for ‘operator*’ (operand type is ‘MyService’)
m_AttachedService<T> = *_Service;
^~~~~~~~~
That makes sense. In AttachServie, _Service is not a pointer.
Changing Inteface::AttachService to:
template <typename T> void AttachService(T _Service)
{
m_AttachedService<T> = _Service;
}
makes the compiler error go away but there is a linker error.
:socc.cc:(.rdata$.refptr._ZN9Interface17m_AttachedServiceI9MyServiceEE[.refptr._ZN9Interface17m_AttachedServiceI9MyServiceEE]+0x0): undefined reference to `Interface::m_AttachedService<MyService>'
collect2: error: ld returned 1 exit status
That makes sense since we have not defined the static member variable.
Adding the following line
template<typename T> T Interface::m_AttachedService;
right after definition of Interface takes care of the linker error.
The following is the next version of the complete program that builds successfully and runs even though it still doesn't do anything useful.
#include <string>
class Interface
{
public:
template <class T> bool init(T *_Service)
{
AttachService(*_Service);
return true;
}
template <typename T> void AttachService(T _Service)
{
m_AttachedService<T> = _Service;
}
protected:
template<typename T> static T m_AttachedService;
};
template<typename T> T Interface::m_AttachedService;
class Service
{
protected:
Service() {}
~Service() {}
virtual void init() = 0;
};
class MyService : public Service, public Interface
{
private:
int A;
std::string B;
protected:
Interface x;
public:
MyService() {}
~MyService() {}
void init() { x.init(this); }
void setA(int a) { A = a; }
void setB(std::string b) { B = b; }
};
int main()
{
MyService myserv;
myserv.init();
}
Time to add your version of the AttachedService to Interface
template <typename T> T AttachedService() { return m_AttachedService; }
That produces the following compiler error.
socc.cc: In member function ‘T Interface::AttachedService()’:
socc.cc:17:73: error: missing template arguments before ‘;’ token
template <typename T> T AttachedService() { return m_AttachedService; }
That makes sense since m_AttachedService is not a member variable but a member variable template.
Changing that to
template <typename T> T AttachedService() { return m_AttachedService<T>; }
removes that error.
Now the final piece in Interface. The nested class InterfaceListener that you have posted does sound right. You have
class InterfaceListener
{
void Received()
{
int a = 1;
std::string b = "hello";
AttachedService().setA(a);
m_AttachedService.setB(b);
};
};
Problems in that class:
AttachedService() is not right since it a member function template. You have to provide a template parameter to use it.
Also, AttachedService() is not a static member function. You need an instance of Interface to make that call.
m_AttachedService is not a member variable. It is a member variable template. You have to provide a template parameter to use it.
The functions setA() and setB() are valid only if the template parameter is MyService. It does not make sense to have code in that function that is specific to a type.
I'll leave it for you to ponder over how you intend to use InterfaceListener and define its functions appropriately. Until then the following program builds and runs for me.
#include <string>
class Interface
{
public:
template <class T> bool init(T *_Service)
{
AttachService(*_Service);
return true;
}
template <typename T> void AttachService(T _Service)
{
m_AttachedService<T> = _Service;
}
template <typename T> T AttachedService() { return m_AttachedService<T>; }
protected:
template<typename T> static T m_AttachedService;
};
template<typename T> T Interface::m_AttachedService;
class Service
{
protected:
Service() {}
~Service() {}
virtual void init() = 0;
};
class MyService : public Service, public Interface
{
private:
int A;
std::string B;
protected:
Interface x;
public:
MyService() {}
~MyService() {}
void init() { x.init(this); }
void setA(int a) { A = a; }
void setB(std::string b) { B = b; }
};
int main()
{
MyService myserv;
myserv.init();
}
Related
I am trying to create something similar to a type-safe multiqueue. The idea is that when I push an item, it will be added to a queue consisting of objects of the same type.
All the queues will have a common interface(the queue_intf in this case) that will do some processing.
The code I came up is the following:
#include <queue>
#include <vector>
#include <memory>
#include <stdlib.h>
class queue_intf {
public:
virtual void process(void) = 0;
};
template <typename T>
class queue : public queue_intf {
public:
std::queue<T> q_;
static const char* name()
{
return typeid(T).name();
}
virtual void process(void) override {
printf("process: %s\n", this->name());
}
void push(T &a) {
printf("push: %s\n", this->name());
}
};
template <typename...>
class queues {
public:
std::vector<queue_intf *> qs_;
void process(void) {
for (auto q: this->qs_) {
q->process();
}
}
};
template <typename T, typename... Ts>
class queues<T, Ts...> : public queues<Ts...> {
public:
queue<T> q_;
queues() {
this->qs_.push_back(&this->q_);
}
void push(T &v) {
q_.push(v);
}
};
class a {
};
class b {
};
int
main (int argc, char *argv[])
{
queues<a, b> qs;
a ai;
b bi;
qs.push(ai);
qs.process();
qs.push(bi);
}
However when I compile it I get the following error:
main.cc: In function ‘int main(int, char**)’:
main.cc:65:15: error: no matching function for call to ‘queues<a, b>::push(b&)’
qs.push(bi);
^
main.cc:45:10: note: candidate: void queues<T, Ts ...>::push(T&) [with T = a; Ts = {b}]
void push(T &v) {
^~~~
main.cc:45:10: note: no known conversion for argument 1 from ‘b’ to ‘a&’
I was expecting the queue class to have void push(b &v) method, but it seems it doesn't.
Any idea why?
Edit:
Here is a smaller example (no std:vector or anything):
template <typename...>
class queues {
};
template <typename T, typename... Ts>
class queues<T, Ts...> : public queues<Ts...> {
public:
void push(T &v) {
}
};
class a {
};
class b {
};
int
main (int argc, char *argv[])
{
queues<a, b> qs;
a ai;
b bi;
qs.push(ai);
qs.push(bi);
}
To explain this, let's use a simplified example, you have just two classes:
class base {
public:
void method(int *);
};
class derived : public base {
public:
void method(char *);
};
And you attempt to call it:
derived d;
int i;
d.method(&i);
This fails. Name lookup starts at the given derived class, and finds a matching class method with the specified name: method. However its parameter doesn't match, so this is ill-formed. If the derived class had both methods, overload resolution will pick the matching one. But once a class method with the specified name is found any parent class will not be searched for any other methods with the same name. Only if the derived class does not have any methods with the specified name does name lookup continue with the parent class(es).
Putting
using base::method;
In the derived class imports base's method into the derived's namespace, at which point everything works just like with regular overload resolution.
This is why you need a using declaration with your recursive templates. The second derived template imports it from the first one, the next one imports everything from the second derived template, and so on.
I have mock defined as follows:
template<typename T>
class ParseTreeMock : public ParseTreeInterface<T> {
public:
MOCK_METHOD1(fillConfigTree, void(std::string const&));
MOCK_METHOD1_T(getProperty, T(std::string const&));
ParseTreeMock(): parseTree(std::make_unique<pt::ptree>()) {
}
static std::unique_ptr<ParseTreeInterface<T>> getDefaultTree() {
return std::make_unique<ParseTreeMock<T>>();
}
private:
std::unique_ptr<pt::ptree> parseTree;
};
which is created later in test case:
class ConfigTest : public ::testing::Test {
protected:
std::unique_ptr<ParseTreeInterface<std::string>> treeMock;
virtual void SetUp() {
treeMock = ParseTreeMock<std::string>::getDefaultTree();
}
};
And I want to set return specific value on getProperty method:
EXPECT_CALL(*treeMock, getProperty("miniReaderConfig.cacheConfig.cacheOnOff")).willOnce(Return(false));
I get error:
In file included from ./lib/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h:43:0,
from ./lib/googletest/googlemock/include/gmock/gmock.h:61,
from ./test/UT/Mocks/ParseTreeMock.hpp:2,
from test/UT/Configuration/ConfigTest.cpp:1:
test/UT/Configuration/ConfigTest.cpp: In member function ‘virtual void ConfigTest_CreateConfigurationWithoutErrors_Test::TestBody()’:
./lib/googletest/googlemock/include/gmock/gmock-spec-builders.h:1844:12: error: ‘class miniReader::Configuration::ParseTreeInterface<std::__cxx11::basic_string<char> >’ has no member named ‘gmock_getProperty’; did you mean ‘getProperty’?
((obj).gmock_##call).InternalExpectedAt(__FILE__, __LINE__, #obj, #call)
Any solution with explanation of error appreciated.
The treeMock variable needs to be of type std::unique_ptr<ParseTreeMock<std::string>>, and then static method needs to look like this
static std::unique_ptr<ParseTreeMock<T>> getDefaultTree()
{
return std::make_unique<ParseTreeMock<T>>();
}
Typically you instantiate a class that implements an interface in your test and then pass the instance to the class that you are testing, and with EXPECT_CALLs you make sure that the class you are testing calls the callbacks on your mock object.
Not related to the error you got, but WillOnce needs to be spelled with the first letter capitalized. Also, since you set the template variable to std::string, the EXPECT_CALL can't expect that a boolean is returned.
This compiles for me:
namespace pt { struct ptree {};}
template<typename T>
class ParseTreeInterface
{
public:
virtual void fillConfigTree(std::string const&) = 0;
virtual T getProperty(std::string const&) = 0;
};
template<typename T>
class ParseTreeMock : public ParseTreeInterface<T> {
public:
MOCK_METHOD1(fillConfigTree, void(std::string const&));
MOCK_METHOD1_T(getProperty, T(std::string const&));
ParseTreeMock(): parseTree(std::make_unique<pt::ptree>()) {
}
static std::unique_ptr<ParseTreeMock<T>> getDefaultTree()
{
return std::make_unique<ParseTreeMock<T>>();
}
private:
std::unique_ptr<pt::ptree> parseTree;
};
class ConfigTest : public ::testing::Test {
protected:
std::unique_ptr<ParseTreeMock<std::string>> treeMock;
virtual void SetUp() {
treeMock = ParseTreeMock<std::string>::getDefaultTree();
}
};
TEST_F(ConfigTest, test)
{
EXPECT_CALL(*treeMock, getProperty("miniReaderConfig.cacheConfig.cacheOnOff")).WillOnce(::testing::Return(""));
}
Ok, maybe not the best title, but here's the deal:
I have a templated interface:
template<typename T>
class MyInterface
{
public:
struct MyStruct
{
T value;
};
virtual void doThis(MyStruct* aPtr) = 0;
};
and an implementation:
template <typename T>
class MyImpl : public MyInterface<T>
{
public:
void doThis(MyStruct* aPtr)
{
} // doThis
};
However, the compiler complains:
In file included from MyTest.cpp:3:0:
MyImpl.h:7:17: error: ‘MyStruct’ has not been declared
void doThis(MyStruct* aPtr)
Why is that?
The following compiled for me:
template<typename T>
class MyInterface
{
public:
struct MyStruct
{
T value;
};
virtual void doThis(MyStruct* aPtr) = 0;
};
template <typename T>
class MyImpl : public MyInterface<T>
{
public:
void doThis(typename MyInterface<T>::MyStruct* aPtr)
{
}
};
int main() {
MyImpl<int> t;
}
The main change is that you need to qualify that the MyStruct was defined within MyInterface<T>.
Since the compiler cannot determine what kind of identifier is the templated subtype, you must help it using the typename keyword. (See When is the "typename" keyword necessary? for more details on typename)
template <typename T>
class BaseQueue
{
public :
virtual void push_back(T value) = 0;
//other virtual methods
};
template <typename T>
class BaseDeque: public virtual BaseQueue<T>
{
public:
virtual void push_front(T value) = 0;
//other virtual methods
};
//Realisation
template <typename T>
class VectorQueue: public BaseQueue<T>
{
typedef typename std::vector<T> array;
private: array adata;
public:
VectorQueue()
{
adata = array();
}
void push_back(T value)
{
adata.push_back(value);
}
};
template <typename T>
class VectorDeque: virtual public VectorQueue<T>, virtual protected BaseDeque<T>//,
{
void push_front(T value)
{
VectorQueue::adata.push_front(value);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
VectorDeque<int> vd = VectorDeque<int>();//here is a error
int i;
std::cin >> i;
return 0;
}
I have such error: "C2259: 'VectorDeque' : cannot instantiate abstract class ...". How can I fix it? Class VectorQueue has realize every virtual method of BaseQueue class already. But the compiler doesn't know it. The only way I see is to write something like this:
template <typename T>
class VectorDeque: virtual public VectorQueue<T>, virtual protected BaseDeque<T>//,
{
void push_front(T value)
{
VectorQueue::adata.push_front(value);
}
void push_back(T value)
{
VectorQueue::push_back(value);
}
//repeat it fo every virtual method of BaseQueue class (interface)
};
But it's awful.
push_back from BaseQueue isn't implemented on the BaseDeque side of the inheritance chain, and thus the childmost class is still abstract.
I think you're trying to force a class relationship here that shouldn't exist. Note how in the standard library deque and vector are distinct container types and things like queue adapt those containers to very precise interfaces rather than trying to inherit.
Even if you solve your diamond issue (or follow #Mark B's advice and keep them separate), you have a few other issues in there:
template <typename T>
class VectorQueue: public BaseQueue<T>
{
typedef typename std::vector<T> array;
private: array adata; // if this is private, VectorDeque can't reach it
public:
// constructors have an initializer section
// member variables should be initialized there, not in the body
VectorQueue()
// : adata() // however, no need to explicitly call default constructor
{
// adata = array();
}
};
template <typename T>
class VectorDeque: virtual public VectorQueue<T>, virtual protected BaseDeque<T>
{
void push_front(T value)
{
// if adata is protected, you can just access it. No need for scoping
/*VectorQueue::*/ adata.push_front(value);
// Error: std::vector doesn't have a method push_front.
// Perhaps you meant to use std::list?
}
};
Multiple inheritance and static polymorphism are of help, for instance:
// Abstract bases
template <typename T, typename Val>
class BaseQueue
{
public :
void push_back(Val val)
{
static_cast<T*>(this)->push_back(val);
}
// ...
};
template <typename T, typename Val>
class BaseDeque
{
public:
void push_front(Val val)
{
static_cast<T*>(this)->push_front(val);
}
// ...
};
// Concrete class
#include <deque>
template <typename Val>
class QueueDeque:
public BaseQueue<QueueDeque<Val>, Val>,
public BaseDeque<QueueDeque<Val>, Val>
{
std::deque<Val> vals;
public:
void push_front(Val val)
{
vals.push_front(val);
}
void push_back(Val val)
{
vals.push_back(val);
}
// etc..
};
int main()
{
QueueDeque<int> vd;// no more error
vd.push_front(5);
vd.push_back(0);
return 0;
}
I have a method foo in class C which either calls foo_1 or foo_2.
This method foo() has to be defined in C because foo() is pure virtual in BaseClass and I actually
have to make objects of type C. Code below:
template <class T>
class C:public BaseClass{
void foo() {
if (something()) foo_1;
else foo_2;
}
void foo_1() {
....
}
void foo_2() {
....
T t;
t.bar(); // requires class T to provide a method bar()
....
}
};
Now for most types T foo_1 will suffice but for some types foo_2 will be called
(depending on something()). However the compiler insists on instantiating both foo_1
and foo_2 because either may be called.
This places a burden on T that it has to provide
a bar method.
How do I tell the compiler the following:
if T does not have bar(), still allow it as an instantiating type?
you could use boost.enable_if. something like this:
#include <boost/utility/enable_if.hpp>
#include <iostream>
struct T1 {
static const bool has_bar = true;
void bar() { std::cout << "bar" << std::endl; }
};
struct T2 {
static const bool has_bar = false;
};
struct BaseClass {};
template <class T>
class C: public BaseClass {
public:
void foo() {
do_foo<T>();
}
void foo_1() {
// ....
}
template <class U>
void foo_2(typename boost::enable_if_c<U::has_bar>::type* = 0) {
// ....
T t;
t.bar(); // requires class T to provide a method bar()
// ....
}
private:
bool something() const { return false; }
template <class U>
void do_foo(typename boost::enable_if_c<U::has_bar>::type* = 0) {
if (something()) foo_1();
else foo_2<U>();
}
template <class U>
void do_foo(typename boost::disable_if_c<U::has_bar>::type* = 0) {
if (something()) foo_1();
// I dunno what you want to happen if there is no T::bar()
}
};
int main() {
C<T1> c;
c.foo();
}
You could create an interface for foo_1 and foo_2, such as:
class IFoo
{
public:
virtual void foo_1()=0;
virtual void foo_2()=0;
};
template <typename T>
class C : public BaseClass, public IFoo
{
void foo()
{
if (something())
foo_1();
else
foo_2();
}
};
template <typename T>
class DerivedWithBar : C<T>
{
public:
void foo_1() { ... }
void foo_2()
{
...
T t;
t.bar(); // requires class T to provide a method bar()
...
}
};
template <typename T>
class DerivedNoBar : C<T>
{
public:
void foo_1() { ... }
void foo_2() { ... }
};
I think the easiest way is to simply write a separate function template that 'C' can call:
template <class T>
void call_bar(T& /*t*/)
{
}
template <>
void call_bar<Something>(Something& t)
{
t.bar();
}
The original 'C' class can be modified accordingly:
void foo_2() {
....
T t;
call_bar(t); // does not require T to provide bar()
....
}
This has the downside that you have to explicitly define which types of T provide a bar method, but that's pretty much inevitable unless you can determine something at compile-time about all the types that do provide a bar method in their public interface or modify all these bar-supporting types so that they do share something in common that can be determined at compile-time.