I described in a previous post how difficult it is to work with dynamic dispatch in c++. I described various ways to deal with it. However I’ve been reading about an even more powerful and easy to use way to do things.

Lets say that I want the following properties of a variable for an object:

  • dynamic dispatch of its methods
  • memory safety

We can achieve combinations of those properties using the techniques I described last time: i.e. use of safe pointers (dynamic dispatch + memory safety), CRTP (memory safety and inheritance but dispatch done statically) or inclusion (memory safety but static dispatch and no inheritance).

However my eyes have been opened a bit after watching Sean Parent’s talk called “C++ seasoning”. Now I also want to achieve value semantics. Value semantics means that assigning a value to a different variable copies it (or, with std::move, moves it without copying). The opposite is reference semantics meaning that only the pointer is copied. With value semantics a value is guaranteed to be the sole way to access the corresponding area of memory.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

int main()
{
	int a = 1;
	int b = a; //value semantics...
	b += 1;    //a is unchanged
	
	std::cout << "a=" << a << ",b=" << b << std::endl;
	//prints: a=1,b=2

	int& ptrA = a;
	int& ptrB = ptrA; //reference semantics
	ptrB += 1;        //ptrA is changed
	
	std::cout << "a=" << a << ",ptrA=" << ptrA << ",ptrB=" << ptrB << std::endl;
	//prints: a=2,ptrA=2,ptrB=2
}

Using value semantics helps to make code easy to understand, since you don’t need to check what other variables (possibly on other threads) might be sharing the same data. There is no sharing.

Also you don’t have to worry about cleaning up pointers any longer, because there are none.

I said in my earlier post that in C++ polymorphism is only available via a pointer/reference variable. That is true, but there are a variety of techniques to maintain value semantics without losing polymorphism, and the purpose of this post is to describe those.

unique_ptr

The simplest way to get closer to value semantics with pointers is definitely std::unique_ptr. An area of memory is guaranteed to be uniquely owned.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <memory>

class Printer {
public:
	virtual void Print() const = 0;	
};

class ConcretePrinter : public Printer {
public:
	virtual void Print() const { std::cout << "ConcretePrinter" << std::endl; }		
};

int main()
{
	std::unique_ptr<Printer> cp(std::make_unique<ConcretePrinter>());
	cp->Print(); //prints: ConcretePrinter
	
	//std::unique_ptr<Printer> notACopy = cp; //doesn't work
	
	std::unique_ptr<Printer> notACopy2 = std::move(cp);
	notACopy2->Print(); //prints: ConcretePrinter
	
	cp->Print(); //dangerous, cp isn't there any more (segfault for me)
}

However we haven’t really achieved proper value semantics, because assigning a value doesn’t work and we have to move it around explicitly.

Let’s look for a better solution.

Hand rolled value semantics with polymorphism

Lets make a type with value semantics that can take anything with a Print() method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <memory>
#include <utility>
#include <vector>

class ConcretePrinter {
public:
     void Print() const { std::cout << "ConcretePrinter" << std::endl; }   
};

class StonePrinter {
public:
     void Print() const { std::cout << "StonePrinter" << std::endl; }     
};

class AnyPrinter {
public:
    template <typename T>
    AnyPrinter(T x) : self_(std::make_shared<model<T>>(std::move(x))) {}
 
    void Print() const
    { self_->Print_(); }
 
private:
    struct concept_t {
        virtual ~concept_t() = default;
        virtual void Print_() const = 0;
    };
    template <typename T>
    struct model : concept_t {
        model(T x) : data_(std::move(x)) {}
        void Print_() const override
        { data_.Print(); }
 
        T data_;
    };
   
    std::shared_ptr<const concept_t> self_;
};

int main()
{
    ConcretePrinter cp;
    
    StonePrinter sp;

    std::vector<AnyPrinter> printables;
    printables.push_back(cp); //pushing copies
    printables.push_back(sp);

    for(const auto& p : printables) {
        p.Print();
    }
}

There is no such thing as a free lunch though. Polymorphism is still achieved using a pointer, but it is hidden away so that it doesn’t contaminate client code.

We get value semantics. There is no inheritance in the printable objects. We can also make heterogeneous containers.

boost type_erasure

The only problem is that the code is rather long and laborious, every time we have a different concept, we would have to define 3 new classes for it.

Let’s check out boost::type_erasure to make it short and sweet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <boost/mpl/vector.hpp>
#include <boost/type_erasure/any.hpp>
#include <boost/type_erasure/member.hpp>
#include <iostream>
#include <vector>

namespace mpl = boost::mpl;
using namespace boost::type_erasure;

class ConcretePrinter {
public:
	 void Print() const { std::cout << "ConcretePrinter" << std::endl; }		
};

class StonePrinter {
public:
	 void Print() const { std::cout << "StonePrinter" << std::endl; }		
};

BOOST_TYPE_ERASURE_MEMBER((has_print), Print, 0)

typedef any<mpl::vector<
		copy_constructible<>, 
		has_print<void()>>> 
	AnyPrinter;

int main()
{
	ConcretePrinter cp;
	
	StonePrinter sp;

	std::vector<AnyPrinter> printables;
	printables.push_back(cp); //pushing copies
	printables.push_back(sp);

	for(auto& p : printables) {
		p.Print();
	}

	AnyPrinter copy = cp;
}

Conclusion

Normally in C++, polymorphism brings an unwelcome friend along with it - pointers. But I’ve described a couple of ways to get polymorphism without pointers being in view (at least in the client code).

References

Boost.TypeErasure

C++ Seasoning

C++ in a nutshell - O’Reilly

Inheritance is the Base Class of Evil code sample

Intrusive value semantic polymorphism example.

Value semantics

What is value semantics?

Minion AnyVarRef