Skip to content

Instantly share code, notes, and snippets.

@quantumelixir
Last active December 4, 2017 20:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save quantumelixir/b25cfb5f29c63b33fd4ed407bdcfb40d to your computer and use it in GitHub Desktop.
Save quantumelixir/b25cfb5f29c63b33fd4ed407bdcfb40d to your computer and use it in GitHub Desktop.
Alternative Runtime Polymorphism
// 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;
}
@quantumelixir
Copy link
Author

quantumelixir commented Dec 4, 2017

moving! 
moving! 
moving! 
moving! 
moving! 
moving! 
<document>
  0
  1
  hello world!
  <document>
    0
    1
    hello world!
  </document>
  myclass!
  2
</document>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment