I am trying to write an adapter
class for an interface class that accepts a) an implementation of the interface, which should be stack-allocated (so no new/delete handling should be required from the outside, the adapter itself may use new/delete) and b) a lambda function that will be called by a respective implementation of the interface.
#include <iostream>
#include <functional>
struct interface {
virtual int hello() = 0;
};
struct implementation : public interface {
virtual int hello() {
std::cout << "hello()\n";
return 42;
}
};
struct adapter {
interface* obj;
adapter(std::function<int()>&& func) {
struct lambda : public interface {
std::function<int()> func;
lambda(std::function<int()> func_): func(func_) { }
virtual int hello() {
return this->func();
}
};
this->obj = new lambda{func};
}
adapter(interface&& impl) {
// TODO: pretty sure that's incorrect
// but can I somehow create a copy of "impl" on the heap?
this->obj = &impl;
}
};
int main() {
adapter a([]() { std::cout << "hello from lambda\n"; return 99; });
a.obj->hello();
#if 0
// ERROR
adapter b(implementation());
b.obj->hello();
#endif
return 0;
}
This is the error I get when enabling the adapter b
part.
prog.cpp: In function 'int main()':
prog.cpp:39:4: error: request for member 'obj' in 'b', which is of non-class type 'adapter(implementation (*)())'
b.obj->hello();
^
adapter(interface&&)
constructor? I will probably need to create a copy of the object on the heap, otherwise it won't be persistent after the adapater
constructorTested on ideone: http://ideone.com/Gz3ICk with C++14 (gcc-5.1)
PS: Yes the adapter
class lacks a destructor that should delete obj
created from the lambda constructor
Try with
adapter b {implementation()};
The problem was that
adapter b(implementation());
wasn't interpreted (if I'm not wrong) as instantiation of an object of type adapter
but was interpreted as a declaration of a function of name b
that receive an object of type takes a single (unnamed) parameter which is also a function, returning type implementation and taking no parameters [correction by Songyuanyao] and return an implementation
adapter
.
I know two solution to solve this ambiguity
1) add a couple of parentheses
adapter b((implementation()));
2) use the new uniform initialization style based on braced parentheses
adapter b {implementation()};
I suggest the form 2 because your using C++11 and (IMHO) it's clearer.
--- Added example to solve the lifetime problem ---
To solve the copy/clone/lifetime of impl
, well... you are using the pointer to a pure virtual base class; the only solution that I see imply cloning the derived class.
I propose a solution where I've
1) switched obj
in adapter
from interface *
to std::unique_ptr<interface>
(to avoid problems with deallocation)
2) added a pure virtual member clone()
in interfece
that return a std::unique_ptr<interface>
3) added an intermediate template class (interHelper
) to implement clone()
only one time
The following is my proposed solution
#include <memory>
#include <iostream>
#include <functional>
struct interface
{
virtual int hello() = 0;
virtual std::unique_ptr<interface> clone () const = 0;
};
template <typename D>
struct interHelper : public interface
{
std::unique_ptr<interface> clone() const override
{ return std::unique_ptr<interface>(new D((const D &)(*this))); }
};
struct implementation : public interHelper<implementation>
{
int hello() override
{
std::cout << "hello()\n";
return 42;
}
};
struct adapter
{
struct lambda : public interHelper<lambda>
{
std::function<int()> func;
lambda (std::function<int()> func_): func(func_)
{ }
int hello() override
{ return this->func(); }
};
std::unique_ptr<interface> obj;
adapter (std::function<int()>&& func) : obj { lambda{func}.clone() }
{ }
adapter (interface&& impl) : obj { impl.clone() }
{ }
};
int main()
{
adapter a([]() { std::cout << "hello from lambda\n"; return 99; });
a.obj->hello();
adapter b { implementation() };
b.obj->hello();
return 0;
}
p.s.: sorry for my bad English