Search code examples
c++c++11interfacemove-semanticsobject-lifetime

adapter class for C++ interface with move constructor


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


Solution

  • 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