Skip to content

Commit 4a13f5c

Browse files
author
Eddie
committed
Listing 26: Switch
1 parent 83781b6 commit 4a13f5c

File tree

3 files changed

+331
-7
lines changed

3 files changed

+331
-7
lines changed

benchmark/cm/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
cmake_minimum_required(VERSION 3.8)
2+
3+
project(CMB)
4+
5+
set(CMAKE_CXX_STANDARD 17)
6+
7+
add_executable(
8+
cmb
9+
main.cpp
10+
)
11+
12+
target_include_directories(cmb PRIVATE "/home/eddie/git/zoo-cm/inc")
13+

benchmark/cm/main.cpp

Lines changed: 144 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <math.h>
55
#include <stdint.h>
66

7+
#include "variant.h"
8+
79
/* Some definitions necessary, they are not in the listings */
810
using f32 = float;
911
constexpr f32 Pi32 = M_PI;
@@ -169,15 +171,110 @@ using P = zoo::Policy<void *[2], ShapeAffordance, zoo::Destroy, zoo::Move>;
169171
using Shape = zoo::AnyContainer<P>;
170172

171173
static_assert(24 == sizeof(Shape));
174+
struct VariantShape: zoo::Variant<square, rectangle, triangle, circle> {
175+
using Base = zoo::Variant<square, rectangle, triangle, circle>;
176+
using Base::Base;
177+
178+
f32 area() {
179+
auto rv = zoo::visit<f32>(
180+
[](auto &s) {
181+
if constexpr (std::is_same_v<zoo::BadVariant &, decltype(s)>) {
182+
return 0.0;
183+
} else {
184+
return s.AreaNP();
185+
}
186+
},
187+
*this
188+
);
189+
return rv;
190+
}
191+
};
172192

173-
auto zooTraverse(std::vector<Shape> &s) {
193+
template<typename E>
194+
auto zooTraverse(std::vector<E> &s) {
174195
f32 rv = 0;
175196
for(auto &shape: s) {
176197
rv += shape.area();
177198
}
178199
return rv;
179200
}
180201

202+
/* ========================================================================
203+
LISTING 25
204+
======================================================================== */
205+
206+
enum shape_type : u32
207+
{
208+
Shape_Square,
209+
Shape_Rectangle,
210+
Shape_Triangle,
211+
Shape_Circle,
212+
213+
Shape_Count,
214+
};
215+
216+
struct shape_union
217+
{
218+
shape_type Type;
219+
f32 Width;
220+
f32 Height;
221+
};
222+
223+
f32 GetAreaSwitch(shape_union Shape)
224+
{
225+
f32 Result = 0.0f;
226+
227+
switch(Shape.Type)
228+
{
229+
case Shape_Square: {Result = Shape.Width*Shape.Width;} break;
230+
case Shape_Rectangle: {Result = Shape.Width*Shape.Height;} break;
231+
case Shape_Triangle: {Result = 0.5f*Shape.Width*Shape.Height;} break;
232+
case Shape_Circle: {Result = Pi32*Shape.Width*Shape.Width;} break;
233+
234+
case Shape_Count: {} break;
235+
}
236+
237+
return Result;
238+
}
239+
240+
/* ========================================================================
241+
LISTING 26
242+
======================================================================== */
243+
244+
f32 TotalAreaSwitch(u32 ShapeCount, shape_union *Shapes)
245+
{
246+
f32 Accum = 0.0f;
247+
248+
for(u32 ShapeIndex = 0; ShapeIndex < ShapeCount; ++ShapeIndex)
249+
{
250+
Accum += GetAreaSwitch(Shapes[ShapeIndex]);
251+
}
252+
253+
return Accum;
254+
}
255+
256+
f32 TotalAreaSwitch4(u32 ShapeCount, shape_union *Shapes)
257+
{
258+
f32 Accum0 = 0.0f;
259+
f32 Accum1 = 0.0f;
260+
f32 Accum2 = 0.0f;
261+
f32 Accum3 = 0.0f;
262+
263+
ShapeCount /= 4;
264+
while(ShapeCount--)
265+
{
266+
Accum0 += GetAreaSwitch(Shapes[0]);
267+
Accum1 += GetAreaSwitch(Shapes[1]);
268+
Accum2 += GetAreaSwitch(Shapes[2]);
269+
Accum3 += GetAreaSwitch(Shapes[3]);
270+
271+
Shapes += 4;
272+
}
273+
274+
f32 Result = (Accum0 + Accum1 + Accum2 + Accum3);
275+
return Result;
276+
}
277+
181278
int main(int argc, const char *argv[]) {
182279
std::random_device rd;
183280
std::mt19937 mersenne(rd());
@@ -208,20 +305,60 @@ int main(int argc, const char *argv[]) {
208305
}
209306
return
210307
benchmark(
211-
zooTraverse,
308+
zooTraverse<Shape>,
212309
shapes
213310
);
214311
};
215-
auto report = [](auto duration) {
312+
auto zooVariant = [](auto &&m) {
313+
std::vector<VariantShape> shapes;
314+
std::uniform_int_distribution sti(0, 2);
315+
std::uniform_real_distribution<float> rd(0.00001, 1.2);
316+
for(int i = Count; i--; ) {
317+
auto shapeTypeIndex = sti(m);
318+
switch(shapeTypeIndex) {
319+
case 0: shapes.emplace_back(std::in_place_index<0>, square(rd(m))); break;
320+
case 1: shapes.emplace_back(std::in_place_index<1>, rectangle(rd(m), rd(m))); break;
321+
case 2: shapes.emplace_back(std::in_place_index<2>, triangle(rd(m), rd(m))); break;
322+
case 3: shapes.emplace_back(std::in_place_index<3>, circle(rd(m))); break;
323+
default: throw std::runtime_error("Impossible shape type index");
324+
}
325+
}
326+
return
327+
benchmark(
328+
zooTraverse<VariantShape>,
329+
shapes
330+
);
331+
};
332+
auto CaseyMuratoriUnion = [](auto &&m) {
333+
std::vector<shape_union> shapes;
334+
std::uniform_int_distribution sti(0, 2);
335+
std::uniform_real_distribution<float> rd(0.00001, 1.2);
336+
for(int i = Count; i--; ) {
337+
auto shapeTypeIndex = sti(m);
338+
shape_union element = { static_cast<shape_type>(shapeTypeIndex), rd(m), rd(m) };
339+
shapes.push_back(element);
340+
}
341+
return
342+
benchmark(
343+
TotalAreaSwitch,
344+
shapes.size(),
345+
shapes.data()
346+
);
347+
};
348+
auto report = [](auto duration, auto txt) {
216349
auto nanos = toNanoseconds(duration);
217350
constexpr auto Cycle = 1/3.6;
218-
std::cout << nanos << ' ' << nanos/double(Count) << ' ' << nanos/Cycle/Count << ' ' << sideEffect << std::endl;
351+
std::cout << nanos << ' ' << nanos/double(Count) << ' ' << nanos/Cycle/Count << ' ' << sideEffect << ' ' << txt << std::endl;
219352
};
220353
auto zte1 = zooTypeErasure(mersenne);
221354
auto durationVI = virtualInheritance(mersenne);
355+
auto zooVar = zooVariant(mersenne);
356+
auto cmSwitch = CaseyMuratoriUnion(mersenne);
222357
auto zte2 = zooTypeErasure(mersenne);
223-
report(zte1);
224-
report(durationVI);
225-
report(zte2);
358+
report(zte1, "ZTE 1");
359+
report(durationVI, "Virtual + inheritance");
360+
report(zooVar, "Zoo variant");
361+
report(cmSwitch, "Casey Muratori's SWITCH");
362+
report(zte2, "ZTE 2");
226363
return 0;
227364
}

benchmark/cm/variant.h

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#include <tuple>
2+
// provides std::tuple_element to be able to index a pack of types,
3+
// indirectly includes type traits and utility
4+
#include <new>
5+
6+
namespace zoo {
7+
8+
template<typename T>
9+
void swap(T &t1, T &t2) {
10+
T temporary{std::move(t1)};
11+
t1 = std::move(t2);
12+
t2 = std::move(temporary);
13+
}
14+
15+
struct Maxer {
16+
int v_;
17+
constexpr Maxer(int v): v_{v} {}
18+
constexpr operator int() const { return v_; }
19+
constexpr Maxer operator*(Maxer o) const { return v_ < o.v_ ? o.v_ : v_; }
20+
};
21+
22+
template<int... Ints>
23+
constexpr auto Largest = int{(Maxer{0} * ... * Maxer(Ints))};
24+
25+
template<typename... Ts>
26+
constexpr auto LargestSize = Largest<sizeof(Ts)...>;
27+
28+
template<typename... Ts>
29+
constexpr auto LargestAlignment = Largest<alignof(Ts)...>;
30+
31+
static_assert(sizeof(void *) == LargestSize<int, float, char, void *>, "");
32+
static_assert(alignof(short) == LargestAlignment<short, char, char[4]>, "");
33+
34+
struct BadVariant {};
35+
36+
template<int Index, typename... Ts>
37+
using TypeAtIndex =
38+
typename std::tuple_element<Index, std::tuple<Ts..., BadVariant>>::type;
39+
40+
template<typename R, typename Visitor, typename Var>
41+
R visit(Visitor &&visitor, Var &&value);
42+
43+
template<typename... Ts>
44+
struct Variant {
45+
constexpr static auto Count = sizeof...(Ts);
46+
constexpr static auto Size = LargestSize<Ts...>;
47+
constexpr static auto Alignment = LargestAlignment<Ts...>;
48+
constexpr static auto NTMC =
49+
std::conjunction<std::is_nothrow_move_constructible<Ts>...>::value;
50+
51+
alignas(Alignment) char space_[Size];
52+
int typeSwitch_ = Count;
53+
54+
Variant() {}
55+
56+
Variant(const Variant &v): typeSwitch_{v.typeSwitch_} {
57+
visit<void>(
58+
[&](const auto &c) {
59+
using Source = std::decay_t<decltype(c)>;
60+
new(as<Source>()) Source{c};
61+
},
62+
v
63+
);
64+
}
65+
66+
Variant(Variant &&v) noexcept(NTMC): typeSwitch_{v.typeSwitch_} {
67+
visit<void>(
68+
[&](auto &&m) {
69+
using Source = std::decay_t<decltype(m)>;
70+
new(as<Source>()) Source{std::move(m)};
71+
},
72+
v
73+
);
74+
v.typeSwitch_ = Count;
75+
}
76+
77+
template<std::size_t Ndx, typename... Args>
78+
Variant(std::in_place_index_t<Ndx>, Args &&...args): typeSwitch_{Ndx} {
79+
using Held = TypeAtIndex<Ndx, Ts...>;
80+
new(as<Held>()) Held{std::forward<Args>(args)...};
81+
}
82+
83+
Variant &operator=(const Variant &model) {
84+
Variant copy{model};
85+
swap(*this, copy);
86+
return *this;
87+
}
88+
89+
Variant &operator=(Variant &&other) noexcept(NTMC) {
90+
destroy();
91+
new(this) Variant{std::move(other)};
92+
return *this;
93+
}
94+
95+
~Variant() { destroy(); }
96+
97+
void reset() {
98+
destroy();
99+
typeSwitch_ = Count;
100+
}
101+
102+
template<typename T>
103+
T *as() noexcept { return reinterpret_cast<T *>(space_); }
104+
105+
template<typename T>
106+
const T *as() const noexcept {
107+
return const_cast<Variant *>(this)->as<T>();
108+
}
109+
110+
private:
111+
void destroy() {
112+
visit<void>(
113+
[](auto &who) {
114+
using Type = std::decay_t<decltype(who)>;
115+
(&who)->~Type();
116+
},
117+
*this
118+
);
119+
}
120+
121+
};
122+
123+
template<int Index, typename... Ts>
124+
auto get(Variant<Ts...> &v) ->
125+
TypeAtIndex<Index, std::tuple<Ts..., BadVariant>> &
126+
{
127+
return v.as<TypeAtIndex<Index, std::tuple<Ts..., BadVariant>>();
128+
}
129+
130+
template<
131+
typename R, typename Visitor, typename V,
132+
int Current, typename Head, typename... Tail
133+
>
134+
struct Visit_impl {
135+
static R execute(Visitor &&visitor, V &&v, int typeSwitch) {
136+
switch(typeSwitch) {
137+
case Current: return visitor(*v.template as<Head>());
138+
default:
139+
return
140+
Visit_impl<
141+
R, Visitor, V, Current + 1, Tail...
142+
>::execute(
143+
std::forward<Visitor>(visitor),
144+
std::forward<V>(v),
145+
typeSwitch
146+
);
147+
}
148+
}
149+
};
150+
151+
template<typename R, typename Visitor, typename V, int Current>
152+
struct Visit_impl<R, Visitor, V, Current, BadVariant> {
153+
static R execute(Visitor &&visitor, V &&v, int) {
154+
return visitor(*v.template as<BadVariant>());
155+
}
156+
};
157+
158+
template<typename R, typename Visitor, typename Var, typename... Types>
159+
R visit_injected(Visitor &&visitor, Var &&v, const Variant<Types...> *) {
160+
return Visit_impl<R, Visitor, Var, 0, Types..., BadVariant>::execute(
161+
std::forward<Visitor>(visitor),
162+
std::forward<Var>(v),
163+
v.typeSwitch_
164+
);
165+
}
166+
167+
template<typename R, typename Visitor, typename Var>
168+
R visit(Visitor &&visitor, Var &&value) {
169+
return visit_injected<R>(
170+
std::forward<Visitor>(visitor), std::forward<Var>(value), &value
171+
);
172+
}
173+
174+
}

0 commit comments

Comments
 (0)