Polymorphic objects with value semantics

(Updated on 27.11.2016 to correct the forwarding through tuples infrastructure, corrected code shows in the post, old version is available in collapsed sections. Update to the follow-up post will be posted in a few days.)

My last article got posted on reddit, quite unexpectedly for me, and in the comments there some people mentioned things they find abominable in c++ or in my coding. That’s great to know someone cares, so I decided to share one thing I personally find abominable, and some ideas how to cope. Please don’t hesitate to express your opinion, as usual, there’s a comment section below…

Too low level unique_ptr


C++11 introduced unique_ptr, which, together with move semantics, finally allowed for a reasonable management of heap-allocated objects. Unfortunately, one was still forced to write std::unique_ptr<A> ua(new A{}), keeping new on the client’s side of things. This was somewhat corrected in C++14, where you can write std::unique_ptr<A> pa = make_unique<A>{}. However, while unique_ptr has its merits in low-level implementations, I think that it is an abomination as far as dealing with objects belonging to class hierarchies is concerned. I say this, because there is no way to make deep copies (unless you hand-craft zilions of boilerplate clone methods all over the hierarchy), you can shoot yourself in the foot if you forget to make your base destructor virtual, the pointer objects have pointer const semantics, there is no immediate way to convert an existing object of one of the classes into the pointer, make_unique is the preferred way of creating the pointer but it is impossible to create a nullptr pointer with it so this case needs special treatment, this has no chance of working: std::cout << *pa, and, finally, you can call .get() on the smart pointer and leak the result (see for instance http://bartoszmilewski.com/2009/05/21/unique_ptr-how-unique-is-it/ for possible problems). I will try to address all these issues in this article. I am not sure, as usual, if somebody hasn’t already invented what I present here, if you know any references, please let me know. For a discussion on OO (aka class hierarchies) vs. value sematics see http://akrzemi1.wordpress.com/2012/02/03/value-semantics/.


As a disclaimer, I need to say that will not attempt to make the solution allocator-firendly, I am sure it can be done, but right now I know too little of them, so I will resort to plain old new.

Basic syntax for polymorphic<>


So, at what kind of syntax I am looking forward to for my polymorphic template? My first decision is to use constructors as a basic creation tool, instead of functions (what’s wrong with constructors anyway?):

struct a {};

struct b : public a 
  {
  b(int,int) {}
  };

auto main(void) -> int
  {
  polymorphic<a> p;    // empty
  polymorphic<a> q(create<b>(17,13));
  }

(I know some of you may not like the new function syntax here, but come on, this is just an exercise, it’s there for you to make up your mind). In this example I would like create to just forward the type b and arguments to the constructor of polymorphic<a>, not to produce a polymorphic and move it. How to achieve that in code? First, create has to be a function, otherwise it will not be able to infer the types in the argument pack. Second, it has to produce something that will carry all the information to the constructor of polymorphic<a>, and that something has to accomodate type (that is b) as well as data (forwarded references to arguments in the pack). Let me try then:

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

    template<class Derived, class... Args>
auto create(Args&&... args) -> auto  // use c++14 auto return type
  {
  return create_t<Derived,std::tuple<Args...>>
           (
           std::tuple<Args...>(std::forward<Args>(args)...)
           );
  }
    template<class Derived, class Tuple>
struct create_t
  {
    Tuple constructor_arguments_;
    create_t(Tuple&& t)
      : constructor_arguments_(std::forward<Tuple>(t))
      {}
  };

    template<class Derived, class... Args>
auto create(Args&&... args) -> auto  // use c++14 auto return type
  {
  return create_t<Derived,std::tuple<Args&&...>>
           (
           std::forward_as_tuple(std::forward<Args>(args)...)
           );
  }

I put the forwarded references into a tuple, that’s what seems the natural choice. I will have to come back to that later on, but for the moment let me try to consider the implementation of polymorphic<a>. First observation is that the actual type of the stored object cannot appear anywhere in its methods, it can only be provided as a template parameter to a template method. But it will have to store something, and that something is going to be a base class pointer. But using the straight type provided to the template class polymorphic would be a terrible waste of a great opportunity.

Second vtable


Let me try to provide a base class for an inner owner of the actual objects:

    template<class Base>
class polymorphic
  {
    struct owner_base
      {
      virtual ~owner_base(void) {}
      };

        template<class Derived>
    struct owner : public owner_base
      {
      Derived payload_;
      };

    std::unique_ptr<owner_base> owner_;

  public:
        template<class Derived, class... Args>
    polymorphic(create_t<Derived,std::tuple<Args...>>&& cr)
      : owner_(new owner<Derived>(/*what goes here?*/))
      {}
  };

If we manage to actually fulfill this idea, we get one thing free: the object of the Derived class will be destroyed at destruction of owner<Derived>, so its type will be known explicitly and the proper destructor of the Derived class will be called, even if destructor was not declared virtual in Base. We provide objects owned by polymorphic with a second vtable. And will exploit the idea later on.

apply for constructors


There is a problem with that code, however. I wrote a template constructor for polymorphic, and it will be given a tuple of Derived constructor arguments in a create_t object, but it needs to feed them into the constructor of Derived as comma-separated.

<Rant>What is this comma-separated stuff anyways? It is not a single entity. It pops up in many languages and causes endless problems. Wouldn’t it be cleaner to only use tuples as multiple arguments to functions and have thus a single entity similar to elements of a cartesian product?</Rant>

How to do that? There are a couple of obscure tricks using recursive metaprogramming in c++11 that allow for it, see for instance http://cpptruths.blogspot.com/2012/06/perfect-forwarding-of-parameter-groups.html and http://stackoverflow.com/questions/687490/how-do-i-expand-a-tuple-into-variadic-template-functions-arguments, but since I use c++14, I can rely on the std::integer_sequence template, in which one of those tricks has been standardised. There’s even an attempt to standardise apply (in a Technical Specification, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3915.pdf), that claims to do what we need, but it turns out useless at closer inspection, since it operates on function pointers, and we have to call a constructor, and everybody(?) knows there’s no such thing as a pointer to a constructor. I adapted those ideas to this case, but I had to go back to create_t and prepare meta-data for the constructor of polymorphic, there have to be two stages here:

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

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

    template<class Derived, class... Args>
auto create(Args&&... args) -> auto  // use c++14 auto return type
  {
  using Indices = std::index_sequence_for<Args...>; // added
  return create_t<Derived,std::tuple<Args&&...>,Indices> // added Indices
           (
           std::forward_as_tuple(std::forward<Args>(args)...)
           );
  }

We create a compile-time sequence of type std::index_sequence containing as many indices as there are elements in the argument pack, and we pass them over through the type system to the constructor of polymorphic:

    template<class Base>
class polymorphic
  {
    struct owner_base
      {
      virtual ~owner_base(void) {}
      };

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

          template<class... Args>
      owner(Args&&... args)
        : payload_(std::forward<Args>(args)...)
        {}
      };

    std::unique_ptr<owner_base> owner_;

  public:
        template<class Derived, class... Args, std::size_t... Idx>
    polymorphic
      (
      create_t<Derived,std::tuple<Args...>,std::index_sequence<Idx...>>&& cr
      )
      : owner_(new owner<Derived>
                     (
                     std::get<Idx>
                       (
                       std::forward<std::tuple<Args>>
                         (
                         cr.constructor_arguments_
                         )
                       )...
                     )
              )
      {}
  };
    template<class Base>
class polymorphic
  {
    struct owner_base
      {
      virtual ~owner_base(void) {}
      };

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

          template<class... Args>
      owner(Args&&... args)
        : payload_(std::forward<Args>(args)...)
        {}
      };

    std::unique_ptr<owner_base> owner_;

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

The constructor can now iterate through the two packs and get the work done (I don’t like the typesetting just as you do, if you have suggestions how to do it to avoid horizontal scroll bars, please let me know).

Interface of polymorphic


Ok, so we managed to construct the internal representation, but what should we be able to actually do with the polymorphic objects? Well, probably call some methods. What is the available syntax here? We might think of providing conversion to reference to Base, but this conversion is not going to be automatically used before the dot and method name. We can use operator->, even though it reminds us the pointer metaphor that we would like to avoid. And finally we could provide a function template that would accept functors callable with Base* or Base& and would be templated on the functor’s return type (actual call and its arguments being captured inside the functor). Although new and shiny, the latter solution has a disadvantage: it is impossible to make sure the canned object we devise here stays canned, the functor will be able to leak the inner pointer at will. So let’s see what we can do with operator->:

    template<class Base>
class polymorphic
  {
    struct owner_base
      {
      virtual auto ptr(void) -> Base* = 0;
      virtual ~owner_base(void) {}
      };

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

          template<class... Args>
      owner(Args&&... args)
        : payload_(std::forward<Args>(args)...)
        {}
      virtual auto ptr(void) -> Base* override
        {
        return &payload_;
        }
      };

    std::unique_ptr<owner_base> owner_;

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

    auto operator->(void) const -> const Base*
      {
      return owner_->ptr();
      }

    auto operator->(void) -> Base*
      {
      return owner_->ptr();
      }
  };
    template<class Base>
class polymorphic
  {
    struct owner_base
      {
      virtual auto ptr(void) -> Base* = 0;
      virtual ~owner_base(void) {}
      };

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

          template<class... Args>
      owner(Args&&... args)
        : payload_(std::forward<Args>(args)...)
        {}
      virtual auto ptr(void) -> Base* override
        {
        return &payload_;
        }
      };

    std::unique_ptr<owner_base> owner_;

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

    auto operator->(void) const -> const Base*
      {
      return owner_->ptr();
      }

    auto operator->(void) -> Base*
      {
      return owner_->ptr();
      }
  };

I provide two overloads of the operator, const and mutable, returning pointers to const or mutable Base, this way using it on a const polymorphic only gives you the const interface. And all would be fine, unfortunately, leaking internal pointers is even easier here than in the functor case, just call

polymorphic<A> pa(create<B>(3,5));
A* p=pa.operator->();

Not all is lost though, otherwise I would not lead you this way. operator-> does not have to return Base*. All it has to do is to return something on which again -> is callable. So, how about we inserted this

    template<class Pointer>
class honeypot
  {
    Pointer ptr_;
  public:
    honeypot(Pointer ptr)
      : ptr_(ptr)
      {}
    auto operator->(void) -> Pointer
      {
      return ptr_;
      }
  };

just before the definition of polymorphic and turn our operators into

    auto operator->(void) const -> honeypot<const Base*>
      {
      return owner_->ptr();
      }
    auto operator->(void) -> honeypot<Base*>
      {
      return owner_->ptr();
      }

thus having the compiler catch entry level abuses into the honeypot? You will answer: Duh, just write

polymorphic<A> pa(create<B>(3,5));
A* p=pa.operator->().operator->();

So let’s try again, a little more complicated now:

    template<class Pointer, int depth>
class honeypot
  {
    honeypot<Pointer, depth-1> ptr_;
  public:
    honeypot(Pointer ptr)
      : ptr_(ptr)
      {}
    auto operator->(void) -> honeypot<Pointer, depth-1>
      {
      return ptr_;
      }
  };

    template<class Pointer>
class honeypot<Pointer, 0>
  {
    Pointer ptr_;
  public:
    honeypot(Pointer ptr)
      : ptr_(ptr)
      {}
    auto operator->(void) -> Pointer
      {
      return ptr_;
      }
  };

and

    auto operator->(void) const -> honeypot<const Base*,2>
      {
      return owner_->ptr();
      }
    auto operator->(void) -> honeypot<Base*,3>
      {
      return owner_->ptr();
      }

I now see your bored face as you think: just put the appropriate number of calls, dumb. So let’s try the advanced version: how many calls would you place if we resorted to one of them compile-time random number generators (see here: http://www.researchgate.net/publication/259005783_Random_number_generator_for_C_template_metaprograms ) for the depth parameter? An indeterministic compile-time error? Yesss!

const semantics


In the code above I made sure that whenever the polymorphic container is const, only the const interface of the contained object is exposed. But that covers only one of three places where const can appear, the other two being the type parameter to polymorphic and the type parameter marshalled by create. What would that mean? I would be tempted to say that const on the Base parameter of polymorphic with non-const Derived should give you only the const interface of Base accessible through operator->, possibly leaving access to mutable parts of Derived interface accessible through the second vtable services, while both const should mean strictly const interface only. The case Base non-const and Derived const should be disallowed (and already is). Having const on one or two of the template parameters would not prevent you from assigning different contents to your polymorphic. There’s at least one problem with this approach that I can see, in that you would not be able to pass a polymorphic<a> object where a polymorphic<const a> was expected, and that should be possible. So that remains to be cared about.

Copy, move and assignments


Default construction of an empty polymorphic amounts to default construction of the inner unique_ptr, the same situation is with move construction and move assignment, but all of these are disabled because we wrote our own constructors. So all we have to do to get them back is say

polymorphic(void) = default;
polymorphic(polymorphic&&) = default;
auto operator=(polymorphic&&) -> polymorphic& = default;

Copy construction requires some work, as we want deep copies. Leveraging our second vtable, we can modify our owners and provide a convenience private method clone_owner:

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

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

          template<class... Args>
      owner(Args&&... args)
        : payload_(std::forward<Args>(args)...)
        {}
      auto ptr(void) -> Base* override
        {
        return &payload_;
        }
      auto clone(void) const -> std::unique_ptr<owner_base> override
        {
        return std::unique_ptr<owner_base>(new owner(*this));
        }
      };

    auto clone_owner(void) const -> std::unique_ptr<owner_base>
      {
      if(owner_)
        return owner_->clone();
      else
        return {};
      }

and then write

polymorphic(const polymorphic& p)
  : owner_(p.clone_owner())
  {}

auto operator=(const polymorphic& p) -> polymorphic&
  {
  polymorphic cp(p);
  std::swap(owner_,cp.owner_);
  return *this;
  }

I also promised assignability of objects of derived classes that are not canned in a polymorphic already, and I throw in for completeness emplace using the same create idiom as construction:

        template
          <
          class Derived, 
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(const Derived& d)
      : owner_(new owner<Derived>(d))
      {}

        template
          <
          class Derived, 
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(Derived&& d)
      : owner_(new owner<Derived>(std::forward<Derived>(d)))
      {}

        template<class Derived, class... Args, std::size_t... Idx>
    auto emplace
        (
        create_t<Derived,std::tuple<Args...>,std::index_sequence<Idx...>>&& cr
        )
      {
      owner_.reset(new owner<Derived>(std::get<Idx>
                                        (
                                        std::forward<std::tuple<Args>>
                                          (
                                          cr.constructor_arguments_
                                          )
                                        )...
                                      )
                  );
      }
        template
          <
          class Derived, 
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(const Derived& d)
      : owner_(new owner<Derived>(d))
      {}

        template
          <
          class Derived, 
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(Derived&& d)
      : owner_(new owner<Derived>(std::forward<Derived>(d)))
      {}

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

The SFINAE restriction on the template constructors is necessary. Without it, perfect forwarding gets in the way of other operations on the class, see for instance https://akrzemi1.wordpress.com/2013/10/10/too-perfect-forwarding/. (Added after post revision: the two converting constructors could be replaced by one polymorphic(Derived d), but I did not want to change too much.)

Other common services


The custom vtable inside polymorphic lets us do other wonders. How about being able to write

polymorphic<A> pa(create<b>(17,5));
std::cout << pa;

and having the proper operator<<(std::ostream&, const b&) called? No problem. We need to prepend the polymorphic template with a declaration for the template operator<< to be able to make it friend:

    template<class Base>
class polymorphic;

    template<class Base>
auto operator<<(std::ostream& out, const polymorphic<Base>& p) -> std::ostream&;

tnen provide a method in owner_base class:

    virtual auto output_operator(std::ostream&) -> std::ostream& = 0;

and another one in owner:

    auto output_operator(std::ostream& out) -> std::ostream& override
      {
      return out << payload_;
      }

declare the operator template as friend in polymorphic:

    friend auto operator<<<Base>(std::ostream&, const polymorphic&) -> std::ostream&;

and finally define the already declared operator template as freestanding function:

    template<class Base>
auto operator<<(std::ostream& out, const polymorphic<Base>& p) -> std::ostream& 
  {
  return p.owner_->output_operator(out);
  }

This does not come free though, as we just committed to providing such an operator for every concrete class that we are going to store in polymorphic, the template magic “not used, not instantiated” does not apply, since an instance of the operator_output method is always going to be created together with the owner instance, the method is not a template itself, and cannot be as a virtual override.

Complete code so far


It’s high time I provided here a collected code base, as I presented the solution in scattered bits. I intend to discuss one more thing below, so if you don’t need to look at the code, just scroll down.

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

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

    template<class Pointer, int depth>
class honeypot
  {
    honeypot<Pointer, depth-1> ptr_;
  public:
    honeypot(Pointer ptr)
      : ptr_(ptr)
      {}
    auto operator->(void) -> honeypot<Pointer, depth-1>
      {
      return ptr_;
      }
  };

    template<class Pointer>
class honeypot<Pointer, 0>
  {
    Pointer ptr_;
  public:
    honeypot(Pointer ptr)
      : ptr_(ptr)
      {}
    auto operator->(void) -> Pointer
      {
      return ptr_;
      }
  };

    template<class Base>
class polymorphic;

    template<class Base>
auto operator<<(std::ostream& out, const polymorphic<Base>& p) -> std::ostream&;

    template<class Base>
class polymorphic
  {
    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>
    struct owner : public owner_base
      {
      Derived payload_;

          template<class... Args>
      owner(Args&&... args)
        : payload_(std::forward<Args>(args)...)
        {}
      auto ptr(void) -> Base* override
        {
        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_;
        }
      };

    auto clone_owner(void) const -> std::unique_ptr<owner_base>
      {
      if(owner_)
        return owner_->clone();
      else
        return {};
      }

    std::unique_ptr<owner_base> owner_;

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

    polymorphic(const polymorphic& p)
      : owner_(p.clone_owner())
      {}

    auto operator=(const polymorphic& p) -> polymorphic&
      {
      polymorphic cp(p);
      std::swap(owner_,cp.owner_);
      return *this;
      }

    polymorphic(void) = default;
    polymorphic(polymorphic&&) = default;
    auto operator=(polymorphic&&) -> polymorphic& = default;

        template
          <
          class Derived, 
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(const Derived& d)
      : owner_(new owner<Derived>(d))
      {}

        template
          <
          class Derived, 
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(Derived&& d)
      : owner_(new owner<Derived>(std::forward<Derived>(d)))
      {}

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

    auto operator->(void) const -> honeypot<const Base*,2>
      {
      return owner_->ptr();
      }

    auto operator->(void) -> honeypot<Base*,3>
      {
      return owner_->ptr();
      }

    friend auto operator<<<Base>(std::ostream&, const polymorphic&) 
                  -> std::ostream&;
  };

    template<class Base>
auto operator<<(std::ostream& out, const polymorphic<Base>& p) -> std::ostream& 
  {
  return p.owner_->output_operator(out);
  }
    template<class Derived, class Tuple, class Indices> 
struct create_t
  {
    typedef Tuple tuple_type;
    Tuple constructor_arguments_;
    create_t(Tuple&& t)
      : constructor_arguments_(std::forward<Tuple>(t))
      {}
  };

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

    template<class Pointer, int depth>
class honeypot
  {
    honeypot<Pointer, depth-1> ptr_;
  public:
    honeypot(Pointer ptr)
      : ptr_(ptr)
      {}
    auto operator->(void) -> honeypot<Pointer, depth-1>
      {
      return ptr_;
      }
  };

    template<class Pointer>
class honeypot<Pointer, 0>
  {
    Pointer ptr_;
  public:
    honeypot(Pointer ptr)
      : ptr_(ptr)
      {}
    auto operator->(void) -> Pointer
      {
      return ptr_;
      }
  };

    template<class Base>
class polymorphic;

    template<class Base>
auto operator<<(std::ostream& out, const polymorphic<Base>& p) -> std::ostream&;

    template<class Base>
class polymorphic
  {
    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>
    struct owner : public owner_base
      {
      Derived payload_;

          template<class... Args>
      owner(Args&&... args)
        : payload_(std::forward<Args>(args)...)
        {}
      auto ptr(void) -> Base* override
        {
        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_;
        }
      };

    auto clone_owner(void) const -> std::unique_ptr<owner_base>
      {
      if(owner_)
        return owner_->clone();
      else
        return {};
      }

    std::unique_ptr<owner_base> owner_;

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

    polymorphic(const polymorphic& p)
      : owner_(p.clone_owner())
      {}

    auto operator=(const polymorphic& p) -> polymorphic&
      {
      polymorphic cp(p);
      std::swap(owner_,cp.owner_);
      return *this;
      }

    polymorphic(void) = default;
    polymorphic(polymorphic&&) = default;
    auto operator=(polymorphic&&) -> polymorphic& = default;

        template
          <
          class Derived, 
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(const Derived& d)
      : owner_(new owner<Derived>(d))
      {}

        template
          <
          class Derived, 
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(Derived&& d)
      : owner_(new owner<Derived>(std::forward<Derived>(d)))
      {}

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

    auto operator->(void) const -> honeypot<const Base*,2>
      {
      return owner_->ptr();
      }

    auto operator->(void) -> honeypot<Base*,3>
      {
      return owner_->ptr();
      }

    friend auto operator<<<Base>
               (
               std::ostream&, 
               const polymorphic&
               ) -> std::ostream&;
  };

    template<class Base>
auto operator<<
    (
    std::ostream& out, 
    const polymorphic<Base>& p
    ) -> std::ostream& 
  {
  return p.owner_->output_operator(out);
  }

Tampering with the interface


It would seem that the second vtable idea did work well in the cases above. We were able to turn some object services into virtual ones outside of their classes, inside our container. However, each and every of them required hand-crafting a virtual communication channel into the inner owner_base hierarchy. That’s fine for standard operations, like copy-construction, or stream output, but I am curious if it is possible to add arbitrary other operations without reprogramming polymorphic. The magic so far works because code written as a template inside owner<> is instantiated in the templated constructor of polymorphic, that’s the only place in code where both types, Base and Derived, are known, and that’s where static binding does the job. So we would have to somehow marshall our extension of virtual interface, as a template, into that constructor, and also find a way of calling the extended services from the outside world. I could think of one solution: make polymorphic dependent not only on Base, but also on some Visitor. I could then come up with the code below, although I don’t particularly like it, since this way of doing things makes it difficult, if not impossible, to assign polymorphic objects differing in Visitor but having the same Base. Moreover, it allows one to leak the inner pointers. If you have any ideas how to overcome it, or how to do it in a different manner, I would be glad to hear.

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

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

    template<class Pointer, int depth>
class honeypot
  {
    honeypot<Pointer, depth-1> ptr_;
  public:
    honeypot(Pointer ptr)
      : ptr_(ptr)
      {}
    auto operator->(void) -> honeypot<Pointer, depth-1>
      {
      return ptr_;
      }
  };

    template<class Pointer>
class honeypot<Pointer, 0>
  {
    Pointer ptr_;
  public:
    honeypot(Pointer ptr)
      : ptr_(ptr)
      {}
    auto operator->(void) -> Pointer
      {
      return ptr_;
      }
  };

    template<class Base,class Visitor>
class polymorphic;

    template<class Base, class Visitor>
auto operator<<(std::ostream& out, const polymorphic<Base,Visitor>& p) 
       -> std::ostream&;

class empty_visitor
  {
  public:
        template<class Derived>
    auto visit(Derived& p) -> void
      {}
  };
  
    template<class Base, class Visitor = empty_visitor>
class polymorphic
  {
    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 auto accept_visitor(Visitor& v) -> void = 0;
      virtual ~owner_base(void) {}
      };

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

          template<class... Args>
      owner(Args&&... args)
        : payload_(std::forward<Args>(args)...)
        {}
      auto ptr(void) -> Base* override
        {
        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_;
        }
      auto accept_visitor(Visitor& v) -> void override
        {
        v.visit(payload_);
        }
      };

    auto clone_owner(void) const -> std::unique_ptr<owner_base>
      {
      if(owner_)
        return owner_->clone();
      else
        return {};
      }

    std::unique_ptr<owner_base> owner_;

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

    polymorphic(const polymorphic& p)
      : owner_(p.clone_owner())
      {}

    auto operator=(const polymorphic& p) -> polymorphic&
      {
      polymorphic cp(p);
      std::swap(owner_,cp.owner_);
      return *this;
      }

    polymorphic(void) = default;
    polymorphic(polymorphic&&) = default;
    auto operator=(polymorphic&&) -> polymorphic& = default;

        template
          <
          class Derived,
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(const Derived& d)
      : owner_(new owner<Derived>(d))
      {}

        template
          <
          class Derived,
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(Derived&& d)
      : owner_(new owner<Derived>(std::forward<Derived>(d)))
      {}

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

    auto operator->(void) const -> honeypot<const Base*,2>
      {
      return owner_->ptr();
      }

    auto operator->(void) -> honeypot<Base*,3>
      {
      return owner_->ptr();
      }
      
    auto accept_visitor(Visitor& v) -> void
      {
      owner_->accept_visitor(v);
      }

    friend auto operator<<<Base>(std::ostream&, const polymorphic&)
                  -> std::ostream&;
  };

    template<class Base>
auto operator<<(std::ostream& out, const polymorphic<Base>& p) -> std::ostream&
  {
  return p.owner_->output_operator(out);
  }

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

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

    template<class Pointer, int depth>
class honeypot
  {
    honeypot<Pointer, depth-1> ptr_;
  public:
    honeypot(Pointer ptr)
      : ptr_(ptr)
      {}
    auto operator->(void) -> honeypot<Pointer, depth-1>
      {
      return ptr_;
      }
  };

    template<class Pointer>
class honeypot<Pointer, 0>
  {
    Pointer ptr_;
  public:
    honeypot(Pointer ptr)
      : ptr_(ptr)
      {}
    auto operator->(void) -> Pointer
      {
      return ptr_;
      }
  };

    template<class Base,class Visitor>
class polymorphic;

    template<class Base, class Visitor>
auto operator<<(std::ostream& out, const polymorphic<Base,Visitor>& p)
       -> std::ostream&;

class empty_visitor
  {
  public:
        template<class Derived>
    auto visit(Derived& p) -> void
      {}
  };

    template<class Base, class Visitor = empty_visitor>
class polymorphic
  {
    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 auto accept_visitor(Visitor& v) -> void = 0;
      virtual ~owner_base(void) {}
      };

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

          template<class... Args>
      owner(Args&&... args)
        : payload_(std::forward<Args>(args)...)
        {}
      auto ptr(void) -> Base* override
        {
        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_;
        }
      auto accept_visitor(Visitor& v) -> void override
        {
        v.visit(payload_);
        }
      };

    auto clone_owner(void) const -> std::unique_ptr<owner_base>
      {
      if(owner_)
        return owner_->clone();
      else
        return {};
      }

    std::unique_ptr<owner_base> owner_;

  public:
        template<class Derived, class... Args, std::size_t... Idx>
    polymorphic
      (
      create_t<Derived,std::tuple<Args...>,std::index_sequence<Idx...>>&& cr
      )
      : owner_(new owner<Derived>
                     (
                     std::forward<std::tuple_element_t
                                    <
                                    Idx,
                                    typename create_t
                                               <
                                               Derived,
                                               std::tuple<Args...>,
                                               std::index_sequence<Idx...>
                                               >::tuple_type
                                    >
                                 >
                       (
                       std::get<Idx>
                         (
                         cr.constructor_arguments_
                         )
                       )...
                     )
              )
      {}
    polymorphic(const polymorphic& p)
      : owner_(p.clone_owner())
      {}

    auto operator=(const polymorphic& p) -> polymorphic&
      {
      polymorphic cp(p);
      std::swap(owner_,cp.owner_);
      return *this;
      }

    polymorphic(void) = default;
    polymorphic(polymorphic&&) = default;
    auto operator=(polymorphic&&) -> polymorphic& = default;

        template
          <
          class Derived,
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(const Derived& d)
      : owner_(new owner<Derived>(d))
      {}

        template
          <
          class Derived,
          class e = typename std::enable_if
                               <
                               std::is_base_of<Base,Derived>::value
                               >::type
          >
    polymorphic(Derived&& d)
      : owner_(new owner<Derived>(std::forward<Derived>(d)))
      {}

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


    auto operator->(void) const -> honeypot<const Base*,2>
      {
      return owner_->ptr();
      }

    auto operator->(void) -> honeypot<Base*,3>
      {
      return owner_->ptr();
      }

    auto accept_visitor(Visitor& v) -> void
      {
      owner_->accept_visitor(v);
      }

    friend auto operator<<<Base,Visitor>
               (
               std::ostream&, 
               const polymorphic&
               ) -> std::ostream&;
  };

    template<class Base, class Visitor>
auto operator<<
    (
    std::ostream& out, 
    const polymorphic<Base, Visitor>& p
    ) -> std::ostream&
  {
  return p.owner_->output_operator(out);
  }

Final remarks


This code is experimental, it went straight to the blog, without even standing close to any production stuff, even toy production. It certainly has sharp edges here and there, and I must have forgotten about a lot of things, but I wanted it out quick. If you have any remarks, please don’t hesitate to write. Thank you for reading, and happy hacking.

PS. There is a follow–up to this article: Polymorphic objects with value semantics – custom interfaces

Advertisements

One Response to Polymorphic objects with value semantics

  1. Pingback: Polymorphic objects with value semantics – custom interfaces | 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: