Meeting C++, Berlin 2018
Goran Aranđelović
goran.arandjelovic@gmail.com
struct device_lock
{
virtual void lock() = 0;
virtual void unlock() = 0;
};
struct no_lock : device_lock
{
void lock() override {}
void unlock() override {}
};
struct subscription_lock : device_lock
{
void lock() override { /* ... */ }
void unlock() override { /* ... */ }
};
struct player
{
virtual void play_stream() = 0;
};
class hybrid_player : public player
{
shared_ptr<stream_protection> sp_;
public:
void set_stream_protection(
shared_ptr<stream_protection> sp
) { sp_ = sp; }
void play_stream() override {
auto _dvb_stream = make_shared<dvb_stream>();
auto _ip_stream = make_shared<ip_stream>();
if (sp_->has_access(_dvb_stream) &&
sp_->has_access(_ip_stream))
{
/* ... */
}
}
};
struct player
{
virtual void play_stream() = 0;
};
class ip_player : public player
{
shared_ptr<stream_protection> sp_;
public:
void set_stream_protection(
shared_ptr<stream_protection> sp
) { sp_ = sp; }
void play_stream() override {
auto _stream = make_shared<ip_stream>();
if (sp_->has_access(_stream)) {
/* ... */
}
}
};
struct stream {
virtual ~stream() = default; // RTTI
};
struct stream_protection {
virtual bool has_access(shared_ptr<stream>) = 0;
};
struct cas_protection : stream_protection {
bool has_access(shared_ptr<stream>) override
{ /* ... */ }
};
struct drm_protection : stream_protection {
bool has_access(shared_ptr<stream>) override
{ /* ... */ }
};
struct dvb_stream : stream { /* ... */ };
struct ip_stream : stream { /* ... */ };
class hybrid_protection : public stream_protection
{
cas_protection cas_p{};
drm_protection drm_p{};
public:
bool has_access(shared_ptr<stream> s) override {
if(auto dvb_s = dynamic_pointer_cast<dvb_stream>(s);
dvb_s) {
return cas_p.has_access(dvb_s);
}
else if(auto ip_s = dynamic_pointer_cast<ip_stream>(s);
ip_s) {
return drm_p.has_access(ip_s);
}
return false;
}
};
struct dvb_stream : stream { /* ... */ };
struct ip_stream : stream { /* ... */ };
class hybrid_protection : public stream_protection
{
cas_protection cas_p{};
drm_protection drm_p{};
public:
bool has_access(shared_ptr<stream> s) override {
if(auto dvb_s = dynamic_pointer_cast<dvb_stream>(s);
dvb_s) {
return cas_p.has_access(dvb_s);
}
else if(auto ip_s = dynamic_pointer_cast<ip_stream>(s);
ip_s) {
return drm_p.has_access(ip_s);
}
return false;
}
};
std::reinterpret_pointer_cast (since C++17)
struct device
{
virtual void lock() = 0;
virtual void unlock() = 0;
virtual void watch() = 0;
};
class cable_box : public device
{
shared_ptr<player> player_;
shared_ptr<device_lock> lock_;
public:
/* other members omitted */
void watch() override {
/* ... */
player_->play_stream();
}
void lock() override { lock_->lock(); }
void unlock() override { lock_->unlock(); }
};
void use_device(shared_ptr<device> d)
{
d->unlock();
d->watch();
d->lock();
}
auto my_box = make_shared<cable_box>();
auto player = make_shared<hybrid_player>();
player->set_stream_protection(
make_shared<hybrid_protection>()
);
my_box->set_player(player);
use_device(my_box);
"Designing software systems is hard because it constantly asks you to choose. And in program design, just as in life, choice is hard."
— Andrei Alexandrescu, Modern C++ design
https://salvarado24.wordpress.com/2012/10/17/the-road-to-monument-valley/
"Designing software systems is hard because it constantly asks you to choose. And in program design, just as in life, choice is hard."
— Andrei Alexandrescu, Modern C++ design
Stelvio pass (2757m), Eastern Alps - Italy
template<typename T>
class list { /* ... */ };
int main()
{
list<int> ints = {1, 2, 3, 4};
list<string> strings = {"meeting", "c++"};
}
template<int I>
struct factorial
{
static constexpr int value = I * factorial<I-1>::value;
};
template<>
struct factorial<0>
{
static constexpr int value = 1;
};
int main()
{
cout << factorial<10>::value << '\n';
}
template<typename T>
struct add_pointer
{
using type = T*;
}
template<typename T>
using add_pointer_t = typename add_pointer<T>::type;
int main()
{
int i = 2018;
add_pointer_t<int> p = &i; // int *p = &i;
}
/* Modern C++ Design
http://loki-lib.sourceforge.net
*/
template<typename T, typename U>
struct Typelist
{
typedef T Head;
typedef U Tail;
};
typedef Typelist<int, Typelist<bool, char>> my_types;
/* Modern C++ Design
http://loki-lib.sourceforge.net
*/
template<typename T, typename U>
struct Typelist
{
typedef T Head;
typedef U Tail;
};
typedef Typelist<int, Typelist<bool, char> > my_types;
// Variadic templates
template<typename ...Ts>
struct Typelist {};
using my_types = Typelist<int, bool, char>;
C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond - David Abrahams, Aleksey Gurtovoy
Modern C++ Design - Andrei Alexandrescu
Joel de Guzman, Dan Marsden, Tobias Schwinger
Louis Dionne
template<typename T> // boost::hana - L.Dionne
struct type {};
template<typename T>
constexpr type<T*> add_pointer(type<T>)
{
return {};
}
template<typename T, typename U>
constexpr std::false_type operator==(type<T>, type<U>)
{
return {};
}
template<typename T>
constexpr std::true_type operator==(type<T>, type<T>)
{
return {};
}
using int_ptr = add_pointer_t<int>;
static_assert(std::is_same<int_ptr, int*>{});
constexpr auto int_ptr = add_pointer(type<int>{});
static_assert(int_ptr == type<int*>{});
"Policy-based design, also known as policy-based class design or policy-based programming, is a computer programming paradigm based on an idiom for C++ known as policies. It has been described as a compile-time variant of the strategy pattern, and has connections with C++ template metaprogramming."
wikipedia.org/wiki/Policy-based_design
struct Point { int x; int y; };
template<typename TablePolicy, typename RulesPolicy>
struct TableGame : private TablePolicy
{
template<typename Player>
void move_player(Player const &player, Point const &point)
{
if(RulesPolicy::valid_move(player, point))
{
move(player, point);
}
}
};
struct device_lock
{
virtual void lock() = 0;
virtual void unlock() = 0;
};
struct no_lock : device_lock
{
void lock() override {}
void unlock() override {}
};
struct subscription_lock : device_lock
{
void lock() override { /* ... */ }
void unlock() override { /* ... */ }
};
struct no_lock
{
void lock() {}
void unlock() {}
};
struct subscription_lock
{
void lock() { /* ... */ }
void unlock() { /* ... */ }
};
struct player
{
virtual void play_stream() = 0;
};
class ip_player : public player
{
shared_ptr<stream_protection> sp_;
public:
void set_stream_protection(
shared_ptr<stream_protection> sp
) { sp_ = sp; }
void play_stream() override {
auto _stream = make_shared<ip_stream>();
if (sp_->has_access(_stream)) {
/* ... */
}
}
};
template<typename StreamProtection>
struct ip_player
{
using SP = StreamProtection;
void play_stream() {
ip_stream _stream{};
if (SP::has_access(_stream)) {
/* ... */
}
}
};
struct player
{
virtual void play_stream() = 0;
};
class hybrid_player : public player
{
shared_ptr<stream_protection> sp_;
public:
void set_stream_protection(
shared_ptr<stream_protection> sp
) { sp_ = sp; }
void play_stream() override {
auto _dvb_stream = make_shared<dvb_stream>();
auto _ip_stream = make_shared<ip_stream>();
if (sp_->has_access(_dvb_stream) &&
sp_->has_access(_ip_stream))
{
/* ... */
}
}
};
template<typename StreamProtection>
struct hybrid_player
{
using SP = StreamProtection;
void play_stream() {
dvb_stream _dvb_stream{};
ip_stream _ip_stream{};
if (SP::has_access(_dvb_stream) &&
SP::has_access(_ip_stream))
{
/* ... */
}
}
};
struct stream {
virtual ~stream() = default; // RTTI
};
struct stream_protection {
virtual bool has_access(shared_ptr<stream>) = 0;
};
struct cas_protection : stream_protection {
bool has_access(shared_ptr<stream>) override
{ /* ... */ }
};
struct drm_protection : stream_protection {
bool has_access(shared_ptr<stream>) override
{ /* ... */ }
};
struct dvb_stream { /* ... */ };
struct ip_stream { /* ... */ };
struct cas_protection {
static bool has_access(dvb_stream)
{ /* ... */ }
};
struct drm_protection {
static bool has_access(ip_stream)
{ /* ... */ }
};
struct dvb_stream : stream { /* ... */ };
struct ip_stream : stream { /* ... */ };
class hybrid_protection : public stream_protection
{
cas_protection cas_p{};
drm_protection drm_p{};
public:
bool has_access(shared_ptr<stream> s) override {
if(auto dvb_s = dynamic_pointer_cast<dvb_stream>(s);
dvb_s) {
return cas_p.has_access(dvb_s);
}
else if(auto ip_s = dynamic_pointer_cast<ip_stream>(s);
ip_s) {
return drm_p.has_access(ip_s);
}
return false;
}
};
struct dvb_stream { /* ... */ };
struct ip_stream { /* ... */ };
struct hybrid_protection
: cas_protection, drm_protection {
using cas_protection::has_access;
using drm_protection::has_access;
};
class cable_box : public device
{
shared_ptr<player> player_;
shared_ptr<device_lock> lock_;
public:
/* other members omitted */
void watch() override {
/* ... */
player_->play_stream();
}
void lock() override { lock_->lock(); }
void unlock() override { lock_->unlock(); }
};
template<typename LockPolicy,
template<typename> typename Player,
typename StreamProtection>
class cable_box : public LockPolicy
{
Player<StreamProtection> player_;
public:
/* other members omitted */
void watch() {
/* ... */
player_.play_stream();
}
void print_lock_state() {
cout << this->is_locked() << '\n';
}
};
void use_device(shared_ptr<device> d)
{
d->unlock();
d->watch();
d->lock();
}
auto my_box = make_shared<cable_box>();
auto player = make_shared<hybrid_player>();
player->set_stream_protection(
make_shared<hybrid_protection>()
);
my_box->set_player(player);
use_device(my_box);
template<typename Device>
void use_device(Device &d) {
d.unlock();
d.watch();
d.lock();
}
cable_box<
no_lock, hybrid_player, hybrid_protection
> my_box{};
use_device(my_box);
// my_box.print_lock_state(); ??
Invented by Walter E. Brown
wg21.link/n4502
template<typename T> // #1
void f(typename T::value_type)
{}
template<typename T> // #2
void f(T)
{}
struct X {
using value_type = int;
};
f<X>(0); // #1
f<int>(0); // #2
template<typename T> // #1
void f(T, std::enable_if_t<std::is_pointer_v<T>>* = 0)
{}
template<typename T> // #2
void f(T, std::enable_if_t<not std::is_pointer_v<T>>* = 0)
{}
int* p{};
int i{};
f(p); // #1
f(i); // #2
template<typename T> // #1
auto f(T t) -> decltype(t.a(), void())
{}
template<typename T> // #2
auto f(T t) -> decltype(t.b(), void())
{}
struct A { void a() {} };
struct B { void b() {} };
struct C {};
f(A{}); // #1
f(B{}); // #2
f(C{}); // error
template<typename...>
using void_t = void;
template<typename Default,
template<typename...> typename Op,
typename,
typename ...Args>
struct detector
{
using value_t = std::false_type;
using type = Default;
};
template<typename Default,
template<typename...> typename Op,
typename ...Args>
struct detector<Default, Op, std::void_t<Op<Args...>>, Args...>
{
using value_t = std::true_type;
using type = Op<Args...>;
};
template<template<typename...> typename Op, typename ...Args>
using is_detected_t = typename detector<nonesuch, Op, std::void_t<>, Args...>::type;
template<template<typename...> typename Op, typename ...Args>
using is_detected = typename detector<nonesuch, Op, std::void_t<>, Args...>::value_t;
template<template<typename...> typename Op, typename ...Args>
constexpr bool is_detected_v = is_detected<Op, Args...>::value;
template<typename T>
using drm_protection_t =
decltype(T::has_access(std::declval<ip_stream>()));
template<typename T>
using has_drm_protection =
is_detected<drm_protection_t, T>;
template<typename StreamProtection>
struct ip_player
{
using SP = StreamProtection;
static_assert(
has_drm_protection<SP>{}
);
void play_stream() {
ip_stream _stream{};
if (SP::has_access(_stream)) {
/* ... */
}
}
};
template<typename T>
using cas_protection_t =
decltype(T::has_access(std::declval<dvb_stream>()));
template<typename T>
using has_cas_protection =
is_detected<cas_protection_t, T>;
template<typename T>
using has_hybrid_protection = std::conjunction<
has_cas_protection<T>,
has_drm_protection<T>
>;
template<typename StreamProtection>
struct hybrid_player
{
using SP = StreamProtection;
static_assert(
has_hybrid_protection<SP>{}
);
void play_stream() {
dvb_stream _dvb_stream{};
ip_stream _ip_stream{};
if (SP::has_access(_dvb_stream) &&
SP::has_access(_ip_stream))
{
/* ... */
}
}
};
template<typename T>
using lock_unlock_t = decltype(
std::declval<T>().lock(),
std::declval<T>().unlock()
);
template<typename T>
using play_stream_t
= decltype(std::declval<T>().play_stream());
template<typename T>
using has_lock_unlock = is_detected<lock_unlock_t, T>;
template<typename T>
using has_play_stream = is_detected<play_stream_t, T>;
template<typename LockPolicy,
template<typename> typename Player,
typename StreamProtection>
class cable_box : public LockPolicy
{
static_assert(
has_lock_unlock<LockPolicy>{} &&
has_play_stream<Player<StreamProtection>>{}
);
Player<StreamProtection> player_;
public:
/* other members omitted */
void watch() {
/* ... */
player_.play_stream();
}
void print_lock_state() {
cout << this->is_locked() << '\n';
}
};
template<typename T> // "Dirty" concept
concept Doable = requires(T t) {
{ t.do_it() };
};
template<typename T>
concept Iterable = requires(T range) {
{ begin(range) } -> Iterator_type<T>;
{ end(range) } -> Sentinel_type<T>;
requires Iterator<Iterator_type<T>>;
requires EqualityComparable<
Iterator_type<T>, Sentinel_type<T>>;
};
Example borrowed from: http://ericniebler.com/2014/02/21/introducing-iterables/
template<typename T>
void f(T const &t) requires Iterable<T>
{
/* ... */
}
template<typename T>
void f(T const &t) requires not Iterable<T>
{
/* ... */
}
void f(C &c) // What's the type of 'c'?
{
/* ... */
}
void f(C &&c)
{
/* ... */
}
template<typename T>
concept DrmProtectable = requires(T t) {
{ T::has_access(std::declval<ip_stream>()) } -> bool;
};
template<DrmProtectable StreamProtection>
struct ip_player
{
using SP = StreamProtection;
void play_stream() {
ip_stream _stream{};
if (SP::has_access(_stream)) {
/* ... */
}
}
};
template<typename T>
concept CasProtectable = requires(T t) {
{ T::has_access(std::declval<dvb_stream>()) } -> bool;
};
template<typename T>
concept HybridProtectable =
CasProtectable<T> && DrmProtectable<T>;
template<HybridProtectable StreamProtection>
struct hybrid_player
{
using SP = StreamProtection;
void play_stream() {
dvb_stream _dvb_stream{};
ip_stream _ip_stream{};
if (SP::has_access(_dvb_stream) &&
SP::has_access(_ip_stream))
{
/* ... */
}
}
};
template<typename T>
concept Lockable = requires(T t) {
{ t.lock() };
{ t.unlock() };
};
template<typename T>
concept Playable = requires(T t) {
{ t.play_stream() };
};
template<Lockable LockPolicy,
template<typename> typename Player,
typename StreamProtection>
requires Playable<Player<StreamProtection>>
class cable_box : public LockPolicy
{
Player<StreamProtection> player_;
public:
/* other members omitted */
void watch() {
/* ... */
player_.play_stream();
}
void print_lock_state() {
cout << this->is_locked() << '\n';
}
};
template<typename T>
concept Device = requires(T t) {
requires Lockable<T>;
{ t.watch() };
};
void use_device(Device auto &d) { // wg21.link/p1141r1
d.unlock();
d.watch();
d.lock();
}
cable_box<
no_lock, hybrid_player, hybrid_protection
> my_box{};
use_device(my_box);
void my_func(cable_box<???, ???, ???> &device)
{
/* ... */
}
template<typename A, typename B, typename C>
void my_func(cable_box<A, B, C> &device)
{
/* ... */
}
void my_func(Device auto &device)
{
/* ... */
}
A a{};
void *v = &a;
struct A {};
struct B : A {};
B *b = ... ;
A *a = b;
struct A {};
struct B : A {};
shared_ptr<B> b = ... ;
shared_ptr<A> a = b;
template<typename T>
struct shared_ptr
{
template<typename U>
shared_ptr(shared_ptr<U> const &,
std::enable_if_t<std::is_convertible_v<U*, T*>>* = 0)
{ /* ... */ }
};
template<typename Player,
typename StreamProtection>
void my_func(cable_box<any_lock, Player, StreamProtection> &device)
{
/* ... */
}
cable_box<
no_lock, hybrid_player, hybrid_protection
> my_box{};
my_func(my_box);
youtu.be/_BpMYeUFXv8