2

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();
    ^
  1. I don't understand the error at all, I would much appreciate an explanation
  2. How can I actually correctly implement the 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 constructor

Tested 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

Niklas R
  • 16,299
  • 28
  • 108
  • 203
  • You need a 'virtual copy constructor' for duplicating polymorphic base classes (which is a virtual clone function). –  Jun 26 '16 at 12:10
  • @DieterLücking Ok, I was hoping it would somehow be possible to copy the rvalue without a virtual copy function. And it's probably the same case with `interface const&` instead of `interface&&`? Going the virtual copy function disrupts the design that I was aiming for. – Niklas R Jun 26 '16 at 12:46

1 Answers1

2

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 implementation takes a single (unnamed) parameter which is also a function, returning type implementation and taking no parameters [correction by Songyuanyao] and return an 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

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
max66
  • 65,235
  • 10
  • 71
  • 111
  • It works, thank you. Could you explain to me why this works? And isn't taking the address of `&impl` in the `adapter` constructor invalid because of the lifetime of `implementation()`? – Niklas R Jun 26 '16 at 11:44
  • "receive an object of type implementation" is not correct. It takes a single (unnamed) parameter which is also a function, returning type `implementation` and taking no parameters. – songyuanyao Jun 26 '16 at 12:01
  • @songyuanyao - ops! You're right; corrected; thanks. – max66 Jun 26 '16 at 12:08
  • @NiklasR - I don't see an elegant solution to the lifetime of `impl` problem; but I've modified my answer to a better-than-nothing solution; hoping this helps – max66 Jun 26 '16 at 13:07
  • Thanks for the explanation and the example to solving the lifetime problem! You're right that it's not an elegant solution, I'll have to spend some time thinking if its worth the trouble or just require the user of the interface to pass the implementation as `new implementation`. – Niklas R Jun 26 '16 at 13:12
  • @NiklasR - modified the example removing `virtual` and adding `override` in virtual methods implementation (I think it's better explicit `override` to avoid mismatchs) – max66 Jun 26 '16 at 13:15