SlideShare a Scribd company logo
New Tools for a More
Functional C++
Sumant Tambe
Sr. Software Engineer, LinkedIn
Microsoft MVP
SF Bay Area ACCU
Sept 28, 2017
Blogger (since 2005)
Coditation—Elegant Code for Big Data
Author (wikibook) Open-source contributor
Since 2013 (Visual Studio and Dev Tech)
Functional Programming
in C++
by Ivan Cukic
(ETA early 2018)
Reviewer
Sum Types and (pseudo) Pattern Matching
Modeling Alternatives
Inheritance vs std::variant
• States of a game of Tennis
• NormalScore
• DeuceScore
• AdvantageScore
• GameCompleteScore
Modeling game states using std::variant
struct NormalScore {
Player p1, p2;
int p1_score, p2_score;
};
struct DeuceScore {
Player p1, p2;
};
struct AdvantageScore {
Player lead, lagging;
};
struct GameCompleteScore {
Player winner, loser;
int loser_score;
};
using GameState = std::variant<NormalScore, DeuceScore,
AdvantageScore, GameCompleteScore>;
Print GameState (std::variant)
struct GameStatePrinter {
std::ostream &o;
explicit GameStatePrinter(std::ostream& out) : o(out) {}
void operator ()(const NormalScore& ns) const {
o << "NormalScore[" << ns.p1 << ns.p2 << ns.p1_score << ns.p2_score << "]";
}
void operator ()(const DeuceScore& ds) const {
o << "DeuceScore[" << ds.p1 << "," << ds.p2 << "]";
}
void operator ()(const AdvantageScore& as) const {
o << "AdvantageScore[" << as.lead << "," << as.lagging << "]";
}
void operator ()(const GameCompleteScore& gc) const {
o << "GameComplete[" << gc.winner << gc.loser << gc.loser_score << "]";
}
};
std::ostream & operator << (std::ostream& o, const GameState& game) {
std::visit(GameStatePrinter(o), game);
return o;
}
std::visit spews blood when you miss a case
Print GameState. Fancier!
std::ostream & operator << (std::ostream& o, const GameState& game) {
std::visit(overloaded {
[&](const NormalScore& ns) {
o << "NormalScore" << ns.p1 << ns.p2 << ns.p1_score << ns.p2_score;
},
[&](const DeuceScore& gc) {
o << "DeuceScore[" << ds.p1 << "," << ds.p2 << "]";
},
[&](const AdvantageScore& as) {
o << "AdvantageScore[" << as.lead << "," << as.lagging << "]";
},
[&](const GameCompleteScore& gc) {
o << "GameComplete[" << gc.winner << gc.loser << gc.loser_score << "]";
}
}, game);
return o;
}
Passing Two Variants to std::visit
std::ostream & operator << (std::ostream& o, const GameState& game) {
std::visit(overloaded {
[&](const NormalScore& ns, const auto& other) {
o << "NormalScore" << ns.p1 << ns.p2 << ns.p1_score << ns.p2_score;
},
[&](const DeuceScore& gc, const auto& other) {
o << "DeuceScore[" << ds.p1 << "," << ds.p2 << "]";
},
[&](const AdvantageScore& as, const auto& other) {
o << "AdvantageScore[" << as.lead << "," << as.lagging << "]";
},
[&](const GameCompleteScore& gc, const auto& other) {
o << "GameComplete[" << gc.winner << gc.loser << gc.loser_score << "]";
}
}, game, someother_variant);
return o;
}
There can be arbitrary number of arbitrary variant types.
The visitor must cover all cases
A Template to Inherit Lambdas
template <class... Ts>
struct overloaded : Ts... {
using Ts::operator()...;
explicit overloaded(Ts... ts) : Ts(ts)... {}
};
A User-Defined Deduction Guide
explicit overloaded(Ts... ts) : Ts(ts)... {}
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
Next GameState Algorithm (all cases in one place)
GameState next (const GameState& now,
const Player& who_scored)
{
return std::visit(overloaded {
[&](const DeuceScore& ds) -> GameState {
if (ds.p1 == who_scored)
return AdvantageScore{ds.p1, ds.p2};
else
return AdvantageScore{ds.p2, ds.p1};
},
[&](const AdvantageScore& as) -> GameState {
if (as.lead == who_scored)
return GameCompleteScore{as.lead, as.lagging, 40};
else
return DeuceScore{as.lead, as.lagging};
},
[&](const GameCompleteScore &) -> GameState {
throw "Illegal State";
},
[&](const NormalScore& ns) -> GameState {
if (ns.p1 == who_scored) {
switch (ns.p1_score) {
case 0: return NormalScore{ns.p1, ns.p2, 15, ns.p2_score};
case 15: return NormalScore{ns.p1, ns.p2, 30, ns.p2_score};
case 30: if (ns.p2_score < 40)
return NormalScore{ns.p1, ns.p2, 40, ns.p2_score};
else
return DeuceScore{ns.p1, ns.p2};
case 40: return GameCompleteScore{ns.p1, ns.p2, ns.p2_score};
default: throw "Makes no sense!";
}
}
else {
switch (ns.p2_score) {
case 0: return NormalScore{ns.p1, ns.p2, ns.p1_score, 15};
case 15: return NormalScore{ns.p1, ns.p2, ns.p1_score, 30};
case 30: if (ns.p1_score < 40)
return NormalScore{ns.p1, ns.p2, ns.p1_score, 40};
else
return DeuceScore{ns.p1, ns.p2};
case 40: return GameCompleteScore{ns.p2, ns.p1, ns.p1_score};
default: throw "Makes no sense!";
}
}
}
}, now);
}
Modeling Alternatives with Inheritance
class GameState {
std::unique_ptr<GameStateImpl> _state;
public:
void next(const Player& who_scored) {}
};
class GameStateImpl {
Player p1, p2;
public:
virtual GameStateImpl * next(
const Player& who_scored) = 0;
virtual ~GameStateImpl(){}
};
class NormalScore : public GameStateImpl {
int p1_score, p2_score;
public:
GameStateImpl * next(const Player&);
};
class DeuceScore : public GameStateImpl {
public:
GameStateImpl * next(const Player&);
};
class AdvantageScore : public GameStateImpl {
int lead;
public:
GameStateImpl * next(const Player&);
};
class GameCompleteScore :public GameStateImpl{
int winner, loser_score;
public:
GameStateImpl * next(const Player&);
};
Sharing State is easier with Inheritance
class GameState {
std::unique_ptr<GameStateImpl> _state;
public:
void next(const Player& who_scored) {}
Player& who_is_serving() const;
double fastest_serve_speed() const;
GameState get_last_state() const;
};
class GameStateImpl {
Player p1, p2;
int serving_player;
double speed;
GameState last_state;
public:
virtual GameStateImpl * next(
const Player& who_scored) = 0;
virtual Player& who_is_serving() const;
virtual double fastest_serve_speed() const;
virtual GameState get_last_state() const;
};
Sharing Common State is Repetitive with std::variant
Player who_is_serving = std::visit([](auto& s) {
return s.who_is_serving();
}, state);
Player who_is_serving = state.who_is_serving();
ceremony!
struct NormalScore {
Player p1, p2;
int p1_score, p2_score;
int serving_player;
Player & who_is_serving();
};
struct DeuceScore {
Player p1, p2;
int serving_player;
Player & who_is_serving();
};
struct AdvantageScore {
Player lead, lagging;
int serving_player;
Player & who_is_serving();
};
struct GameCompleteScore {
Player winner, loser;
int loser_score;
int serving_player;
Player & who_is_serving();
};
How about recursive std::variant?
struct NormalScore {
Player p1, p2;
int p1_score, p2_score;
int serving_player;
Player & who_is_serving();
GameState last_state;
};
struct DeuceScore {
Player p1, p2;
int serving_player;
Player & who_is_serving();
GameState last_state;
};
struct AdvantageScore {
Player lead, lagging;
int serving_player;
Player & who_is_serving();
GameState last_state;
};
struct GameCompleteScore {
Player winner, loser;
int loser_score;
int serving_player;
Player & who_is_serving();
GameState last_state;
};
Not possible unless you use recursive_wrapper and dynamic allocation.
Not in C++17.
Dare I say, it’s not algebraic? It does not compose 
std::variant is a container. Not an abstraction.
std::variant disables fluent interfaces
{
using GameState = std::variant<NormalScore, DeuceScore,
AdvantageScore, GameCompleteScore>;
GameState state = NormalScore {..};
GameState last_state = std::visit([](auto& s) {
return s.get_last_state();
}, state);
double last_speed = std::visit([](auto& s) {
return state.fastest_serve_speed();
}, last_state);
double last_speed = state.get_last_state().fastest_serve_speed();
}
ceremony!
Combine Implementation Inheritance with
std::variant
{
using GameState = std::variant<NormalScore_v2, DeuceScore_v2,
AdvantageScore_v2, GameCompleteScore_v2>;
GameState state = NormalScore_v2 {..};
Player who_is_serving = std::visit([](SharedGameState& s) {
return s.who_is_serving();
}, state);
Player who_is_serving = state.who_is_serving();
}
SharedGameState
who_is_serving()
NormalScore_v2 DeuceScore_v2 AdvantageScore_v2 GameCompleteScore_v2
ceremony!
Modeling Alternatives
Inheritance std::variant
Dynamic Allocation No dynamic allocation
Intrusive Non-intrusive
Reference semantics (how will you copy a
vector?)
Value semantics
Algorithm scattered into classes Algorithm in one place
Language supported
Clear errors if pure-virtual is not implemented
Library supported
std::visit spews blood on missing cases
Creates a first-class abstraction It’s just a container
Keeps fluent interfaces Disables fluent interfaces. Repeated std::visit
Supports recursive types (Composite) Must use recursive_wrapper and dynamic
allocation. Not in the C++17 standard.
Deep Immutability
const is shallow
struct X {
void bar();
};
struct Y {
X* xptr;
explicit Y(X* x) : xptr(x) {}
void foo() const {
xptr->bar();
}
};
{
const Y y(new X);
y.foo(); // mutates X??
}
Deep Immutability: propagate_const<T>
struct X {
void bar();
void bar() const; // Compiler error without this function
};
struct Y {
X* xptr;
propagate_const<X *> xptr;
explicit Y(X* x) : xptr(x) {}
void foo() const {
xptr->bar();
}
};
{
const Y y(new X);
y.foo(); // calls X.bar() const
}
Deep Immutability: propagate_const<T>
#include <experimental/propagate_const>
using std::experimental::propagate_const;
{
propagate_const<X *> xptr;
propagate_const<std::unique_ptr<X>> uptr;
propagate_const<std::shared_ptr<X>> shptr;
const propagate_const<std::shared_ptr<X>> c_shptr;
shptr.get() === X*
c_shptr.get() === const X*
*shptr === X&
*c_shptr === const X&
get_underlying(shptr) === shared_ptr<X>
get_underlying(c_shptr) === const shared_ptr<X>
shptr = another_shptr; // Error. Not copy-assignable
shptr = std::move(another_shptr) // but movable
Library fundamental TS v2
Mutable Temporaries
The Named Parameter Idiom (mutable)
class configs {
std::string server;
std::string protocol;
public:
configs & set_server(const std::string& s);
configs & set_protocol(const std::string& s);
};
start_server(configs().set_server(“localhost”)
.set_protocol(“https”));
The Named Parameter Idiom (immutable)
class configs {
public:
configs set_server(const std::string& s) const {
configs temp(*this); temp.server = s; return temp;
}
configs set_protocol(const std::string& proto) const {
configs temp(*this); temp.protocol = proto; return temp;
}
};
start_server(configs().set_server(“localhost”)
.set_protocol(“https”));
Avoid
copy-constructors?
The Named Parameter Idiom (immutable*)
class configs {
public:
configs set_server(const std::string& s) const {
configs temp(*this); temp.server = s; return temp;
}
configs set_protocol(const std::string& proto) const {
configs temp(*this); temp.protocol = proto; return temp;
}
configs set_server(const std::string& s) && {
server = s; return *this;
}
configs set_protocol(const std::string& proto) && {
protocol = proto; return *this;
}
};
start_server(configs().set_server(“localhost”)
.set_protocol(“https”));
&
&
The Named Parameter Idiom (immutable*)
class configs {
public:
configs set_server(const std::string& s) const {
configs temp(*this); temp.server = s; return temp;
}
configs set_protocol(const std::string& proto) const {
configs temp(*this); temp.protocol = proto; return temp;
}
configs&& set_server(const std::string& s) && {
server = s; return *this; std::move(*this);
}
configs&& set_protocol(const std::string& proto) && {
protocol = proto; return *this; std::move(*this);
}
};
start_server(configs().set_server(“localhost”)
.set_protocol(“https”));
&
&
Thank You!

More Related Content

PPTX
Approach to myopathy
PDF
4 Steps simple approach myopathy
PPTX
Wireless Power Transmission(Future is Here)
PPT
8051 ch9-950217
PPTX
Star topology ppt
PPTX
Magnetron
PPT
Networking basics PPT
PPTX
ZigBee Technology
Approach to myopathy
4 Steps simple approach myopathy
Wireless Power Transmission(Future is Here)
8051 ch9-950217
Star topology ppt
Magnetron
Networking basics PPT
ZigBee Technology

What's hot (20)

PDF
DETECTION OF FAULT LOCATION IN TRANSMISSION LINE USING INTERNET OF THINGS (IOT)
PPTX
Evaluation and investigation of Neuromuscular disorders
PDF
info diodos tvs.pdf
PPTX
Wireless Power Transmission through TESLA COILS
PPT
powerline communication system
PPTX
Spinal Cord Injury
PPTX
Fundamentals of Nerve conduction studies and its Interpretations
PPTX
Occupational Therapy Evaluations
PPTX
IP classes
PPTX
PDF
Ph.d. thesis modeling and simulation of z source inverter design and its con...
PPT
EEG - Montages, Equipment and Basic Physics
PPTX
Routing Protocols
PDF
Introduction to Software Defined Networking (SDN)
PDF
Wireless power / Wireless Electricity
PPT
Routing
DOCX
Nat failover with dual isp on cisco router configuration explained with example
PPTX
Wireless electricity
PPTX
A Practical Guide to (Correctly) Troubleshooting with Traceroute
DETECTION OF FAULT LOCATION IN TRANSMISSION LINE USING INTERNET OF THINGS (IOT)
Evaluation and investigation of Neuromuscular disorders
info diodos tvs.pdf
Wireless Power Transmission through TESLA COILS
powerline communication system
Spinal Cord Injury
Fundamentals of Nerve conduction studies and its Interpretations
Occupational Therapy Evaluations
IP classes
Ph.d. thesis modeling and simulation of z source inverter design and its con...
EEG - Montages, Equipment and Basic Physics
Routing Protocols
Introduction to Software Defined Networking (SDN)
Wireless power / Wireless Electricity
Routing
Nat failover with dual isp on cisco router configuration explained with example
Wireless electricity
A Practical Guide to (Correctly) Troubleshooting with Traceroute
Ad

Similar to New Tools for a More Functional C++ (20)

PPTX
Vocabulary Types in C++17
PPT
Vectors Intro.ppt
PDF
Object Oriented Programming (OOP) using C++ - Lecture 3
PDF
AI Lab menu for ptu students and easy to use and best quality and help for 6t...
PDF
AI Lab print ptu universty st soldier collage .pdf
PDF
Object-oriented Design: Polymorphism via Inheritance (vs. Delegation)
PPTX
Object Oriented Programming using C++: Ch09 Inheritance.pptx
PPT
Oop objects_classes
PDF
Effective replacement of dynamic polymorphism with std::variant
PDF
Object Oriented Programming (OOP) using C++ - Lecture 4
PDF
Overloading in Overdrive: A Generic Data-Centric Messaging Library for DDS
PPT
Inheritance compiler support
PDF
C++ L02-Conversion+enum+Operators
PPT
Templates and Polymorphism in C++ Programming
PPT
vdocument.in_data-structures-and-algorithms-in-c-michael-t-goodrich-roberto-t...
PDF
Minimal Introduction to C++ - Part III - Final
PPT
Lecture4
PPTX
Fun with Lambdas: C++14 Style (part 2)
PDF
Generic programming and concepts that should be in C++
PDF
Make all of your classes work correctly with sortingData.cpp. Note t.pdf
Vocabulary Types in C++17
Vectors Intro.ppt
Object Oriented Programming (OOP) using C++ - Lecture 3
AI Lab menu for ptu students and easy to use and best quality and help for 6t...
AI Lab print ptu universty st soldier collage .pdf
Object-oriented Design: Polymorphism via Inheritance (vs. Delegation)
Object Oriented Programming using C++: Ch09 Inheritance.pptx
Oop objects_classes
Effective replacement of dynamic polymorphism with std::variant
Object Oriented Programming (OOP) using C++ - Lecture 4
Overloading in Overdrive: A Generic Data-Centric Messaging Library for DDS
Inheritance compiler support
C++ L02-Conversion+enum+Operators
Templates and Polymorphism in C++ Programming
vdocument.in_data-structures-and-algorithms-in-c-michael-t-goodrich-roberto-t...
Minimal Introduction to C++ - Part III - Final
Lecture4
Fun with Lambdas: C++14 Style (part 2)
Generic programming and concepts that should be in C++
Make all of your classes work correctly with sortingData.cpp. Note t.pdf
Ad

More from Sumant Tambe (19)

PDF
Kafka tiered-storage-meetup-2022-final-presented
PPTX
Systematic Generation Data and Types in C++
PPTX
Tuning kafka pipelines
PPTX
C++ Coroutines
PPTX
C++ Generators and Property-based Testing
PDF
Reactive Stream Processing in Industrial IoT using DDS and Rx
PDF
RPC over DDS Beta 1
PDF
Remote Log Analytics Using DDS, ELK, and RxJS
PDF
Property-based Testing and Generators (Lua)
PDF
Reactive Stream Processing for Data-centric Publish/Subscribe
PDF
Reactive Stream Processing Using DDS and Rx
PDF
Fun with Lambdas: C++14 Style (part 1)
PDF
An Extensible Architecture for Avionics Sensor Health Assessment Using DDS
PDF
Standardizing the Data Distribution Service (DDS) API for Modern C++
PDF
Communication Patterns Using Data-Centric Publish/Subscribe
PDF
C++11 Idioms @ Silicon Valley Code Camp 2012
PPTX
Retargeting Embedded Software Stack for Many-Core Systems
PPTX
Ph.D. Dissertation
PDF
Native XML processing in C++ (BoostCon'11)
Kafka tiered-storage-meetup-2022-final-presented
Systematic Generation Data and Types in C++
Tuning kafka pipelines
C++ Coroutines
C++ Generators and Property-based Testing
Reactive Stream Processing in Industrial IoT using DDS and Rx
RPC over DDS Beta 1
Remote Log Analytics Using DDS, ELK, and RxJS
Property-based Testing and Generators (Lua)
Reactive Stream Processing for Data-centric Publish/Subscribe
Reactive Stream Processing Using DDS and Rx
Fun with Lambdas: C++14 Style (part 1)
An Extensible Architecture for Avionics Sensor Health Assessment Using DDS
Standardizing the Data Distribution Service (DDS) API for Modern C++
Communication Patterns Using Data-Centric Publish/Subscribe
C++11 Idioms @ Silicon Valley Code Camp 2012
Retargeting Embedded Software Stack for Many-Core Systems
Ph.D. Dissertation
Native XML processing in C++ (BoostCon'11)

Recently uploaded (20)

PDF
Understanding Forklifts - TECH EHS Solution
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Softaken Excel to vCard Converter Software.pdf
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PPTX
ManageIQ - Sprint 268 Review - Slide Deck
PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PDF
medical staffing services at VALiNTRY
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PPT
Introduction Database Management System for Course Database
PDF
How Creative Agencies Leverage Project Management Software.pdf
PPTX
ai tools demonstartion for schools and inter college
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
PDF
System and Network Administraation Chapter 3
PPTX
Transform Your Business with a Software ERP System
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PPTX
ISO 45001 Occupational Health and Safety Management System
Understanding Forklifts - TECH EHS Solution
Design an Analysis of Algorithms II-SECS-1021-03
Softaken Excel to vCard Converter Software.pdf
VVF-Customer-Presentation2025-Ver1.9.pptx
ManageIQ - Sprint 268 Review - Slide Deck
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
medical staffing services at VALiNTRY
PTS Company Brochure 2025 (1).pdf.......
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Introduction Database Management System for Course Database
How Creative Agencies Leverage Project Management Software.pdf
ai tools demonstartion for schools and inter college
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
2025 Textile ERP Trends: SAP, Odoo & Oracle
System and Network Administraation Chapter 3
Transform Your Business with a Software ERP System
Odoo Companies in India – Driving Business Transformation.pdf
Upgrade and Innovation Strategies for SAP ERP Customers
ISO 45001 Occupational Health and Safety Management System

New Tools for a More Functional C++

  • 1. New Tools for a More Functional C++ Sumant Tambe Sr. Software Engineer, LinkedIn Microsoft MVP SF Bay Area ACCU Sept 28, 2017
  • 2. Blogger (since 2005) Coditation—Elegant Code for Big Data Author (wikibook) Open-source contributor Since 2013 (Visual Studio and Dev Tech)
  • 3. Functional Programming in C++ by Ivan Cukic (ETA early 2018) Reviewer
  • 4. Sum Types and (pseudo) Pattern Matching
  • 5. Modeling Alternatives Inheritance vs std::variant • States of a game of Tennis • NormalScore • DeuceScore • AdvantageScore • GameCompleteScore
  • 6. Modeling game states using std::variant struct NormalScore { Player p1, p2; int p1_score, p2_score; }; struct DeuceScore { Player p1, p2; }; struct AdvantageScore { Player lead, lagging; }; struct GameCompleteScore { Player winner, loser; int loser_score; }; using GameState = std::variant<NormalScore, DeuceScore, AdvantageScore, GameCompleteScore>;
  • 7. Print GameState (std::variant) struct GameStatePrinter { std::ostream &o; explicit GameStatePrinter(std::ostream& out) : o(out) {} void operator ()(const NormalScore& ns) const { o << "NormalScore[" << ns.p1 << ns.p2 << ns.p1_score << ns.p2_score << "]"; } void operator ()(const DeuceScore& ds) const { o << "DeuceScore[" << ds.p1 << "," << ds.p2 << "]"; } void operator ()(const AdvantageScore& as) const { o << "AdvantageScore[" << as.lead << "," << as.lagging << "]"; } void operator ()(const GameCompleteScore& gc) const { o << "GameComplete[" << gc.winner << gc.loser << gc.loser_score << "]"; } }; std::ostream & operator << (std::ostream& o, const GameState& game) { std::visit(GameStatePrinter(o), game); return o; }
  • 8. std::visit spews blood when you miss a case
  • 9. Print GameState. Fancier! std::ostream & operator << (std::ostream& o, const GameState& game) { std::visit(overloaded { [&](const NormalScore& ns) { o << "NormalScore" << ns.p1 << ns.p2 << ns.p1_score << ns.p2_score; }, [&](const DeuceScore& gc) { o << "DeuceScore[" << ds.p1 << "," << ds.p2 << "]"; }, [&](const AdvantageScore& as) { o << "AdvantageScore[" << as.lead << "," << as.lagging << "]"; }, [&](const GameCompleteScore& gc) { o << "GameComplete[" << gc.winner << gc.loser << gc.loser_score << "]"; } }, game); return o; }
  • 10. Passing Two Variants to std::visit std::ostream & operator << (std::ostream& o, const GameState& game) { std::visit(overloaded { [&](const NormalScore& ns, const auto& other) { o << "NormalScore" << ns.p1 << ns.p2 << ns.p1_score << ns.p2_score; }, [&](const DeuceScore& gc, const auto& other) { o << "DeuceScore[" << ds.p1 << "," << ds.p2 << "]"; }, [&](const AdvantageScore& as, const auto& other) { o << "AdvantageScore[" << as.lead << "," << as.lagging << "]"; }, [&](const GameCompleteScore& gc, const auto& other) { o << "GameComplete[" << gc.winner << gc.loser << gc.loser_score << "]"; } }, game, someother_variant); return o; } There can be arbitrary number of arbitrary variant types. The visitor must cover all cases
  • 11. A Template to Inherit Lambdas template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; explicit overloaded(Ts... ts) : Ts(ts)... {} }; A User-Defined Deduction Guide explicit overloaded(Ts... ts) : Ts(ts)... {} template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
  • 12. Next GameState Algorithm (all cases in one place) GameState next (const GameState& now, const Player& who_scored) { return std::visit(overloaded { [&](const DeuceScore& ds) -> GameState { if (ds.p1 == who_scored) return AdvantageScore{ds.p1, ds.p2}; else return AdvantageScore{ds.p2, ds.p1}; }, [&](const AdvantageScore& as) -> GameState { if (as.lead == who_scored) return GameCompleteScore{as.lead, as.lagging, 40}; else return DeuceScore{as.lead, as.lagging}; }, [&](const GameCompleteScore &) -> GameState { throw "Illegal State"; }, [&](const NormalScore& ns) -> GameState { if (ns.p1 == who_scored) { switch (ns.p1_score) { case 0: return NormalScore{ns.p1, ns.p2, 15, ns.p2_score}; case 15: return NormalScore{ns.p1, ns.p2, 30, ns.p2_score}; case 30: if (ns.p2_score < 40) return NormalScore{ns.p1, ns.p2, 40, ns.p2_score}; else return DeuceScore{ns.p1, ns.p2}; case 40: return GameCompleteScore{ns.p1, ns.p2, ns.p2_score}; default: throw "Makes no sense!"; } } else { switch (ns.p2_score) { case 0: return NormalScore{ns.p1, ns.p2, ns.p1_score, 15}; case 15: return NormalScore{ns.p1, ns.p2, ns.p1_score, 30}; case 30: if (ns.p1_score < 40) return NormalScore{ns.p1, ns.p2, ns.p1_score, 40}; else return DeuceScore{ns.p1, ns.p2}; case 40: return GameCompleteScore{ns.p2, ns.p1, ns.p1_score}; default: throw "Makes no sense!"; } } } }, now); }
  • 13. Modeling Alternatives with Inheritance class GameState { std::unique_ptr<GameStateImpl> _state; public: void next(const Player& who_scored) {} }; class GameStateImpl { Player p1, p2; public: virtual GameStateImpl * next( const Player& who_scored) = 0; virtual ~GameStateImpl(){} }; class NormalScore : public GameStateImpl { int p1_score, p2_score; public: GameStateImpl * next(const Player&); }; class DeuceScore : public GameStateImpl { public: GameStateImpl * next(const Player&); }; class AdvantageScore : public GameStateImpl { int lead; public: GameStateImpl * next(const Player&); }; class GameCompleteScore :public GameStateImpl{ int winner, loser_score; public: GameStateImpl * next(const Player&); };
  • 14. Sharing State is easier with Inheritance class GameState { std::unique_ptr<GameStateImpl> _state; public: void next(const Player& who_scored) {} Player& who_is_serving() const; double fastest_serve_speed() const; GameState get_last_state() const; }; class GameStateImpl { Player p1, p2; int serving_player; double speed; GameState last_state; public: virtual GameStateImpl * next( const Player& who_scored) = 0; virtual Player& who_is_serving() const; virtual double fastest_serve_speed() const; virtual GameState get_last_state() const; };
  • 15. Sharing Common State is Repetitive with std::variant Player who_is_serving = std::visit([](auto& s) { return s.who_is_serving(); }, state); Player who_is_serving = state.who_is_serving(); ceremony! struct NormalScore { Player p1, p2; int p1_score, p2_score; int serving_player; Player & who_is_serving(); }; struct DeuceScore { Player p1, p2; int serving_player; Player & who_is_serving(); }; struct AdvantageScore { Player lead, lagging; int serving_player; Player & who_is_serving(); }; struct GameCompleteScore { Player winner, loser; int loser_score; int serving_player; Player & who_is_serving(); };
  • 16. How about recursive std::variant? struct NormalScore { Player p1, p2; int p1_score, p2_score; int serving_player; Player & who_is_serving(); GameState last_state; }; struct DeuceScore { Player p1, p2; int serving_player; Player & who_is_serving(); GameState last_state; }; struct AdvantageScore { Player lead, lagging; int serving_player; Player & who_is_serving(); GameState last_state; }; struct GameCompleteScore { Player winner, loser; int loser_score; int serving_player; Player & who_is_serving(); GameState last_state; }; Not possible unless you use recursive_wrapper and dynamic allocation. Not in C++17. Dare I say, it’s not algebraic? It does not compose  std::variant is a container. Not an abstraction.
  • 17. std::variant disables fluent interfaces { using GameState = std::variant<NormalScore, DeuceScore, AdvantageScore, GameCompleteScore>; GameState state = NormalScore {..}; GameState last_state = std::visit([](auto& s) { return s.get_last_state(); }, state); double last_speed = std::visit([](auto& s) { return state.fastest_serve_speed(); }, last_state); double last_speed = state.get_last_state().fastest_serve_speed(); } ceremony!
  • 18. Combine Implementation Inheritance with std::variant { using GameState = std::variant<NormalScore_v2, DeuceScore_v2, AdvantageScore_v2, GameCompleteScore_v2>; GameState state = NormalScore_v2 {..}; Player who_is_serving = std::visit([](SharedGameState& s) { return s.who_is_serving(); }, state); Player who_is_serving = state.who_is_serving(); } SharedGameState who_is_serving() NormalScore_v2 DeuceScore_v2 AdvantageScore_v2 GameCompleteScore_v2 ceremony!
  • 19. Modeling Alternatives Inheritance std::variant Dynamic Allocation No dynamic allocation Intrusive Non-intrusive Reference semantics (how will you copy a vector?) Value semantics Algorithm scattered into classes Algorithm in one place Language supported Clear errors if pure-virtual is not implemented Library supported std::visit spews blood on missing cases Creates a first-class abstraction It’s just a container Keeps fluent interfaces Disables fluent interfaces. Repeated std::visit Supports recursive types (Composite) Must use recursive_wrapper and dynamic allocation. Not in the C++17 standard.
  • 21. const is shallow struct X { void bar(); }; struct Y { X* xptr; explicit Y(X* x) : xptr(x) {} void foo() const { xptr->bar(); } }; { const Y y(new X); y.foo(); // mutates X?? }
  • 22. Deep Immutability: propagate_const<T> struct X { void bar(); void bar() const; // Compiler error without this function }; struct Y { X* xptr; propagate_const<X *> xptr; explicit Y(X* x) : xptr(x) {} void foo() const { xptr->bar(); } }; { const Y y(new X); y.foo(); // calls X.bar() const }
  • 23. Deep Immutability: propagate_const<T> #include <experimental/propagate_const> using std::experimental::propagate_const; { propagate_const<X *> xptr; propagate_const<std::unique_ptr<X>> uptr; propagate_const<std::shared_ptr<X>> shptr; const propagate_const<std::shared_ptr<X>> c_shptr; shptr.get() === X* c_shptr.get() === const X* *shptr === X& *c_shptr === const X& get_underlying(shptr) === shared_ptr<X> get_underlying(c_shptr) === const shared_ptr<X> shptr = another_shptr; // Error. Not copy-assignable shptr = std::move(another_shptr) // but movable Library fundamental TS v2
  • 25. The Named Parameter Idiom (mutable) class configs { std::string server; std::string protocol; public: configs & set_server(const std::string& s); configs & set_protocol(const std::string& s); }; start_server(configs().set_server(“localhost”) .set_protocol(“https”));
  • 26. The Named Parameter Idiom (immutable) class configs { public: configs set_server(const std::string& s) const { configs temp(*this); temp.server = s; return temp; } configs set_protocol(const std::string& proto) const { configs temp(*this); temp.protocol = proto; return temp; } }; start_server(configs().set_server(“localhost”) .set_protocol(“https”)); Avoid copy-constructors?
  • 27. The Named Parameter Idiom (immutable*) class configs { public: configs set_server(const std::string& s) const { configs temp(*this); temp.server = s; return temp; } configs set_protocol(const std::string& proto) const { configs temp(*this); temp.protocol = proto; return temp; } configs set_server(const std::string& s) && { server = s; return *this; } configs set_protocol(const std::string& proto) && { protocol = proto; return *this; } }; start_server(configs().set_server(“localhost”) .set_protocol(“https”)); & &
  • 28. The Named Parameter Idiom (immutable*) class configs { public: configs set_server(const std::string& s) const { configs temp(*this); temp.server = s; return temp; } configs set_protocol(const std::string& proto) const { configs temp(*this); temp.protocol = proto; return temp; } configs&& set_server(const std::string& s) && { server = s; return *this; std::move(*this); } configs&& set_protocol(const std::string& proto) && { protocol = proto; return *this; std::move(*this); } }; start_server(configs().set_server(“localhost”) .set_protocol(“https”)); & &

Editor's Notes

  • #13: How many of you think that it’s an important benefit that all cases are together.
  • #16: Who_is_serving visitor.
  • #17: Who_is_serving visitor.
  • #19: Who_is_serving visitor.
  • #28: *this is an lvalue. Still calls the copy-constructor.