Polymorphic objects with value semantics – custom interfaces

polymorphic values


In my previous post I showed how we could provide object handlers, called polymorhic, that were the cure to most of the troubles that follow from using pointers, even smart pointers. Given class b with base class a we could write

{
polymorphic<a> p1(create<b>(args_to_constructor_of_b));
p1->foo();              // properly calls foo virtual or not
polymorphic<a> p2 = p1; // real deep copy of contained b
const auto p3 = std::move(p1); // real move from p1
p3->mutate();           // doesn't compile, implements ordinary const semantics
b bobj;
p2 = bobj;                 // properly copies bobj of type b into p2
std::cout << p2;        // calls operator<< suitable for b
}              // p2,p3 correctly delete b even with no virtual destructor in a

All this boon has been achieved by keeping and leveraging a second virtual dispatch table inside polymorphic. The remaining question was how to open the interface of polymorphic to using additional configurable interfaces–vtables. I will try to present two solutions to this problem in this post.

Who knows what


What should that mean, “custom interface”? One possible answer was given in the previous post, by introducing a second Visitor template parameter to polymorphic, but that answer is hardly satisfactory, since it seems impossible to provide for interoperation of polymorphic objects with the same type but different visitors, code reachable through the second virtual table that does the actual cloning does not have the type information about the target Visitor, this would have to come from a template parameter and virtual functions cannot be template instances, at least I have not found a hack to emulate that (not yet, is the favourite answer of my PhD advisor when he is told a sentence with a negation, not yet).

So, what were the true purposes Visitor served in that answer? Well, first, it provided for the type of the custom interface in the templated constructor of polymorphic. Second, it was used on the interface of polymorphic objects themselves, to allow for calls that go through it. Usage in both of these places was implicit, since polymorphic<Base,Visitor> was visible, no additional template parameter was needed. This means that if we remove Visitor from the template parameters list of polymorphic, we will have to provide type information separately in both places. Moreover, since Visitor was known to owner_base, we were able to embed the virtual communication channel in the owner_base hierarchy, and so Visitor did not need to bother about virtual methods. This is no longer going to be the case, so in fact we will need to provide different (but coherent) type information for the constructor and for the actual calls.

Functionality


Enough talk, let’s get to work. How do we provide for type information in the constructor of polymorphic? We marshall it through the create suite. What sort of type is going to be needed there? It has to be a template class, inheriting from a pure non–template interface, with virtual overrides that do the dirty work. Template, because we will need an instance for whatever type we actually create inside polymorphic. The pure non–template interface will serve to identify the custom interface on calls done through the interface of polymorphic. Let me show that on the following example

struct a
  {
  virtual auto foo(int) -> int = 0;
  };
  
struct b : public a
  {
  b(int,int) {}
  auto foo(int k) -> int override
    {
    return 2*k;
    }
  auto bar(int k) -> int
    {
    return 3*k;
    }
  auto baz(int k) -> int
    {
    return 4*k;
    }
  };

struct bar_interface_base
  {
  virtual auto bar(int) -> int = 0;
  };

    template<class Derived>
struct bar_interface : public bar_interface_base
  {
  virtual auto typed_ptr(void) -> Derived* = 0;
  auto bar(int k) -> int override
    {
    return this->typed_ptr()->bar(k);
    }
  };

What I want to do with these is:

polymorphic<a> pa(create<b,bar_interface>(1,2));
assert(2==pa->foo(1));  // taken care of by solution of previous post
assert(3==pa.interface<bar_interface_base>()->bar(1)) // new mechanism
assert(4==pa.interface<baz_interface_base>()->baz(1)) // throws std::bad_cast

It seems reasonable to me to expect the user to know what custom interfaces are provided on an object. There’s even a possibility to query an object for the presence of an interface, but there’s not any kind of list of them.

Implementation


Let me then try to implement that functionality in code, throwing in, in addition, the possibility to specify an entire list of interfaces in create, not just one. I will need an upgrade of the create suite to accomodate for the templated class pack:

    template
      <
      class Derived, 
      class Tuple, 
      class Indices, 
      template<class>class... Interfaces
      >
struct create_t
  {
    Tuple constructor_arguments_;
    create_t(Tuple&& t)
      : constructor_arguments_(std::forward<Tuple>(t))
      {}
  };

    template<class Derived, template<class>class... Interfaces, class... Args>
auto create(Args&&... args) -> auto
  {
  using Indices = std::index_sequence_for<Args...>;
  return create_t<Derived,std::tuple<Args...>,Indices,Interfaces...>
           (
           std::tuple<Args...>(std::forward<Args>(args)...)
           );
  }
    template
      <
      class Derived,
      class Tuple,
      class Indices,
      template<class>class... Interfaces
      >
struct create_t
  {
    typedef Tuple tuple_type;
    Tuple constructor_arguments_;
    create_t(Tuple&& t)
      : constructor_arguments_(std::forward<Tuple>(t))
      {}
  };

    template
      <
      class Derived,
      template<class>class... Interfaces,
      class... Args
      >
auto create(Args&&... args) -> auto
  {
  using Indices = std::index_sequence_for<Args...>;
  return create_t<Derived,std::tuple<Args&&...>,Indices,Interfaces...>
           (
           std::forward_as_tuple(std::forward<Args>(args)...)
           );
  }

Using a type pack for Interfaces means that we do not need to care about the situation when no custom interface is specified, it just integrates smoothly. Then I need to change the constructor and emplace to transfer that to owner:

        template
          <
          class Derived, 
          class... Args, 
          std::size_t... Idx, 
          template<class>class... Interfaces
          >
    polymorphic
      (
      create_t
        <
        Derived,
        std::tuple<Args...>,
        std::index_sequence<Idx...>,
        Interfaces...
        >&& cr
      )
      : owner_(new owner<Derived,Interfaces...>
                     (
                     std::get<Idx>
                       (
                       std::forward<std::tuple<Args>>
                         (
                         cr.constructor_arguments_
                         )
                       )...
                     )
              )
      {}

        template
          <
          class Derived, 
          class... Args, 
          std::size_t... Idx, 
          template<class>class... Interfaces
          >
    auto emplace
      (
      create_t
        <
        Derived,
        std::tuple<Args...>,
        std::index_sequence<Idx...>,
        Interfaces...
        >&& cr
      )
      {
      owner_.reset(new owner<Derived,Interfaces...>
                         (
                         std::get<Idx>
                           (
                           std::forward<std::tuple<Args>>
                             (
                             cr.constructor_arguments_
                             )
                           )...
                        )
                  );
      }

Our next stop is the owner_base hierarchy:

    struct owner_base
      {
      virtual auto ptr(void) -> Base* = 0;
      virtual auto clone(void) const -> std::unique_ptr<owner_base> = 0;
      virtual auto output_operator(std::ostream&) -> std::ostream& = 0;
      virtual ~owner_base(void) {}
      };

        template<class Derived, template<class>class... Interfaces>
    struct owner : public owner_base, public Interfaces<Derived>...
      {
      Derived payload_;

          template<class... Args>
      owner(Args&&... args)
        : payload_(std::forward<Args>(args)...)
        {}
      auto ptr(void) -> Base* override
        {
        return &payload_;
        }
      auto typed_ptr(void) -> Derived* // this usually is an override provided 
                                       // that the Interfaces pack is not empty
        {
        return &payload_;
        }
      auto clone(void) const -> std::unique_ptr<owner_base> override
        {
        return std::unique_ptr<owner_base>(new owner(*this));
        }
      auto output_operator(std::ostream& out) -> std::ostream& override
        {
        return out << payload_;
        }
      };

owner_base didn’t really change, but the template owner inherits from all the instances of interfaces for the type Derived, and produces an override for typed_ptr. Caveat: one must not put the override clause where it seems to belong, otherwise the compiler is going to protest if the pack Interfaces... is empty. The last installment is the interface method of polymorphic itself:

        template<class InterfaceBase>
    auto interface(void) const -> honeypot<InterfaceBase*,4>
      {
      return &dynamic_cast<InterfaceBase&>(*owner_.get());
      }

Now, owner.get() gives you a pointer to owner_base, at the address of which is really sitting an object of type owner with suitable template parameters, and possibly inheriting from the given interfaces and interface bases. If an interface was given to create, dynamic_cast will be able to do a side cast to its base, which is what you really need. There’s an additional quirk in the code, in that I wanted the reference version of dynamic_cast, this way I get an exception on failure, dynamic_cast in the pointer variant would just return nullptr. And since we worked hard on producing honeypot, why not use it here as well. It’s true, though, that the user can now produce an interface that will leak internal pointers, but that unfortunately seems unavoidable.

Isn’t it ugly?


I wonder if you share my feelings about the work that is being left for the user of our services, let me repeat it here:

struct bar_interface_base
  {
  virtual auto bar(int) -> int = 0;
  };

    template<class Derived>
struct bar_interface : public bar_interface_base
  {
  virtual auto typed_ptr(void) -> Derived* = 0;
  auto bar(int k) -> int override
    {
    return this->typed_ptr()->bar(k);
    }
  };

Boilerplate, that’s what it is. Insupportable. Want to trade boilerplate for syntactic sugar, despite all the Perlis? Read on.

Syntactic sugar


We are going to indulge in such perverted c++ pleasures in a while, that I should probably be asking you to declare that you are of age. But, never mind.

So I would like to limit client code to more or less this:

    template<class Derived>
struct bar_interface
  {
  auto bar(int k) -> int
    {
    return typed_ptr()->bar(k);
    }
  };

Impossible, you say. Where’s the virtual mechanism? What the heck is typed_ptr()? Virtual? Here you go:

    template<class Derived>
struct bar_interface
  {
  virtual auto bar(int k) -> int
    {
    return typed_ptr()->bar(k);
    }
  };

Why virtual and not override? Because we are going to inherit from the same class. Yes, I’m not out of my mind, this is a template, and we need another instance of that template as a base class. Unfortunately, this really has to be a base class, if a would–be override method is side–inherited, it will not be part of the virtual call mechanism (not yet! not yet!):

struct x
  {
  virtual auto work(void) -> void {}
  };
struct y
  {
  virtual auto work(void) -> void {}
  };
struct w : public y
  {};
struct z : public x, public w
  {}
auto main(void) -> int
  {
  z zi;
  y* py = &zi;
  py -> work();  // calls y::work, not x::work, no inheritance chain
  }

So I could force the client programmer to write the heresy herself, or let the library do the dirty thing:

    template<class Derived, class... Base>
struct bar_interface: public Base...
  {
  virtual auto bar(int k) -> int
    {
    return typed_ptr()->bar(k);
    }
  };

and that is my choice, as it results in less code, and in code that does not change as we write another interface. Ok, you probably already recovered from the shock at the idea of inheriting from oneself, so I may talk fine details. What are we going to use as that base class, what template parameters to the interface? Well, void for Derived and empty Base pack seem to be the most reasonable choice. However, this template instance has no type information on the original Derived, and the compiler will not let us write that call, even if we provided a typed_ptr() that would return a void* pointer. Indeed. We only need that bar implementation code if Derived is not void. Is it possible? Yes, just write a custom function or class template that will be called from this particular interface and provide a specialization for void. Here we go. Boilerplate again. Unless. Unless we use the new c++14 addition called generic lambda:

    template<class Derived, class... Base>
struct bar_interface: public Base...
  {
  virtual auto bar(int k) -> int
    {
    auto fun=[&](auto ptr){return ptr->bar(k);};
    return fun(typed_ptr()); 
    }
  };

fun is a first–class object, with its own custom type unique to that particular lambda. It comes with a templated operator(), and templated means the “not used not instantiated” magic works. There is no std::function instance compatible with that object, as there is not enough type information to do that (argument to operator() is templated, and for the compiler there is no way of knowing that each intended instance is going to return the same type) but it is usable with other templates. That saves a lot of boilerplate typing. The abover version still isn’t good though, as we are still trying to unconditionally call the implementation. However, the conditionality can now be factored out of client’s code, into library services:

    template<typename ReturnType, typename PointerType, class Functor>
struct call_t
  {
    Functor& functor_;
    explicit call_t(Functor&& functor)
      : functor_(functor)
      {}
    auto operator()(PointerType ptr) -> ReturnType
      {
      return functor_(ptr);
      }
  };

    template<typename ReturnType, class Functor>
struct call_t<ReturnType, void*,  Functor>
  {
    explicit call_t(Functor functor)
      {}
    auto operator()(void*) -> ReturnType
      {
      ReturnType* ptr=nullptr;
      return *ptr; // never intended to be called
      }
  };

    template<class ReturnType, class PointerType, class Functor>
auto call(Functor&& functor) -> auto
  {
  return call_t<ReturnType, PointerType, Functor>
           (
           std::forward<Functor>(functor)
           );
  }

    template<class Derived, class... Base>
struct bar_interface: public Base...
  {
  virtual auto bar(int k) -> int
    {
    auto fun=[&](auto ptr){return ptr->bar(k);};
    return call<int,Derived*>(fun)(typed_ptr());
    }
  };

So, instead of calling fun, we ask template call to do it, and if it sees void*, it does not. The template call has to be given the return type of your interface function, it will not be able to infer it from instantiation of operator() of the functor in the void* case because there is no instantiantion then, and some value has to be returned. A point to notice is that we are never going to physically call bar for bar_interface<void>, since it is virtual and calls will resolve to some derived class override. Thus, the sole functionality of call_t<ReturnType,void*,Functor> is merely to make your compiler happy. Consciously returning a dereferenced dangling pointer in its operator() is one of the pleasures I advertised.

That leaves us with the outstanding typed_ptr(). Where do we actually get the pointer from? Should we really bother the programmer to insert a virtual typed_ptr into the interface? Not nice. Boilerplate, clutter, and it would be a different virtual function for every template instance. We should really let the library take care of that:

    template<template<class,class...>class UserInterface, class Derived>
struct interface
  : public UserInterface<Derived,UserInterface<void>>
  {
  virtual auto typed_ptr(void) -> Derived* = 0;
  };

    template
      <
      template<class,class...>class UserInterface, 
      class Derived, 
      class... UIBases
      >
struct get_ptr_t
  {
  static auto get(UserInterface<Derived,UIBases...>* i) -> Derived*
    {
    return dynamic_cast<interface<UserInterface,Derived>&>(*i).typed_ptr();
    }
  };

    template
      <
      template<class,class...>class UserInterface, 
      class... UIBases
      >
struct get_ptr_t<UserInterface, void, UIBases...>
  {
  static auto get(UserInterface<void,UIBases...>* i) -> void*
    {
    return nullptr;
    }
  };

    template
      <
      template<class,class...>class UserInterface, 
      class Derived, 
      class... UIBases
      >
auto get_ptr(UserInterface<Derived,UIBases...>* i) -> Derived*
  {
  return get_ptr_t<UserInterface,Derived,UIBases...>::get(i);
  };

    template<class Derived, class... Base>
struct bar_interface: public Base...
  {
  virtual auto bar(int k) -> int
    {
    auto fun=[&](auto ptr){return ptr->bar(k);};
    return call<int,Derived*>(fun)(get_ptr(this));
    }
  };

We are not going to use straight bar_interface objects internally, instead, we have an interface template that inherits from bar_interface and that we are going to use as a base class for owner inside polymorphic. So, this in the scope of bar_interface can be cast to suitable interface, and pointer to actual object with proper type retrieved. Or not, if we are in the void case and have no type information to perform that cast, but then again, we don’t really need that pointer, the compiler does.

So, with a bit of syntactic sugar as flavour, all the programmer has to care about is the signature and return type of bar itself, implementation body within the generic lambda, but that looks like plain old c++ enough, and the first template argument to call where the return type of bar has to be written for a second time. (Side note: is it possible to get from the compiler the return type of the current function without typing its name?) What remains is integration of the above with polymorphic. We will need an upgrade of the create suite to accomodate for different template arguments pattern of the interfaces:

    template
      <
      class Derived, 
      class Tuple, 
      class Indices, 
      template<class,class...>class... UserInterfaces
      >
struct create_t
  {
    Tuple constructor_arguments_;
    create_t(Tuple&& t)
      : constructor_arguments_(std::forward<Tuple>(t))
      {}
  };

    template
      <
      class Derived, 
      template<class,class...>class... UserInterfaces, 
      class... Args
      >
auto create(Args&&... args) -> auto
  {
  using Indices = std::index_sequence_for<Args...>;
  return create_t<Derived,std::tuple<Args...>,Indices,UserInterfaces...>
           (
           std::tuple<Args...>(std::forward<Args>(args)...)
           );
  }

and then an upgrade of constructor and emplace in polymorphic:

        template
          <
          class Derived, 
          class... Args, 
          std::size_t... Idx, 
          template<class,class...>class... UserInterfaces
          >
    polymorphic
      (
      create_t
        <
        Derived,
        std::tuple<Args...>,
        std::index_sequence<Idx...>,
        UserInterfaces...
        >&& cr
      )
      : owner_(new owner<Derived,UserInterfaces...>
                     (
                     std::get<Idx>
                       (
                       std::forward<std::tuple<Args>>
                         (
                         cr.constructor_arguments_
                         )
                       )...
                     )
              )
      {}

        template
          <
          class Derived, 
          class... Args, 
          std::size_t... Idx, 
          template<class,class...>class... UserInterfaces
          >
    auto emplace
        (
        create_t
          <
          Derived,
          std::tuple<Args...>,
          std::index_sequence<Idx...>,
          UserInterfaces...
          >&& cr
        )
      {
      owner_.reset(new owner<Derived,UserInterfaces...>(std::get<Idx>
                                        (
                                        std::forward<std::tuple<Args>>
                                          (
                                          cr.constructor_arguments_
                                          )
                                        )...
                                     )
                  );
      }

that instantiate owner of the owner_base hierarchy that needs an update as well:

        struct owner_base
          {
          virtual auto ptr(void) -> Base* = 0;
          virtual auto clone(void) const -> std::unique_ptr<owner_base> = 0;
          virtual auto output_operator(std::ostream&) -> std::ostream& = 0;
          virtual ~owner_base(void) {}
          };

            template<class Derived, template<class,class...>class... UserInterfaces>
        struct owner : public owner_base, public interface<UserInterfaces,Derived>...
          {
          Derived payload_;

              template<class... Args>
          owner(Args&&... args)
            : payload_(std::forward<Args>(args)...)
            {}
          auto ptr(void) -> Base* override
            {
            return &payload_;
            }
          auto typed_ptr(void) -> Derived* // this really is an override provided 
                                           // that the Interfaces pack is not empty
            {
            return &payload_;
            }
          auto clone(void) const -> std::unique_ptr<owner_base> override
            {
            return std::unique_ptr<owner_base>(new owner(*this));
            }
          auto output_operator(std::ostream& out) -> std::ostream& override
            {
            return out << payload_;
            }
          };

Finally, to allow calls from outside, the interface method in polymorphic needs an upgrade too:

        template<template<class,class...>class UserInterface>
    auto interface(void) const -> honeypot<UserInterface<void>*,4>
      {
      return &dynamic_cast<UserInterface<void>&>(*owner_.get());
      }

Now, using all the machinery and assuming we have baz_interface defined similarly to bar_interface, we can do this:

polymorphic<A> na(create<B,bar_interface>(1,2));
try
  {
  na.interface<bar_interface>()->bar(17); // ok
  na.interface<baz_interface>()->baz(17); // throws std::bad_cast
  }
catch(std::exception& e)
  {
  std::cerr << e.what() << "\n";
  }

using the same name for interface specification in create as well as interface selection in method interface

Hope that you enjoyed the ride. Complete code versions will be available soon. Happy hacking.

Advertisements

One Response to Polymorphic objects with value semantics – custom interfaces

  1. Pingback: Polymorphic objects with value semantics | Programming blog of Łukasz Wojakowski

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: