-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathshow_concept.cpp
148 lines (114 loc) · 3.71 KB
/
show_concept.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <type_traits>
#include "../tc_concept.hpp"
// Demonstrates overload selection by means of requirements.
//
// Requirements allow one to easily match all the types NOT implementing
// a typeclass.
template<class T>
struct Show {
static std::string show(T const &) = delete;
};
// Show int
template<>
TC_INSTANCE(Show<int>, {
static std::string show(int const & x) {
return ("int"+std::to_string(x));
}
});
struct Foo{};
// Show Foo
template<>
TC_INSTANCE(Show<Foo>, {
static std::string show(Foo const & x) {
return "Foo";
}
});
// Show a => Show [a]
template<class T> requires Instance<Show<T>>
TC_INSTANCE(Show<std::vector<T>>, {
static std::string show(std::vector<T> const & xs) {
std::string res = "[";
if (xs.size() > 0) {
for (size_t i = 0; i < xs.size() - 1; i++) {
res += tc_impl_t<Show<T>>::show(xs[i]);
res += ",";
}
res += tc_impl_t<Show<T>>::show(xs[xs.size()-1]);
}
res += "]";
return res;
}
});
// print Show instances
template<class T> requires Instance<Show<T>>
std::ostream & operator<<(std::ostream & os, T const & x) {
// we are using std::operator<< explicitly to avoid ambigous overload
std::operator<<(os,tc_impl_t<Show<T>>::show(x));
return os;
}
// print non-Show instances
template<class T>
std::ostream & operator<<(std::ostream & os, T const & x) {
// we are using std::operator<< explicitly to avoid ambigous overload
std::operator<<(os, "<UNSHOWABLE>");
return os;
}
// Emulation of rust-like dynamic traits.
//
// Unfortunately, I do not know if an adequate analogue of
// full-blown haskell-like existential types is possible.
// If the typeclass being dynamized has more than one type parameter
// then the DynClass may be a template having one parameter less.
struct DynShow {
virtual std::string show_me() const = 0;
virtual ~DynShow() {} // Do not forget this! We usually hide DynShow
// behind some smart pointer.
};
// We have to provide some Show instance to DynShow values.
template<>
TC_INSTANCE(Show<DynShow>, {
static std::string show(DynShow const & x) {
return x.show_me();
}
});
template<class T>
struct DynShowWrapper: DynShow {
T self;
std::string show_me() const {
return tc_impl_t<Show<T>>::show(self);
}
DynShowWrapper(T x): self(x) {}
};
// type-inferring constructor
template<class T>
std::unique_ptr<DynShow> to_show(T x) {
return std::make_unique<DynShowWrapper<T>>(x);
}
// and an instance to support boxed Show values:
// in rust this would be: impl<T: Show+?Sized> Show for Box<T>
template<class T>
TC_INSTANCE(Show<std::unique_ptr<T>>, {
static std::string show(std::unique_ptr<T> const & ptr) {
return tc_impl_t<Show<T>>::show(*ptr);
}
});
int main() {
std::cout << 123 << std::endl; // old int overload (more specific than template overload)
std::cout << 123. << std::endl; // old double overload
std::cout << Foo() << std::endl; // new and the only overload
std::cout << std::vector<int>{1,2,3} << std::endl; // new Show instance
// A correct overload is selected.
std::cout << std::vector<double>{1,2,3} << std::endl;
// And now enter the dynamic Show trait:
std::cout << to_show(3) << std::endl;
// Now we demonstrate an analogue of rusty Vec<Box<dyn Show>>.
std::vector<std::unique_ptr<DynShow>> some_showables;
some_showables.push_back(to_show(1));
some_showables.push_back(to_show(Foo()));
some_showables.push_back(to_show(2));
std::cout << some_showables << std::endl;
}