Last active
December 4, 2017 20:48
-
-
Save quantumelixir/b25cfb5f29c63b33fd4ed407bdcfb40d to your computer and use it in GitHub Desktop.
Alternative Runtime Polymorphism
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This code presents an alternate style of runtime polymorphism | |
// fleshed out in Sean Parent's talk on the subject | |
// (https://www.youtube.com/watch?v=QGcVXgEVMJg). The goal is to treat | |
// different collections of types polymorphically based on their uses | |
// in different contexts without requiring client-side inheritance of | |
// those types depending on each of the uses. | |
#include <iostream> | |
#include <memory> | |
#include <string> | |
#include <vector> | |
// A default implementation of draw for all objects that can be | |
// streamed to std::ostream. | |
template <typename T> | |
void draw(const T &object, std::ostream &os, int padding) { | |
os << std::string(padding, ' ') << object << std::endl; | |
} | |
// Type unifying all drawable objects. | |
struct ObjectType { | |
template <class Model> | |
ObjectType(Model x) | |
: self_(std::make_shared<ModelType<Model>>(std::move(x))) {} | |
// Q: Why does std::forward<Model>(x) give compiler errors here (in | |
// combination with ObjectType(Model&& x))? | |
void draw(std::ostream &os, int padding) const { self_->draw(os, padding); } | |
// Define drawable objects' interfaces. | |
struct ConceptType { | |
virtual ~ConceptType() = default; | |
virtual void draw(std::ostream &os, int padding) const = 0; | |
}; | |
// And inherit all requisite types from the interface type. | |
template <class Model> struct ModelType : ConceptType { | |
ModelType(Model &&x) : value_(x) { std::cout << "moving! " << std::endl; } | |
ModelType(const Model &x) : value_(x) { | |
std::cout << "copying! " << std::endl; | |
} | |
// Delegate to the default draw function. | |
void draw(std::ostream &os, int padding) const override { | |
::draw(value_, os, padding); | |
} | |
private: | |
Model value_; | |
}; | |
std::shared_ptr<const ConceptType> self_; | |
}; | |
// Define the document as a collection of objects. | |
using DocumentType = std::vector<ObjectType>; | |
// And specialize the document's draw method. | |
template <> | |
void draw(const DocumentType &document, std::ostream &os, int padding) { | |
os << std::string(padding, ' ') << "<document>" << std::endl; | |
for (auto &object : document) { | |
object.draw(os, padding + 2); | |
} | |
os << std::string(padding, ' ') << "</document>" << std::endl; | |
} | |
// Add a new type to the list of drawable objects. | |
class MyClass {}; | |
template <> void draw(const MyClass &myclass, std::ostream &os, int padding) { | |
os << std::string(padding, ' ') << "myclass!" << std::endl; | |
} | |
// Client code is cleaner. | |
int main() { | |
DocumentType document; | |
document.emplace_back(0); | |
document.emplace_back(1); | |
document.emplace_back(std::string("hello world") + "!"); | |
document.emplace_back(document); | |
document.emplace_back(MyClass{}); | |
document.emplace_back(2); | |
draw(document, std::cout, 0); | |
return 0; | |
} |
Author
quantumelixir
commented
Dec 4, 2017
•
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment