These are the slides from my ACCU 2019 talk. You can find the recording here: https://www.youtube.com/watch?v=JGYxOieiZnY. All my other talks can be found here: https://train-it.eu/resources.
---
This short talk presents how easy it is to replace some cases of dynamic polymorphism with std::variant. During the lecture, we will analyze and compare a few implementations of the same simple Finite State Machine. It turns up that variant-based code is not only much faster but also it gives us the opportunity to define our interfaces and program flow much better. The talk will end up with the discussion of pros and cons of each approach and will try to give guidelines on when to use them.
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
Effective replacement of dynamic polymorphism with std::variant
1. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 1/108
Mateusz Pusz
April 12, 2019
E ective replacement of dynamic
polymorphism with
std::variant
2. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 2/108
Why?
3. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 3/108
• Time required to perform some action or to produce some result
• Measured in units of time like hours, minutes, seconds, nanoseconds or clock periods
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Why?
LATENCY
3
4. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 4/108
• Time required to perform some action or to produce some result
• Measured in units of time like hours, minutes, seconds, nanoseconds or clock periods
• In capital markets, the use of algorithmic trading to react to market events faster than the competition
to increase profitability of trades
• Many use cases where predictability of latency in message delivery is just as important, if not more
important than achieving a low average latency
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Why?
LATENCY
LOW LATENCY
3
5. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 5/108
• In Low Latency system we care a lot about
WCET (Worst Case Execution Time)
• In order to limit WCET we should limit the
usage of specific C++ language features
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
How NOT to develop software that have predictable
performance?
4
6. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 6/108
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Things to avoid on the fast path
5
7. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 7/108
• C++ tools that trade performance for usability (e.g. std::shared_ptr, std::function)
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Things to avoid on the fast path
5
8. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 8/108
• C++ tools that trade performance for usability (e.g. std::shared_ptr, std::function)
• Throwing exceptions on a likely code path
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Things to avoid on the fast path
5
9. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 9/108
• C++ tools that trade performance for usability (e.g. std::shared_ptr, std::function)
• Throwing exceptions on a likely code path
• Dynamic polymorphism
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Things to avoid on the fast path
5
10. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 10/108
• C++ tools that trade performance for usability (e.g. std::shared_ptr, std::function)
• Throwing exceptions on a likely code path
• Dynamic polymorphism
• Multiple inheritance
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Things to avoid on the fast path
5
11. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 11/108
• C++ tools that trade performance for usability (e.g. std::shared_ptr, std::function)
• Throwing exceptions on a likely code path
• Dynamic polymorphism
• Multiple inheritance
• RTTI
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Things to avoid on the fast path
5
12. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 12/108
• C++ tools that trade performance for usability (e.g. std::shared_ptr, std::function)
• Throwing exceptions on a likely code path
• Dynamic polymorphism
• Multiple inheritance
• RTTI
• Dynamic memory allocations
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Things to avoid on the fast path
5
13. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 13/108
• Dynamic polymorphism
• Dynamic memory allocations
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Things to avoid on the fast path
• C++ tools that trade performance for usability (e.g. std::shared_ptr, std::function)
• Throwing exceptions on a likely code path
• Multiple inheritance
• RTTI
6
14. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 14/108
How?
15. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 15/108
class base : noncopyable {
public:
virtual ~base() = default;
virtual void foo() = 0;
};
class x : public base {
public:
void foo() override;
};
class y : public base {
public:
void foo() override;
};
std::unique_ptr<base> b = std::make_unique<x>();
b->foo();
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
How?
DYNAMIC POLYMORPHISM
8
16. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 16/108
class base : noncopyable {
public:
virtual ~base() = default;
virtual void foo() = 0;
};
class x : public base {
public:
void foo() override;
};
class y : public base {
public:
void foo() override;
};
std::unique_ptr<base> b = std::make_unique<x>();
b->foo();
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
How?
DYNAMIC POLYMORPHISM
8
17. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 17/108
class base : noncopyable {
public:
virtual ~base() = default;
virtual void foo() = 0;
};
class x : public base {
public:
void foo() override;
};
class y : public base {
public:
void foo() override;
};
std::unique_ptr<base> b = std::make_unique<x>();
b->foo();
struct x { void foo(); };
struct y { void foo(); };
std::variant<x, y> b;
std::visit([](auto&& v){ v.foo(); }, b);
• Shorter
• Faster
• Value semantics
• Works on unrelated classes
• More flexible thanks to duck typing
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
How?
DYNAMIC POLYMORPHISM VARIANT
8
18. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 18/108
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
THE END
T H A N K Y O U !
9
19. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 19/108
Bonus slides
20. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 20/108
Abstract machine that can be in exactly one of a finite number of
states at any given time.
-- Wikipedia
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Finite State Machine
11
21. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 21/108
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Finite State Machine
12
22. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 22/108
• State changes to another in a response to
some external inputs called events
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Finite State Machine
12
23. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 23/108
• State changes to another in a response to
some external inputs called events
• The change from one state to another is
called a transition
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Finite State Machine
12
24. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 24/108
• State changes to another in a response to
some external inputs called events
• The change from one state to another is
called a transition
• A FSM is defined by
– a list of states
– its initial state
– the conditions for each transition
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Finite State Machine
12
25. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 25/108
Single dynamic dispatch
CASE #1
26. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 26/108
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Class diagram
14
27. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 27/108
template<typename Event>
class state : noncopyable {
public:
virtual ~state() = default;
virtual std::unique_ptr<state> on_event(Event) = 0;
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Single dynamic dispatch
15
28. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 28/108
template<typename Event>
class state : noncopyable {
public:
virtual ~state() = default;
virtual std::unique_ptr<state> on_event(Event) = 0;
};
template<typename Event>
class fsm {
std::unique_ptr<state<Event>> state_;
public:
explicit fsm(std::unique_ptr<state<Event>> state) : state_(std::move(state)) {}
void dispatch(Event e)
{
auto new_state = state_->on_event(e);
if(new_state)
state_ = std::move(new_state);
}
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Single dynamic dispatch
15
29. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 29/108
template<typename Event>
class state : noncopyable {
public:
virtual ~state() = default;
virtual std::unique_ptr<state> on_event(Event) = 0;
};
template<typename Event>
class fsm {
std::unique_ptr<state<Event>> state_;
public:
explicit fsm(std::unique_ptr<state<Event>> state) : state_(std::move(state)) {}
void dispatch(Event e)
{
auto new_state = state_->on_event(e);
if(new_state)
state_ = std::move(new_state);
}
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Single dynamic dispatch
16
30. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 30/108
class connection_fsm : public fsm<event> {
public:
connection_fsm():
fsm<event>(std::make_unique<state_idle>()) {}
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Connection FSM
17
31. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 31/108
class connection_fsm : public fsm<event> {
public:
connection_fsm():
fsm<event>(std::make_unique<state_idle>()) {}
};
using s = state<event>;
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Connection FSM
17
32. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 32/108
class connection_fsm : public fsm<event> {
public:
connection_fsm():
fsm<event>(std::make_unique<state_idle>()) {}
};
using s = state<event>;
class state_idle final : public s {
public:
std::unique_ptr<s> on_event(event e) override;
};
class state_connecting final : public s {
static constexpr int n_max = 3;
int n = 0;
public:
std::unique_ptr<s> on_event(event e) override;
};
class state_connected final : public s {
public:
std::unique_ptr<s> on_event(event e) override;
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Connection FSM
17
33. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 33/108
std::unique_ptr<s> state_idle::on_event(event e)
{
}
std::unique_ptr<s> state_connecting::on_event(event e)
{
}
std::unique_ptr<s> state_connected::on_event(event e)
{
}
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Transitions
18
34. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 34/108
std::unique_ptr<s> state_idle::on_event(event e)
{
if(e == event::connect)
return std::make_unique<state_connecting>();
return nullptr;
}
std::unique_ptr<s> state_connecting::on_event(event e)
{
}
std::unique_ptr<s> state_connected::on_event(event e)
{
}
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Transitions
18
35. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 35/108
std::unique_ptr<s> state_idle::on_event(event e)
{
if(e == event::connect)
return std::make_unique<state_connecting>();
return nullptr;
}
std::unique_ptr<s> state_connecting::on_event(event e)
{
switch(e) {
case event::connected:
return std::make_unique<state_connected>();
case event::timeout:
return ++n < n_max ?
nullptr : std::make_unique<state_idle>();
default:
return nullptr;
}
}
std::unique_ptr<s> state_connected::on_event(event e)
{
}
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Transitions
18
37. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 37/108
std::unique_ptr<s> state_idle::on_event(event e)
{
if(e == event::connect)
return std::make_unique<state_connecting>();
return nullptr;
}
std::unique_ptr<s> state_connecting::on_event(event e)
{
switch(e) {
case event::connected:
return std::make_unique<state_connected>();
case event::timeout:
return ++n < n_max ?
nullptr : std::make_unique<state_idle>();
default:
return nullptr;
}
}
std::unique_ptr<s> state_connected::on_event(event e)
{
if(e == event::disconnect)
return std::make_unique<state_idle>();
return nullptr;
}
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
The slow part
19
38. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 38/108
• Fold expressions come handy ;-)
template<typename Fsm, typename... Events>
void dispatch(Fsm& fsm, Events... events)
{
(fsm.dispatch(events), ...);
}
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Testing transitions
20
39. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 39/108
• Fold expressions come handy ;-)
template<typename Fsm, typename... Events>
void dispatch(Fsm& fsm, Events... events)
{
(fsm.dispatch(events), ...);
}
• Simple message flow
connection_fsm fsm;
dispatch(fsm, event::connect, event::timeout, event::connected, event::disconnect);
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Testing transitions
20
40. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 40/108
• Open to new alternatives
– new derived types may be added by clients at any point of time (long a er base class
implementation is finished)
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Single dynamic dispatch
21
41. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 41/108
• Open to new alternatives
– new derived types may be added by clients at any point of time (long a er base class
implementation is finished)
• Closed to new operations
– clients cannot add new operations to dynamic dispatch
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Single dynamic dispatch
21
42. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 42/108
• Open to new alternatives
– new derived types may be added by clients at any point of time (long a er base class
implementation is finished)
• Closed to new operations
– clients cannot add new operations to dynamic dispatch
• Multi-level
– many levels of inheritance possible
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Single dynamic dispatch
21
43. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 43/108
• Open to new alternatives
– new derived types may be added by clients at any point of time (long a er base class
implementation is finished)
• Closed to new operations
– clients cannot add new operations to dynamic dispatch
• Multi-level
– many levels of inheritance possible
• Object Oriented
– whole framework is based on objects
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Single dynamic dispatch
21
44. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 44/108
Double dynamic dispatch
CASE #2
45. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 45/108
class event : noncopyable {
public:
virtual ~event() = default;
};
class event_connect final : public event {
std::string_view address_;
public:
explicit event_connect(std::string_view address): address_(address) {}
std::string_view address() const { return address_; }
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
What if we want our events to pass data?
23
46. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 46/108
class event : noncopyable {
public:
virtual ~event() = default;
};
class event_connect final : public event {
std::string_view address_;
public:
explicit event_connect(std::string_view address): address_(address) {}
std::string_view address() const { return address_; }
};
std::unique_ptr<state> state_idle::on_event(const event& e)
{
if(auto ptr = dynamic_cast<const event_connect*>(&e)) { /* ... */ }
else { /* ... */ }
}
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
What if we want our events to pass data?
23
47. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 47/108
class event : noncopyable {
public:
virtual ~event() = default;
};
class event_connect final : public event {
std::string_view address_;
public:
explicit event_connect(std::string_view address): address_(address) {}
std::string_view address() const { return address_; }
};
std::unique_ptr<state> state_idle::on_event(const event& e)
{
if(auto ptr = dynamic_cast<const event_connect*>(&e)) { /* ... */ }
else { /* ... */ }
}
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
What if we want our events to pass data?
A really bad idea :-(
23
48. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 48/108
Special form of multiple dispatch, and a mechanism that
dispatches a function call to di erent concrete functions
depending on the runtime types of two objects involved in the call
-- Wikipedia
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Double dispatch (aka Visitor Pattern)
24
49. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 49/108
template<typename State>
struct event : private noncopyable {
virtual ~event() = default;
virtual std::unique_ptr<State> dispatch(State& s) const = 0;
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Double dispatch (aka Visitor Pattern)
25
53. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 53/108
template<typename State>
struct event : private noncopyable {
virtual ~event() = default;
virtual std::unique_ptr<State> dispatch(State& s) const = 0;
};
class state : noncopyable {
public:
virtual ~state() = default;
virtual std::unique_ptr<state> on_event(const event_connect&) { return nullptr; }
virtual std::unique_ptr<state> on_event(const event_connected&) { return nullptr; }
virtual std::unique_ptr<state> on_event(const event_disconnect&) { return nullptr; }
virtual std::unique_ptr<state> on_event(const event_timeout&) { return nullptr; }
};
class state_idle final : public state {
public:
using state::on_event;
std::unique_ptr<state> on_event(const event_connect& e) override
{
return std::make_unique<state_connecting>(std::string(e.address()));
}
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Double dispatch (aka Visitor Pattern)
27
54. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 54/108
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Class diagram
28
55. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 55/108
template<typename State>
struct event : private noncopyable {
virtual ~event() = default;
virtual std::unique_ptr<State> dispatch(State& s) const = 0;
};
struct event_connect final : public event<state> {
std::unique_ptr<state> dispatch(state& s) const override { return s.on_event(*this); }
// ...
};
struct event_connected final : public event<state> {
std::unique_ptr<state> dispatch(state& s) const override { return s.on_event(*this); }
};
struct event_disconnect final : public event<state> {
std::unique_ptr<state> dispatch(state& s) const override { return s.on_event(*this); }
};
struct event_timeout final : public event<state> {
std::unique_ptr<state> dispatch(state& s) const override { return s.on_event(*this); }
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Curiously Recurring Template Pattern (CRTP)
29
64. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 64/108
std::unique_ptr<state>
state_idle::on_event(const event_connect& e)
{
return std::make_unique<state_connecting>(
std::string{e.address()});
}
std::unique_ptr<state>
state_connecting::on_event(const event_connected&)
{
return std::make_unique<state_connected>();
}
std::unique_ptr<state>
state_connecting::on_event(const event_timeout&)
{
return ++n < n_max ?
nullptr : std::make_unique<state_idle>();
}
std::unique_ptr<state>
state_connected::on_event(const event_disconnect&)
{
return std::make_unique<state_idle>();
}
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
The slow part
33
65. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 65/108
template<typename Fsm, typename... Events>
void dispatch(Fsm& fsm, const Events&... events)
{
(fsm.dispatch(*events), ...);
}
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Testing transitions
34
66. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 66/108
template<typename Fsm, typename... Events>
void dispatch(Fsm& fsm, const Events&... events)
{
(fsm.dispatch(*events), ...);
}
dispatch(fsm,
std::make_unique<event_connect>("train-it.eu"),
std::make_unique<event_timeout>(),
std::make_unique<event_connected>(),
std::make_unique<event_disconnect>());
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Testing transitions
34
67. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 67/108
template<typename Fsm, typename... Events>
void dispatch(Fsm& fsm, const Events&... events)
{
(fsm.dispatch(*events), ...);
}
dispatch(fsm,
std::make_unique<event_connect>("train-it.eu"),
std::make_unique<event_timeout>(),
std::make_unique<event_connected>(),
std::make_unique<event_disconnect>());
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
The slow part
35
68. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 68/108
• Open to new alternatives
• Closed to new alternatives
– one of class hierarchies fixed at design time and cannot be extended by clients
• Closed to new operations
– clients cannot add new operations to dynamic dispatch
• Multi-level
– many levels of inheritance possible
• Object Oriented
– whole framework is based on objects
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Double dynamic dispatch (aka Visitor Pattern)
36
69. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 69/108
Variant + external transitions
CASE #3
70. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 70/108
struct event_connect { std::string_view address; };
struct event_connected {};
struct event_disconnect {};
struct event_timeout {};
using event = std::variant<event_connect, event_connected, event_disconnect, event_timeout>;
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Events and states
EVENTS
38
71. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 71/108
struct event_connect { std::string_view address; };
struct event_connected {};
struct event_disconnect {};
struct event_timeout {};
using event = std::variant<event_connect, event_connected, event_disconnect, event_timeout>;
struct state_idle {};
struct state_connecting {
static constexpr int n_max = 3;
int n = 0;
std::string address;
};
struct state_connected {};
using state = std::variant<state_idle, state_connecting, state_connected>;
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Events and states
EVENTS
STATES
38
78. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 78/108
template<typename StateVariant, typename EventVariant, typename Transitions>
class fsm {
StateVariant state_;
public:
void dispatch(const EventVariant& event)
{
std::optional<StateVariant> new_state = std::visit(Transitions{}, state_, event);
if(new_state)
state_ = *std::move(new_state);
}
};
using connection_fsm = fsm<state, event, transitions>;
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
FSM engine
40
79. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 79/108
template<typename StateVariant, typename EventVariant, typename Transitions>
class fsm {
StateVariant state_;
public:
void dispatch(const EventVariant& event)
{
std::optional<StateVariant> new_state = std::visit(Transitions{}, state_, event);
if(new_state)
state_ = *std::move(new_state);
}
};
using connection_fsm = fsm<state, event, transitions>;
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
FSM engine
41
80. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 80/108
template<typename StateVariant, typename EventVariant, typename Transitions>
class fsm {
StateVariant state_;
public:
void dispatch(const EventVariant& event)
{
std::optional<StateVariant> new_state = std::visit(Transitions{}, state_, event);
if(new_state)
state_ = *std::move(new_state);
}
};
using connection_fsm = fsm<state, event, transitions>;
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
FSM engine
42
81. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 81/108
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Class diagram
43
82. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 82/108
template<typename Fsm, typename... Events>
void dispatch(Fsm& fsm, Events&&... events)
{
(fsm.dispatch(std::forward<Events>(events)), ...);
}
dispatch(fsm,
event_connect{"train-it.eu"},
event_timeout{},
event_connected{},
event_disconnect{});
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Testing transitions
44
83. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 83/108
Variant + transitions in FSM
CASE #4
84. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 84/108
template<typename Derived, typename StateVariant, typename EventVariant>
class fsm {
StateVariant state_;
public:
void dispatch(const EventVariant& event)
{
auto new_state = std::visit(
state_, event);
if(new_state)
state_ = *std::move(new_state);
}
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
FSM engine
46
85. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 85/108
template<typename Derived, typename StateVariant, typename EventVariant>
class fsm {
StateVariant state_;
public:
void dispatch(const EventVariant& event)
{
Derived& child = static_cast<Derived&>(*this);
auto new_state = std::visit(
[&](auto& s, const auto& e) -> std::optional<StateVariant>
{ return child.on_event(s, e); },
state_, event);
if(new_state)
state_ = *std::move(new_state);
}
};
• CRTP again ;-)
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
FSM engine
46
86. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 86/108
template<typename Derived, typename StateVariant>
class fsm {
StateVariant state_;
public:
template<typename Event>
void dispatch(Event&& event)
{
Derived& child = static_cast<Derived&>(*this);
auto new_state = std::visit(
[&](auto& s) -> std::optional<StateVariant>
{ return child.on_event(s, std::forward<Event>(event)); },
state_);
if(new_state)
state_ = *std::move(new_state);
}
};
• Visitation on a StateVariant only
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
FSM engine: dispatch() as an overload set
47
87. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 87/108
class connection_fsm
: public fsm<connection_fsm, state> {
public:
auto on_event(state_idle&,
const event_connect& e)
{ }
auto on_event(state_connecting&,
const event_connected&)
{ }
auto on_event(state_connecting& s,
const event_timeout&)
{
}
auto on_event(state_connected&,
const event_disconnect&)
{ }
template<typename State, typename Event>
auto on_event(State&, const Event&)
{ }
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Transitions de ned by the FSM itself
48
88. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 88/108
class connection_fsm
: public fsm<connection_fsm, state> {
public:
auto on_event(state_idle&,
const event_connect& e)
{ return state_connecting{std::string(e.address)}; }
auto on_event(state_connecting&,
const event_connected&)
{ return state_connected{}; }
auto on_event(state_connecting& s,
const event_timeout&)
{
return ++s.n < state_connecting::n_max ?
std::nullopt : std::optional<state>(state_idle{});
}
auto on_event(state_connected&,
const event_disconnect&)
{ return state_idle{}; }
template<typename State, typename Event>
auto on_event(State&, const Event&)
{ return std::nullopt; }
};
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Transitions de ned by the FSM itself
48
89. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 89/108
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
Class diagram
49
90. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 90/108
What about performance?
91. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 91/108
CLOSED(Start)
LISTEN/-
CLOSE/-
LISTEN
SYN
RECEIVED
SYN
SENT
CONNECT/ (Step 1 of the 3-way-handshake)SYN
SYN/SYN+ACK(Step 2 of the 3-way-handshake)
unusual event
client/receiver path
server/sender path
RST/-
SYN/SYN+ACK (simultaneous open)
SYN+ACK/ACK
(Step 3 of the 3-way-handshake)
Data exchange occurs
ESTABLISHED
FIN/ACK
ACK/-
CLOSE/-
SEND/SYN
CLOSE/FIN
CLOSE/FIN
CLOSING
TIME WAIT
CLOSED
FIN WAIT 1
FIN WAIT 2
CLOSE WAIT
LAST ACK
CLOSE/FIN
FIN/ACK
FIN+ACK/ACK
ACK/-
FIN/ACK
Timeout
(Go back to start)
Active CLOSE Passive CLOSE
ACK/-
ACK/-
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
TCP state diagram
51
92. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 92/108
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
TCP FSM Performance
52
93. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 93/108
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
TCP FSM Performance
52
94. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 94/108
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
TCP FSM Performance
52
95. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 95/108
Summary
96. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 96/108
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
std::variant<Types...> vs inheritance
INHERITANCE VARIANT
54
97. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 97/108
• Open/Closed to new alternatives • Closed to new alternatives
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
std::variant<Types...> vs inheritance
INHERITANCE VARIANT
54
98. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 98/108
• Open/Closed to new alternatives
• Closed to new operations
• Closed to new alternatives
• Open to new operations
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
std::variant<Types...> vs inheritance
INHERITANCE VARIANT
54
99. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 99/108
• Open/Closed to new alternatives
• Closed to new operations
• Multi-level
• Closed to new alternatives
• Open to new operations
• Single level
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
std::variant<Types...> vs inheritance
INHERITANCE VARIANT
54
100. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 100/108
• Open/Closed to new alternatives
• Closed to new operations
• Multi-level
• OO
• Closed to new alternatives
• Open to new operations
• Single level
• Functional
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
std::variant<Types...> vs inheritance
INHERITANCE VARIANT
54
101. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 101/108
• Open/Closed to new alternatives
• Closed to new operations
• Multi-level
• OO
• Pointer semantics
• Closed to new alternatives
• Open to new operations
• Single level
• Functional
• Value semantics
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
std::variant<Types...> vs inheritance
INHERITANCE VARIANT
54
102. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 102/108
• Open/Closed to new alternatives
• Closed to new operations
• Multi-level
• OO
• Pointer semantics
• Design forced by the implementation details
• Closed to new alternatives
• Open to new operations
• Single level
• Functional
• Value semantics
• Many design choices possible
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
std::variant<Types...> vs inheritance
INHERITANCE VARIANT
54
103. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 103/108
• Open/Closed to new alternatives
• Closed to new operations
• Multi-level
• OO
• Pointer semantics
• Design forced by the implementation details
• Forces dynamic memory allocations
• Closed to new alternatives
• Open to new operations
• Single level
• Functional
• Value semantics
• Many design choices possible
• No dynamic memory allocations
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
std::variant<Types...> vs inheritance
INHERITANCE VARIANT
54
104. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 104/108
• Open/Closed to new alternatives
• Closed to new operations
• Multi-level
• OO
• Pointer semantics
• Design forced by the implementation details
• Forces dynamic memory allocations
• Strict interfaces
• Closed to new alternatives
• Open to new operations
• Single level
• Functional
• Value semantics
• Many design choices possible
• No dynamic memory allocations
• Duck typing
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
std::variant<Types...> vs inheritance
INHERITANCE VARIANT
54
105. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 105/108
• Open/Closed to new alternatives
• Closed to new operations
• Multi-level
• OO
• Pointer semantics
• Design forced by the implementation details
• Forces dynamic memory allocations
• Strict interfaces
• Complex
• Closed to new alternatives
• Open to new operations
• Single level
• Functional
• Value semantics
• Many design choices possible
• No dynamic memory allocations
• Duck typing
• Simple
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
std::variant<Types...> vs inheritance
INHERITANCE VARIANT
54
106. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 106/108
• Open/Closed to new alternatives
• Closed to new operations
• Multi-level
• OO
• Pointer semantics
• Design forced by the implementation details
• Forces dynamic memory allocations
• Strict interfaces
• Complex
• Slower
• Closed to new alternatives
• Open to new operations
• Single level
• Functional
• Value semantics
• Many design choices possible
• No dynamic memory allocations
• Duck typing
• Simple
• Faster
ACCU 2019 | E ective replacement of dynamic polymorphism with std::variant
std::variant<Types...> vs inheritance
INHERITANCE VARIANT
54
107. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 107/108
108. 12.04.2019 Effective replacement of dynamic polymorphism with std::variant
file:///C:/repos/cppTrainings/build/out/polymorphism_with_variant/polymorphism_with_variant.html#108 108/108