Last active
August 29, 2015 14:11
-
-
Save bzar/00765d257322cf77309b to your computer and use it in GitHub Desktop.
Algebraic datatype emulation in C++ (and the same in Rust)
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
#include <iostream> | |
#include <cassert> | |
#include <string> | |
// Algebraic type | |
enum ShapeType { CIRCLE, RECTANGLE, ZERO }; | |
struct Circle { int cx, cy, r; static const ShapeType type = CIRCLE; }; | |
struct Rectangle { int x, y, w, h; static const ShapeType type = RECTANGLE; }; | |
struct Zero { static const ShapeType type = ZERO; }; | |
struct Shape { | |
ShapeType const type; | |
Shape(Circle const& v) : type(CIRCLE), circle(v) {} | |
Shape(Rectangle const& v) : type(RECTANGLE), rectangle(v) {} | |
Shape(Zero const& v) : type(ZERO), zero(v) {} | |
union { | |
Circle circle; | |
Rectangle rectangle; | |
Zero zero; | |
}; | |
}; | |
// Generic matching | |
template<typename R, typename C, typename T, typename F , typename... Args> | |
R match(C const& c, T C::* tp, F f, Args... args) | |
{ | |
if(c.type == (c.*tp).type) | |
{ | |
return f(c.*tp); | |
} | |
else | |
{ | |
return match<R, C>(c, args...); | |
} | |
} | |
template<typename R, typename C, typename F> | |
R match(C const& c, F f) | |
{ | |
return f(); | |
} | |
template<typename R, typename C> | |
R match(C const& c) | |
{ | |
assert(false); | |
} | |
// Matching print | |
void print(Shape const& s) | |
{ | |
match<void>(s, | |
&Shape::circle, [](Circle const& c) { | |
std::cout << "Circle { " << c.cx << ", " << c.cy << ", " << c.r << " }" << std::endl; | |
}, | |
&Shape::rectangle, [](Rectangle const& r) { | |
std::cout << "Rectangle { " << r.x << ", " << r.y << ", " << r.w << ", " << r.h << " }" << std::endl; | |
}, | |
&Shape::zero, [](Zero const& z) { | |
std::cout << "Zero {}" << std::endl; | |
}); | |
} | |
int main(int argc, char** argv) { | |
// Do some shapes | |
Shape s1 = Circle {1,2,3}; | |
Shape s2 = Rectangle {1,2,3,4}; | |
Shape s3 = Zero {}; | |
// Call lambda on match | |
auto s2s = match<std::string>(s2, | |
&Shape::circle, [](Circle const& c) { return "Circle!"; }, | |
&Shape::rectangle, [](Rectangle const& r) { return "Rectangle!"; }, | |
[]() { return "Other"; }); | |
std::cout << s2s << std::endl; | |
// Print all | |
print(s1); | |
print(s2); | |
print(s3); | |
} |
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
enum Shape { | |
Circle(int, int, int), | |
Rectangle(int, int, int, int), | |
Zero | |
} | |
fn print(s: Shape) { | |
match s { | |
Circle(cx, cy, r) => println!("Circle {{ {}, {}, {} }}", cx, cy, r), | |
Rectangle(x, y, w, h) => println!("Rectangle {{ {}, {}, {}, {} }}", x, y, w, h), | |
Zero => println!("Zero") | |
} | |
} | |
fn main() { | |
let s1 = Circle(1,2,3); | |
let s2 = Rectangle(1,2,3,4); | |
let s3 = Zero; | |
let s2s = match s2 { | |
Circle(_, _, _) => "Circle!", | |
Rectangle(_, _, _, _) => "Rectangle!", | |
_ => "Other" | |
}; | |
println!("{}", s2s); | |
print(s1); | |
print(s2); | |
print(s3); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment