#include <iostream>
int main()
{
std::cout << "Hi, I'm Borislav!\n";
std::cout << "These slides are here: https://is.gd/dmxcpp\n";
return 0;
}
#include <iostream>
int main()
{
std::cout << "Hi, I'm Borislav!\n";
std::cout << "These slides are here: https://is.gd/dmxcpp\n";
return 0;
}
DynaMix: A New Take on Polymorphism
DynaMix: A New Take on Polymorphism
Many forms
The same code does multiple things
The differentiation is done by the compiler
abs(5);
abs(5.); // function overloads
template <typename Vector> // templates
auto dot_product(const Vector& a, const Vector& b) {
assert(a.size() == b.size());
typename Vector::value_type sum = 0;
for (size_t i=0; i<a.size(); ++i) {
sum += a[i]*b[i]; // operator overloads
}
return sum;
}
Static polymorphism is modern C++
We're not going to talk about modern C++
Dynamic polymorphism and Object oriented programming (OOP)
std::function
Totally anti modern C++
Out of the box in an OOP context C++ only gives us virtual functions for polymorphism
struct Drawable {
virtual void draw(ostream& out) const = 0;
}
void f(const Drawable& d) {
d.draw(cout);
}
struct Square : public Drawable {
virtual void draw(ostream& out) const override { out << "Square\n"; }
};
struct Circle : public Drawable {
virtual void draw(ostream& out) const override { out << "Circle\n"; }
};
int main() {
f(Square{});
f(Circle{});
}
DynaMix: A New Take on Polymorphism
DynaMix: A New Take on Polymorphism
using Drawable = Library_Magic(void, draw, (ostream&));
void f(const Drawable& d) {
d.draw(cout);
}
struct Square {
void draw(ostream& out) const { out << "Square\n"; }
};
struct Circle {
void draw(ostream& out) const { out << "Circle\n"; }
};
int main() {
f(Square{});
f(Circle{});
}
using Drawable = Library_Magic(void, draw, (ostream&));
void f(const Drawable& d) {
d.draw(cout);
}
struct Square {
void draw(ostream& out) const { out << "Square\n"; }
};
struct Circle {
void draw(ostream& out) const { out << "Circle\n"; }
};
int main() {
f(Square{});
f(Circle{});
}
collide(obj1, obj2);
DynaMix: A New Take on Polymorphism
DynaMix: A New Take on Polymorphism
Two more mobile games in development
Compose and modify polymorphic objects at run time
module FlyingCreature
def move_to(target)
puts can_move_to?(target) ?
"flying to #{target}"
: "can't fly to #{target}"
end
def can_move_to?(target)
true # flying creatures don't care
end
end
module AfraidOfEvens
def can_move_to?(target)
target % 2 != 0
end
end
a = Object.new
a.extend(FlyingCreature)
a.move_to(10) # -> flying to 10
a.extend(AfraidOfEvens)
a.move_to(10) # -> can’t fly to 10
module FlyingCreature
def move_to(target)
puts can_move_to?(target) ?
"flying to #{target}"
: "can't fly to #{target}"
end
def can_move_to?(target)
true # flying creatures don't care
end
end
module AfraidOfEvens
def can_move_to?(target)
target % 2 != 0
end
end
a = Object.new
a.extend(FlyingCreature)
a.move_to(10) # -> flying to 10
a.extend(AfraidOfEvens)
a.move_to(10) # -> can’t fly to 10
module FlyingCreature
def move_to(target)
puts can_move_to?(target) ?
"flying to #{target}"
: "can't fly to #{target}"
end
def can_move_to?(target)
true # flying creatures don't care
end
end
module AfraidOfEvens
def can_move_to?(target)
target % 2 != 0
end
end
a = Object.new
a.extend(FlyingCreature)
a.move_to(10) # -> flying to 10
a.extend(AfraidOfEvens)
a.move_to(10) # -> can’t fly to 10
module FlyingCreature
def move_to(target)
puts can_move_to?(target) ?
"flying to #{target}"
: "can't fly to #{target}"
end
def can_move_to?(target)
true # flying creatures don't care
end
end
module AfraidOfEvens
def can_move_to?(target)
target % 2 != 0
end
end
a = Object.new
a.extend(FlyingCreature)
a.move_to(10) # -> flying to 10
a.extend(AfraidOfEvens)
a.move_to(10) # -> can’t fly to 10
module FlyingCreature
def move_to(target)
puts can_move_to?(target) ?
"flying to #{target}"
: "can't fly to #{target}"
end
def can_move_to?(target)
true # flying creatures don't care
end
end
module AfraidOfEvens
def can_move_to?(target)
target % 2 != 0
end
end
a = Object.new
a.extend(FlyingCreature)
a.move_to(10) # -> flying to 10
a.extend(AfraidOfEvens)
a.move_to(10) # -> can’t fly to 10
module FlyingCreature
def move_to(target)
puts can_move_to?(target) ?
"flying to #{target}"
: "can't fly to #{target}"
end
def can_move_to?(target)
true # flying creatures don't care
end
end
module AfraidOfEvens
def can_move_to?(target)
target % 2 != 0
end
end
a = Object.new
a.extend(FlyingCreature)
a.move_to(10) # -> flying to 10
a.extend(AfraidOfEvens)
a.move_to(10) # -> can’t fly to 10
DynaMix means "Dynamic Mixins"
struct cd_reader {
string get_sound() const {
return cd.empty() ? "silence" : ("cd: " + cd);
}
string cd;
};
template <typename Self>
struct headphones {
const Self* self() const {
return static_cast<const Self*>(this);
}
void play() {
cout << "Playing " << self()->get_sound()
<< " through headphones\n";
}
};
struct diskman : public cd_reader, public headphones<diskman> {};
struct boombox : public cd_reader, public speakers<boombox> {};
struct ipod : public mp3_reader, public headphones<ipod> {};
struct cd_reader {
string get_sound() const {
return cd.empty() ? "silence" : ("cd: " + cd);
}
string cd;
};
template <typename Self>
struct headphones {
const Self* self() const {
return static_cast<const Self*>(this);
}
void play() {
cout << "Playing " << self()->get_sound()
<< " through headphones\n";
}
};
struct diskman : public cd_reader, public headphones<diskman> {};
struct boombox : public cd_reader, public speakers<boombox> {};
struct ipod : public mp3_reader, public headphones<ipod> {};
struct cd_reader {
string get_sound() const {
return cd.empty() ? "silence" : ("cd: " + cd);
}
string cd;
};
template <typename Self>
struct headphones {
const Self* self() const {
return static_cast<const Self*>(this);
}
void play() {
cout << "Playing " << self()->get_sound()
<< " through headphones\n";
}
};
struct diskman : public cd_reader, public headphones<diskman> {};
struct boombox : public cd_reader, public speakers<boombox> {};
struct ipod : public mp3_reader, public headphones<ipod> {};
struct cd_reader {
string get_sound() const {
return cd.empty() ? "silence" : ("cd: " + cd);
}
string cd;
};
template <typename Self>
struct headphones {
const Self* self() const {
return static_cast<const Self*>(this);
}
void play() {
cout << "Playing " << self()->get_sound()
<< " through headphones\n";
}
};
struct diskman : public cd_reader, public headphones<diskman> {};
struct boombox : public cd_reader, public speakers<boombox> {};
struct ipod : public mp3_reader, public headphones<ipod> {};
struct cd_reader {
string get_sound() const {
return cd.empty() ? "silence" : ("cd: " + cd);
}
string cd;
};
template <typename Self>
struct headphones {
const Self* self() const {
return static_cast<const Self*>(this);
}
void play() {
cout << "Playing " << self()->get_sound()
<< " through headphones\n";
}
};
struct diskman : public cd_reader, public headphones<diskman> {};
struct boombox : public cd_reader, public speakers<boombox> {};
struct ipod : public mp3_reader, public headphones<ipod> {};
struct cd_reader {
string get_sound() const {
return cd.empty() ? "silence" : ("cd: " + cd);
}
string cd;
};
template <typename Self>
struct headphones {
const Self* self() const {
return static_cast<const Self*>(this);
}
void play() {
cout << "Playing " << self()->get_sound()
<< " through headphones\n";
}
};
struct diskman : public cd_reader, public headphones<diskman> {};
struct boombox : public cd_reader, public speakers<boombox> {};
struct ipod : public mp3_reader, public headphones<ipod> {};
template <typename SoundPlayer>
void use_player(SoundPlayer& player) {
player.play();
}
int main() {
diskman dm;
dm.cd = "Led Zeppelin IV (1971)";
use_player(dm); // -> Playing "Led Zeppelin IV (1971)"
ipod ip;
ip.mp3 = "Led Zeppelin - Black Dog.mp3";
use_player(ip); // -> Playing "Led Zeppelin - Black Dog.mp3"
}
template <typename SoundPlayer>
void use_player(SoundPlayer& player) {
player.play();
}
int main() {
diskman dm;
dm.cd = "Led Zeppelin IV (1971)";
use_player(dm); // -> Playing "Led Zeppelin IV (1971)"
ipod ip;
ip.mp3 = "Led Zeppelin - Black Dog.mp3";
use_player(ip); // -> Playing "Led Zeppelin - Black Dog.mp3"
}
template <typename SoundPlayer>
void use_player(SoundPlayer& player) {
player.play();
}
int main() {
diskman dm;
dm.cd = "Led Zeppelin IV (1971)";
use_player(dm); // -> Playing "Led Zeppelin IV (1971)"
ipod ip;
ip.mp3 = "Led Zeppelin - Black Dog.mp3";
use_player(ip); // -> Playing "Led Zeppelin - Black Dog.mp3"
}
template <typename SoundPlayer>
void use_player(SoundPlayer& player) {
player.play();
}
int main() {
diskman dm;
dm.cd = "Led Zeppelin IV (1971)";
use_player(dm); // -> Playing "Led Zeppelin IV (1971)"
ipod ip;
ip.mp3 = "Led Zeppelin - Black Dog.mp3";
use_player(ip); // -> Playing "Led Zeppelin - Black Dog.mp3"
}
dynamix::object
– just an empty object
dynamix::object sound_player; // just an empty dynamix::object
dynamix::mutate(sound_player)
.add<cd_reader>()
.add<headphones_output>();
sound_player.get<cd_reader>()->insert("Led Zeppelin IV (1971)");
// play is a message
play(sound_player); // cant have sound_player.play() :(
// -> Playing CD "Led Zeppelin IV (1971)" through headphones
dynamix::mutate(sound_player)
.remove<headphones_output>()
.add<speakers_output>();
play(sound_player);
// -> Playing CD "Led Zeppelin IV (1971)" THROUGH SPEAKERS
dynamix::object sound_player; // just an empty dynamix::object
dynamix::mutate(sound_player)
.add<cd_reader>()
.add<headphones_output>();
sound_player.get<cd_reader>()->insert("Led Zeppelin IV (1971)");
// play is a message
play(sound_player); // cant have sound_player.play() :(
// -> Playing CD "Led Zeppelin IV (1971)" through headphones
dynamix::mutate(sound_player)
.remove<headphones_output>()
.add<speakers_output>();
play(sound_player);
// -> Playing CD "Led Zeppelin IV (1971)" THROUGH SPEAKERS
dynamix::object sound_player; // just an empty dynamix::object
dynamix::mutate(sound_player)
.add<cd_reader>()
.add<headphones_output>();
sound_player.get<cd_reader>()->insert("Led Zeppelin IV (1971)");
// play is a message
play(sound_player); // cant have sound_player.play() :(
// -> Playing CD "Led Zeppelin IV (1971)" through headphones
dynamix::mutate(sound_player)
.remove<headphones_output>()
.add<speakers_output>();
play(sound_player);
// -> Playing CD "Led Zeppelin IV (1971)" THROUGH SPEAKERS
dynamix::object sound_player; // just an empty dynamix::object
dynamix::mutate(sound_player)
.add<cd_reader>()
.add<headphones_output>();
sound_player.get<cd_reader>()->insert("Led Zeppelin IV (1971)");
// play is a message
play(sound_player); // cant have sound_player.play() :(
// -> Playing CD "Led Zeppelin IV (1971)" through headphones
dynamix::mutate(sound_player)
.remove<headphones_output>()
.add<speakers_output>();
play(sound_player);
// -> Playing CD "Led Zeppelin IV (1971)" THROUGH SPEAKERS
dynamix::object sound_player; // just an empty dynamix::object
dynamix::mutate(sound_player)
.add<cd_reader>()
.add<headphones_output>();
sound_player.get<cd_reader>()->insert("Led Zeppelin IV (1971)");
// play is a message
play(sound_player); // cant have sound_player.play() :(
// -> Playing CD "Led Zeppelin IV (1971)" through headphones
dynamix::mutate(sound_player)
.remove<headphones_output>()
.add<speakers_output>();
play(sound_player);
// -> Playing CD "Led Zeppelin IV (1971)" THROUGH SPEAKERS
dynamix::object sound_player; // just an empty dynamix::object
dynamix::mutate(sound_player)
.add<cd_reader>()
.add<headphones_output>();
sound_player.get<cd_reader>()->insert("Led Zeppelin IV (1971)");
// play is a message
play(sound_player); // cant have sound_player.play() :(
// -> Playing CD "Led Zeppelin IV (1971)" through headphones
dynamix::mutate(sound_player)
.remove<headphones_output>()
.add<speakers_output>();
play(sound_player);
// -> Playing CD "Led Zeppelin IV (1971)" THROUGH SPEAKERS
Messages:
// Declare
DYNAMIX_MESSAGE_0(string, get_sound);
DYNAMIX_MESSAGE_0(void, play);
DYNAMIX_MESSAGE_2(int, foo, float, arg1, string, arg2);
// Define
DYNAMIX_DEFINE_MESSAGE(get_sound);
DYNAMIX_DEFINE_MESSAGE(play);
DYNAMIX_DEFINE_MESSAGE(foo);
We fully separate the interface from the implementation.
Message vs Method
Late binding and Smalltalk
struct Foo {
void bar();
};
Foo foo;
foo.bar(); // message
void Foo::bar() {} // method
Mixins:
DYNAMIX_DECLARE_MIXIN(cd_reader);
DYNAMIX_DECLARE_MIXIN(headphones_output);
// That’s all we need to mutate
class cd_reader {
public:
string get_sound() {
return _cd.empty() ? "silence" : ("CD " + cd);
}
void insert(const string& cd) {
_cd = cd;
}
string _cd;
};
DYNAMIX_DEFINE_MIXIN(cd_reader, get_sound_msg);
// ...
DYNAMIX_DEFINE_MIXIN(mp3_reader, get_sound_msg);
class headphones_output {
public:
void play() {
cout << "Playing " << get_sound(dm_this)
<< " through headphones\n";
}
};
DYNAMIX_DEFINE_MIXIN(headphones_output, play_msg);
class headphones_output {
public:
void play() {
cout << "Playing " << get_sound(dm_this)
<< " through headphones\n";
}
};
DYNAMIX_DEFINE_MIXIN(headphones_output, play_msg);
dm_this
is like self
: the owning objectMixQuest: github.com/iboB/mixquest
The library has found a niche in mobile games, which are less likely to sacrifice performance
DynaMix is a means to create a project's architecture rather than achieve its purpose.
... so it's not really suitable for:
std::function
C++ allows you to have great power with OOP. Make use of it!
... but don't abuse it
Borislav Stanimirov / ibob.github.io / @stanimirovb
DynaMix is here: github.com/ibob/dynamix/
Link to these slides: http://ibob.github.io/slides/dynamix-cppcon/
Slides license Creative Commons By 3.0