How to Have Polymorphic Containers with Value Semantics in C++

Can I have polymorphic containers with value semantics in C++?

Since the objects of different classes will have different sizes, you would end up running into the slicing problem if you store them as values.

One reasonable solution is to store container safe smart pointers. I normally use boost::shared_ptr which is safe to store in a container. Note that std::auto_ptr is not.

vector<shared_ptr<Parent>> vec;
vec.push_back(shared_ptr<Parent>(new Child()));

shared_ptr uses reference counting so it will not delete the underlying instance until all references are removed.

Can I Have Polymorphic Containers With Value Semantics in C++11?

Just for fun, based on James's comment about a template-based system, I came up with this Vector-like implementation. It's missing lots of features, and may be buggy, but it's a start!

#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>

template <typename T>
class Vector
{
public:
T &operator[] (int i) const { return p[i]->get(); }
template <typename D>
void push_back(D &x) { p.push_back(ptr_t(new DerivedNode<D>(x))); }

private:
class Node
{
public:
virtual T &get() = 0;
};

template <typename D>
class DerivedNode : public Node
{
public:
DerivedNode(D &x) : x(x) {}
virtual D &get() { return x; }
private:
D x;
};

typedef boost::shared_ptr<Node> ptr_t;
std::vector<ptr_t> p;
};

///////////////////////////////////////

class Parent
{
public:
Parent() : parent_mem(1) {}
virtual void write() const { std::cout << "Parent: " << parent_mem << std::endl; }
int parent_mem;
};

class Child : public Parent
{
public:
Child() : child_mem(2) { parent_mem = 2; }
void write() const { std::cout << "Child: " << parent_mem << ", " << child_mem << std::endl; }

int child_mem;
};

int main()
{
Vector<Parent> v;

v.push_back(Parent());
v.push_back(Child());

v[0].write();
v[1].write();
}

Run-time polymorphic class with value semantics

Yes, it is possible, but of course there must be some hidden pointer, and the actual data must be stored on the heap. The reason is that the actual size of the data cannot be known at compile-time, and then can't be on the stack.

The idea is to store the actual implementation through a pointer of a polymorphic class ValueImpl, that provides any virtual method you need, like increments() or print(), and in addition a method clone(), so that your class Data is able to implement the value semantics:

class ValueImpl
{
public:
virtual ~ValueImpl() {};
virtual std::unique_ptr<ValueImpl> clone() const { return new ValueImpl(); }
virtual void increments() {}
virtual void print() const { std::cout << "VoidValue "; }
};

class Value
{
private:
ValueImpl * p_; // The underlying pointer

public:
// Default constructor, allocating a "void" value
Value() : p_(new ValueImpl) {}

// Construct a Value given an actual implementation:
// This allocates memory on the heap, hidden in clone()
// This memory is automatically deallocated by unique_ptr
Value(const ValueImpl & derived) : p_(derived.clone()) {}

// Destruct the data (unique_ptr automatically deallocates the memory)
~Value() {}

// Copy constructor and assignment operator:
// Implements a value semantics by allocating new memory
Value(const Value & other) : p_(other.p_->clone()) {}
Value & operator=(const Value & other)
{
if(&other != this)
{
p_ = std::move(other.p_->clone());
}
return *this;
}

// Custom "polymorphic" methods
void increments() { p_->increments(); }
void print() { p_->print(); }
};

The contained pointer is stored inside a C++11 std::unique_ptr<ValueImpl> to ensure the memory is released when destroyed or assigned a new value.

The derived implementations can finally be defined the following way:

class IntValue : public ValueImpl
{
public:
IntValue(int k) : k_(k) {}
std::unique_ptr<IntValue> clone() const
{
return std::unique_ptr<IntValue>(new IntValue(k_));
}
void increments() { k_++; }
void print() const { std::cout << "Int(" << k_ << ") "; }

private:
int k_;
};

class DoubleValue : public ValueImpl
{
public:
DoubleValue(double x) : x_(x) {}
std::unique_ptr<DoubleValue> clone() const
{
return std::unique_ptr<DoubleValue>(new DoubleValue(k_));
}
void increments() { x_ += 1.0; }
void print() const { std::cout << "Double(" << x_ << ") "; }

private:
int x_;
};

Which is enough to make the code snippet in the question works without any modification. This provides run-time polymorphism with value semantics, instead of the traditional run-time polymorphism with pointer semantics provided built-in by the C++ language. In fact, the concept of polymorphism (handling generic objects that behave differently according to their true "type") is independent from the concept of pointers (being able to share memory and optimize function calls by using the address of an object), and IMHO it is more for implementation details that polymorphism is only provided via pointers in C++. The code above is a work-around to take advantage of polymorphism when using pointers is not "philosophically required", and hence ease memory management.

Note: Thanks for CaptainObvlious for the contribution and his evolved code available here that I partially integrated. Not integrated are:

  • To ease the creation of derived implementations, you may want to create an intermediate templated class
  • You may prefer to use an abstract interface instead of my non-abstract base class

Ad hoc polymorphism and heterogeneous containers with value semantics

Different alternatives

It is possible. There are several alternative approaches to your problem. Each one has different advantages and drawbacks (I will explain each one):

  1. Create an interface and have a template class which implements this interface for different types. It should support cloning.
  2. Use boost::variant and visitation.

Blending static and dynamic polymorphism

For the first alternative you need to create an interface like this:

class UsableInterface 
{
public:
virtual ~UsableInterface() {}
virtual void use() = 0;
virtual std::unique_ptr<UsableInterface> clone() const = 0;
};

Obviously, you don't want to implement this interface by hand everytime you have a new type having the use() function. Therefore, let's have a template class which does that for you.

template <typename T> class UsableImpl : public UsableInterface
{
public:
template <typename ...Ts> UsableImpl( Ts&&...ts )
: t( std::forward<Ts>(ts)... ) {}
virtual void use() override { use( t ); }
virtual std::unique_ptr<UsableInterface> clone() const override
{
return std::make_unique<UsableImpl<T>>( t ); // This is C++14
// This is the C++11 way to do it:
// return std::unique_ptr<UsableImpl<T> >( new UsableImpl<T>(t) );
}

private:
T t;
};

Now you can actually already do everything you need with it. You can put these things in a vector:

std::vector<std::unique_ptr<UsableInterface>> usables;
// fill it

And you can copy that vector preserving the underlying types:

std::vector<std::unique_ptr<UsableInterface>> copies;
std::transform( begin(usables), end(usables), back_inserter(copies),
[]( const std::unique_ptr<UsableInterface> & p )
{ return p->clone(); } );

You probably don't want to litter your code with stuff like this. What you want to write is

copies = usables;

Well, you can get that convenience by wrapping the std::unique_ptr into a class which supports copying.

class Usable
{
public:
template <typename T> Usable( T t )
: p( std::make_unique<UsableImpl<T>>( std::move(t) ) ) {}
Usable( const Usable & other )
: p( other.clone() ) {}
Usable( Usable && other ) noexcept
: p( std::move(other.p) ) {}
void swap( Usable & other ) noexcept
{ p.swap(other.p); }
Usable & operator=( Usable other )
{ swap(other); }
void use()
{ p->use(); }
private:
std::unique_ptr<UsableInterface> p;
};

Because of the nice templated contructor you can now write stuff like

Usable u1 = 5;
Usable u2 = std::string("Hello usable!");

And you can assign values with proper value semantics:

u1 = u2;

And you can put Usables in an std::vector

std::vector<Usable> usables;
usables.emplace_back( std::string("Hello!") );
usables.emplace_back( 42 );

and copy that vector

const auto copies = usables;

You can find this idea in Sean Parents talk Value Semantics and Concepts-based Polymorphism. He also gave a very brief version of this talk at Going Native 2013, but I think this is to fast to follow.

Moreover, you can take a more generic approach than writing your own Usable class and forwarding all the member functions (if you want to add other later). The idea is to replace the class Usable with a template class. This template class will not provide a member function use() but an operator T&() and operator const T&() const. This gives you the same functionality, but you don't need to write an extra value class every time you facilitate this pattern.

A safe, generic, stack-based discriminated union container

The template class boost::variant is exactly that and provides something like a C style union but safe and with proper value semantics. The way to use it is this:

using Usable = boost::variant<int,std::string,A>;
Usable usable;

You can assign from objects of any of these types to a Usable.

usable = 1;
usable = "Hello variant!";
usable = A();

If all template types have value semantics, then boost::variant also has value semantics and can be put into STL containers. You can write a use() function for such an object by a pattern that is called the visitor pattern. It calls the correct use() function for the contained object depending on the internal type.

class UseVisitor : public boost::static_visitor<void>
{
public:
template <typename T>
void operator()( T && t )
{
use( std::forward<T>(t) );
}
}

void use( const Usable & u )
{
boost::apply_visitor( UseVisitor(), u );
}

Now you can write

Usable u = "Hello";
use( u );

And, as I already mentioned, you can put these thingies into STL containers.

std::vector<Usable> usables;
usables.emplace_back( 5 );
usables.emplace_back( "Hello world!" );
const auto copies = usables;

The trade-offs

You can grow the functionality in two dimensions:

  • Add new classes which satisfy the static interface.
  • Add new functions which the classes must implement.

In the first approach I presented it is easier to add new classes. The second approach makes it easier to add new functionality.

In the first approach it it impossible (or at least hard) for client code to add new functions. In the second approach it is impossible (or at least hard) for client code to add new classes to the mix. A way out is the so-called acyclic visitor pattern which makes it possible for clients to extend a class hierarchy with new classes and new functionality. The drawback here is that you have to sacrifice a certain amount of static checking at compile-time. Here's a link which describes the visitor pattern including the acyclic visitor pattern along with some other alternatives. If you have questions about this stuff, I'm willing to answer.

Both approaches are super type-safe. There is not trade-off to be made there.

The run-time-costs of the first approach can be much higher, since there is a heap allocation involved for each element you create. The boost::variant approach is stack based and therefore is probably faster. If performance is a problem with the first approach consider to switch to the second.

C++: Polymorphic container which can add several objects at once

There are two problems here, 1. How to decide what type each child will be, and 2. How to create multiple children.

To decide which child to create

This can be done at compile time or runtime. To do this at compile time you need templates.

template<class Child, class Arg1, class Arg2>
vector<unique_ptr<Parent>> CreateVec(Arg1&& arg1, Arg2&& arg2)
{
vector<unique_ptr<Parent>> result;
result.push_back(unique_ptr<Child>(
new Child(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2))));
return result;
}

called as follows CreateVec<MyChild>(myArg1, myArg2).

If you need to decide at runtime, you can use a map of factory functions indexed by a run-time variable. Or you could use a pointer to a factory object as your run-time variable.

To create multiple children

Here you have a choice between chained functions and variadic templates.

Chained functions

This is essentially what iostreams does. Have your function which creates a vector and adds a single child return an object which allows you to add a second child, and returns itself allowing you to continue.

The problem here is that you want the function to return the vector, so it can't also return another object. You could use a conversion operator to get the vector, or an explicit function, but probably easiest is to create the vector first, and just use the functions to add the children.

class AddChildren
{
vector<unique_ptr<Parent>>& m_vector;
public:
explicit AddChildren(vector<unique_ptr<Parent>>& theVector)
: m_vector(theVector) {}

template<class Child, class Arg1, class Arg2>
AddChildren& add(Arg1&& arg1, Arg2&& arg2)
{
m_vector.push_back(unique_ptr<Child>(
new Child(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2))));
return *this;
}
};

used as follows:

vector<unique_ptr<Parent>> myvector;
AddChildren(myvector)
.add<ChildA>(var1, var2)
.add<ChildB>(var3, var4)
.add<ChildC>(var5, var6);

If you are using a run-time method of choosing the type you can use operator() and have it look like this:

vector<unique_ptr<Parent>> myvector;
AddChildren(myvector)
(childAType, var1, var2)(childBType, var3, var4)(childCType, var5, var6);

(This can also be done with compile-time selection of type by using a dummy object of a specific type-selector type for each child type as a parameter.)

Using variadic templates

Use a variadic template to peel off three parameters at a time, and add a child object.

void addChildren(vector<unique_ptr<Parent>>& theVector)
{}

template<class FirstChild, class FirstArg1, class FirstArg2, class... Rest>
void addChildren(vector<unique_ptr<Parent>>& theVector,
FirstChild childtype, FirstArg1&& arg1, FirstArg2&& arg2, Rest&&... rest)
{
addChild(theVector, childtype,
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2));
addChildren(theVector, std::forward<Rest>(rest)...);
}

template<class... Args>
vector<unique_ptr<Parent>> CreateVec(Args&&... args)
{
vector<unique_ptr<Parent>> result;
addChildren(result, std::forward<Args>(args)...);
return result;

}

I assume here the existence of a function addChild which can add a child given its type (as a parameter) and its arguments.

The main problem with this is that VS2012 doesn't have variadic templates. There are two ways to simulate variadic templates. 1. Write a single function which takes the maximum number of parameters you might need, and defaults most of them to some known type which you can take to mean "not present". 2. Write out as many overloads as you think you will need.

If you know you will never need more than say ten child objects, the second option is actually perfectly feasible -- you only need to write them once and it's probably less than 150 lines of code. Alternatively you can use Boost.Preprocessor to generate the functions, but that's a whole new level of complexity.

Polymorphic value types and interfaces

A solution (tested) is to have a base class for the interfaces:

class AnyInterface {
virtual ~AnyInterface() {} // make it polymorphic
};

struct HasColor : public AnyInterface {
// ... same stuff
};

So then we have the following:

vector<AnyInterface*> ShapeValue::getInterfaces() { return _obj->getInterfaces(); }

Could then define a helper to grab the interface we want:

template<class I>
I* hasInterface(Shape& shape) {
for(auto interface : shape.getInterfaces()) {
if(auto p = dynamic_cast<I*>(interface)) {
return p;
}
}
return nullptr;
}

This way ShapeValue does not need to know about all the interface types.

c++ Object parameters: polymorphism, value semantics, object lifetimes?

The typical solution for your scenario would involve a resource-managing handler object which you do pass by value. Popular candidates are shared_ptr and unique_ptr:

#include <list>
#include <memory>
#include "all_zoo_animals.h" // yours

typedef std::shared_ptr<Animal> AnimalPtr; // see note
typedef std::list<AnimalPtr> AnimalCollection;

AnimalCollection zoo;

void addAnimal(AnimalPtr a)
{
zoo.push_back(a);
}

int main()
{
AnimalPtr a = AnimalPtr(new Penguin);
a.feed(fish);
addAnimal(a); // from local variable, see note

addAnimal(AnimalPtr(new Puffin)); // from temporary
}

If it is feasible, you could also define AnimalPtr as std::unique_ptr<Animal>, but then you have to say addAnimal(std::move(a));. This is more restrictive (as only one object handles the animal at any given time), but also lighter-weight.

Inheritance-free polymorphism

Your basic idea (do not require inheritance) is good. I would recommend using Adobe.Poly instead. When you use 1 std::function per single operation you have N sort-of virtual table (pointers), plus potentially N heap allocations (depending if SBO (Small Buffer Optimization) can be applied or not).

You are also very likely to get into object life-time management problems. In your implementation you assume that the real object lives longer than the "interface". Sooner or later you will get it wrong. This is why I would encourage a value-semantic approach. Adobe.Poly gives you that.

With Adobe.Poly you only get one vtable (pointer). It also implements SBO: potentially not a single allocation.

I would not necessarily go with Boost.TypeErasure. It requires learning another "language" for specifying interfaces, which exploits lots of meta programming, and as of today it does not implement SBO.

Adobe.Poly is not well documented. See this post for examples of how you use it. Also, see this paper on how it is implemented.



Related Topics



Leave a reply



Submit