Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

position velocity acceleration mass ← obj1 ← obj2 Björn Fahller Cache Friendly + Functional + Ranges = ❤️

Slide 3

Slide 3 text

https://jobs.netinsight.net/departments/development

Slide 4

Slide 4 text

https://jobs.netinsight.net/departments/development

Slide 5

Slide 5 text

https://jobs.netinsight.net/departments/development

Slide 6

Slide 6 text

https://jobs.netinsight.net/departments/development

Slide 7

Slide 7 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 7/183 STOP WAR Inspiration https://youtu.be/rX0ItVEVjHc Mike Acton – CppCon Keynote 2014 Data Oriented Design https://youtu.be/b9hNKFj5R3Y Mathieu Ropert – MeetingC++ 2023 Data Storage in Entity Component System

Slide 8

Slide 8 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 8/183 STOP WAR struct S { int x; int y; int z; int d }; y x z d y x z d y x z d y x z d y x z d y x z d y x z d

Slide 9

Slide 9 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 9/183 STOP WAR struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d

Slide 10

Slide 10 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 10/183 STOP WAR struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access

Slide 11

Slide 11 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 11/183 STOP WAR struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access access

Slide 12

Slide 12 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 12/183 STOP WAR struct S { int x; int y; int z; int d }; read read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access access

Slide 13

Slide 13 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 13/183 STOP WAR struct S { int x; int y; int z; int d }; read read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access access access

Slide 14

Slide 14 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 14/183 STOP WAR struct S { int x; int y; int z; int d }; read read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access access access access

Slide 15

Slide 15 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 15/183 STOP WAR struct S { int x; int y; int z; int d }; read read read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access access access access

Slide 16

Slide 16 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 16/183 STOP WAR struct S { int x; int y; int z; int d }; read read read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access access access access access

Slide 17

Slide 17 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 17/183 STOP WAR struct S { int x; int y; int z; int d }; read read read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access access access access access access

Slide 18

Slide 18 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 18/183 STOP WAR struct S { int x; int y; int z; int d }; read read read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access access access access access access access

Slide 19

Slide 19 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 19/183 STOP WAR struct S { int x; int y; int z; int d }; read read read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access access access access access access access access

Slide 20

Slide 20 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 20/183 STOP WAR vector of structs / structs of vectors struct S { int x; int y; int z; int d }; y x z d y x z d y x z d y x z d y x z d y x z d y x z d x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d

Slide 21

Slide 21 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 21/183 STOP WAR vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d

Slide 22

Slide 22 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 22/183 STOP WAR vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d

Slide 23

Slide 23 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 23/183 STOP WAR vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access

Slide 24

Slide 24 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 24/183 STOP WAR vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access

Slide 25

Slide 25 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 25/183 STOP WAR vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access

Slide 26

Slide 26 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 26/183 STOP WAR vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access

Slide 27

Slide 27 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 27/183 STOP WAR vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access access

Slide 28

Slide 28 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 28/183 STOP WAR vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access access access

Slide 29

Slide 29 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 29/183 STOP WAR vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access access access access

Slide 30

Slide 30 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 30/183 STOP WAR read vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access access access access

Slide 31

Slide 31 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 31/183 STOP WAR read vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access access access access access

Slide 32

Slide 32 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 32/183 STOP WAR read vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access access access access access access

Slide 33

Slide 33 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 33/183 STOP WAR read vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access access access access access access access

Slide 34

Slide 34 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 34/183 STOP WAR read vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access access access access access access access

Slide 35

Slide 35 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 35/183 STOP WAR read vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access access access access access access access

Slide 36

Slide 36 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 36/183 STOP WAR read vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access access access access access access access

Slide 37

Slide 37 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 37/183 STOP WAR read vector of structs / structs of vectors struct S { int x; int y; int z; int d }; read y x z d y x z d y x z d y x z d y x z d y x z d y x z d access x x x x x x x x x x x x x x x x x x x x x x x x y y y y y y y y y y y y y y y y y y y y y y y y z z z z z z z z z z z z z z z z z z z z z z z z d d d d d d d d d d d d d d d d d d d d d d d d access access access access access access access access access access

Slide 38

Slide 38 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 38/183 STOP WAR Live Demo vector_of_structs vs struct_of_vectors

Slide 39

Slide 39 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 39/183 STOP WAR Observations In this exampe, struct of vectors...

Slide 40

Slide 40 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 40/183 STOP WAR Observations In this exampe, struct of vectors... … has way fewer memory accesses

Slide 41

Slide 41 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 41/183 STOP WAR Observations In this exampe, struct of vectors... … has way fewer memory accesses … has way fewer cache misses

Slide 42

Slide 42 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 42/183 STOP WAR Observations In this exampe, struct of vectors... … has way fewer memory accesses … has way fewer cache misses … has slightly fewer instructions

Slide 43

Slide 43 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 43/183 STOP WAR Observations In this exampe, struct of vectors... … has way fewer memory accesses … has way fewer cache misses … has slightly fewer instructions … is significantly faster

Slide 44

Slide 44 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 44/183 STOP WAR Observations In this exampe, struct of vectors... … has way fewer memory accesses … has way fewer cache misses … has slightly fewer instructions … is significantly faster … is horrible code to work with

Slide 45

Slide 45 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 45/183 STOP WAR Observations In this exampe, struct of vectors... … has way fewer memory accesses … has way fewer cache misses … has slightly fewer instructions … is significantly faster … is horrible code to work with Can we make it easier to work with and keep the performance?

Slide 46

Slide 46 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 46/183 STOP WAR std::tuple<> is cool! template class table { public: using row = std::tuple; row operator[](size_t i) { auto access = [&](std::index_sequence) { return row{std::get(data_)[i]...}; }; return std::invoke(access, indexes); } ... private: static constexpr std::index_sequence_for indexes{}; std::tuple...> data_; };

Slide 47

Slide 47 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 47/183 STOP WAR std::tuple<> is cool! template class table { public: using row = std::tuple; row operator[](size_t i) { auto access = [&](std::index_sequence) { return row{std::get(data_)[i]...}; }; return std::invoke(access, indexes); } ... private: static constexpr std::index_sequence_for indexes{}; std::tuple...> data_; }; Storage is a tuple of vectors of Ts

Slide 48

Slide 48 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 48/183 STOP WAR std::tuple<> is cool! template class table { public: using row = std::tuple; row operator[](size_t i) { auto access = [&](std::index_sequence) { return row{std::get(data_)[i]...}; }; return std::invoke(access, indexes); } ... private: static constexpr std::index_sequence_for indexes{}; std::tuple...> data_; }; Access a row as a tuple of references to the elements

Slide 49

Slide 49 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 49/183 STOP WAR std::tuple<> is cool! template class table { public: using row = std::tuple; row operator[](size_t i) { auto access = [&](std::index_sequence) { return row{std::get(data_)[i]...}; }; return std::invoke(access, indexes); } ... private: static constexpr std::index_sequence_for indexes{}; std::tuple...> data_; }; Build by dereferencing the same offset in each vector in the tuple

Slide 50

Slide 50 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 50/183 STOP WAR table

Slide 51

Slide 51 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 51/183 STOP WAR template class table { public: using row = std::tuple; void push_back(Ts... ts); void pop_back(); row operator[](size_t i); row back(); void reserve(size_t size); bool empty() const; size_t size() const private: ... }; table

Slide 52

Slide 52 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 52/183 STOP WAR template class table { public: using row = std::tuple; void push_back(Ts... ts); void pop_back(); row operator[](size_t i); row back(); void reserve(size_t size); bool empty() const; size_t size() const private: ... }; template void drop_if(Table& t, P predicate) { size_t i = 0; while (i < t.size()) { if (predicate(t[i])) { t[i] = std::move(v.back()); t.pop_back(); }  else { ++i; } } } table

Slide 53

Slide 53 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 53/183 STOP WAR template class table { public: using row = std::tuple; void push_back(Ts... ts); void pop_back(); row operator[](size_t i); row back(); void reserve(size_t size); bool empty() const; size_t size() const private: ... }; template void drop_if(Table& t, P predicate) { size_t i = 0; while (i < t.size()) { if (predicate(t[i])) { t[i] = std::move(v.back()); t.pop_back(); }  else { ++i; } } } while (!values.empty()) { drop_if(values, [&](auto r) { auto [x,y,z,d] = r; return z < start; });         ++iter;         start += 10;     } } table

Slide 54

Slide 54 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 54/183 STOP WAR Live Demo templated

Slide 55

Slide 55 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 55/183 STOP WAR Observations In this exampe...

Slide 56

Slide 56 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 56/183 STOP WAR Observations In this exampe... … the ergonomics is roughly the same as with vector of structs

Slide 57

Slide 57 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 57/183 STOP WAR Observations In this exampe... … the ergonomics is roughly the same as with vector of structs … performance is the same as with struct of vectors

Slide 58

Slide 58 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 58/183 STOP WAR Observations In this exampe... … the ergonomics is roughly the same as with vector of structs … performance is the same as with struct of vectors But, we also need…

Slide 59

Slide 59 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 59/183 STOP WAR Observations In this exampe... … the ergonomics is roughly the same as with vector of structs … performance is the same as with struct of vectors But, we also need… … a stable row-ID to lookup objects

Slide 60

Slide 60 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 60/183 STOP WAR Observations In this exampe... … the ergonomics is roughly the same as with vector of structs … performance is the same as with struct of vectors But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges

Slide 61

Slide 61 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 61/183 STOP WAR Observations In this exampe... … the ergonomics is roughly the same as with vector of structs … performance is the same as with struct of vectors But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want...

Slide 62

Slide 62 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 62/183 STOP WAR Observations In this exampe... … the ergonomics is roughly the same as with vector of structs … performance is the same as with struct of vectors But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns

Slide 63

Slide 63 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 63/183 STOP WAR Observations In this exampe... … the ergonomics is roughly the same as with vector of structs … performance is the same as with struct of vectors But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements

Slide 64

Slide 64 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 64/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c d e f h g A B C D E F H G i I 0 1 2 3 4 5 7 6 8 data index first_free = 9

Slide 65

Slide 65 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 65/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c d e f h g A B C D E F H G i I 0 1 2 3 4 5 7 6 8 data index erase first_free = 9

Slide 66

Slide 66 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 66/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c d e f h g A B C D E F H G i I 0 1 2 3 4 5 7 6 8 data index erase first_free = 9

Slide 67

Slide 67 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 67/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c d e f h g A B C D E F H G 0 1 2 3 4 5 7 6 8 data index erase i I first_free = 9

Slide 68

Slide 68 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 68/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c d e f h g A B C D E F H G 0 1 2 3 4 5 7 6 8 data index erase i I 3 first_free = 9

Slide 69

Slide 69 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 69/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c d e f h g A B C D E F H G 0 1 2 3 4 5 7 6 8 data index erase i I 3 first_free = 9

Slide 70

Slide 70 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 70/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c d e f h g A B C D E F H G 0 1 2 3 4 5 7 6 8 data index erase i I 3 first_free = 9 9

Slide 71

Slide 71 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 71/183 STOP WAR first_free = 3 Stable row ID 0 1 2 3 4 5 7 6 8 a b c d e f h g A B C D E F H G 0 1 2 3 4 5 7 6 8 data index erase i I 3 9

Slide 72

Slide 72 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 72/183 STOP WAR first_free = 3 Stable row ID 0 1 2 3 4 5 7 6 8 a b c d e f h g A B C D E F H G 0 1 2 3 4 5 7 6 8 data index erase i I 3 Lookup row_id 8 9

Slide 73

Slide 73 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 73/183 STOP WAR first_free = 3 Stable row ID 0 1 2 3 4 5 7 6 8 a b c d e f h g A B C D E F H G 0 1 2 3 4 5 7 6 8 data index erase i I 3 Lookup row_id 8 9

Slide 74

Slide 74 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 74/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C I E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index first_free = 3

Slide 75

Slide 75 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 75/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C I E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index find ‘i’ first_free = 3

Slide 76

Slide 76 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 76/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C I E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index find ‘i’ first_free = 3

Slide 77

Slide 77 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 77/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C I E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index find ‘i’ first_free = 3

Slide 78

Slide 78 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 78/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C I E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index find ‘i’ ‘i’ is at row_id 8, which is stored at offset 3 in the columns. first_free = 3

Slide 79

Slide 79 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 79/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C J E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index first_free = 3

Slide 80

Slide 80 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 80/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C J E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index insert ‘k’, ‘K’ first_free = 3

Slide 81

Slide 81 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 81/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C J E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index insert ‘k’, ‘K’ first_free = 3

Slide 82

Slide 82 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 82/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C J E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index insert ‘k’, ‘K’ first_free = 3 k K 3

Slide 83

Slide 83 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 83/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C J E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index insert ‘k’, ‘K’ first_free = 3 k K 3

Slide 84

Slide 84 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 84/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C J E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index insert ‘k’, ‘K’ first_free = 3 k K 3

Slide 85

Slide 85 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 85/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C J E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index insert ‘k’, ‘K’ first_free = 9 k K 3

Slide 86

Slide 86 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 86/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C J E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index insert ‘k’, ‘K’ first_free = 9 k K 3 8

Slide 87

Slide 87 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 87/183 STOP WAR Stable row ID 0 1 2 3 4 5 7 6 8 a b c i e f h g A B C J E F H G 0 1 2 9 4 5 7 6 3 data index 0 1 2 8 4 5 7 6 reverse index insert ‘k’, ‘K’ first_free = 9 k K 3 8 Return row_id == 3, which refers to the new element

Slide 88

Slide 88 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 88/183 STOP WAR Stable row ID template class table { public: using row = std::tuple; struct row_id { size_t offset; }; row operator[](row_id id) { auto offset = index_[id.offset].offset; auto access = [&](std::index_sequence) { return row{std::get(data_)[offset]...}; }; return std::invoke(access, indexes); } private: static constexpr auto indexes = std::index_sequence_for{}; std::tuple...> data_; std::vector reverse_index_; std::vector index_; row_id first_free_ = {0}; };

Slide 89

Slide 89 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 89/183 STOP WAR Stable row ID template class table { public: using row = std::tuple; struct row_id { size_t offset; }; row operator[](row_id id) { auto offset = index_[id.offset].offset; auto access = [&](std::index_sequence) { return row{std::get(data_)[offset]...}; }; return std::invoke(access, indexes); } private: static constexpr auto indexes = std::index_sequence_for{}; std::tuple...> data_; std::vector reverse_index_; std::vector index_; row_id first_free_ = {0}; }; Translate row_id to offset in the vectors

Slide 90

Slide 90 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 90/183 STOP WAR Stable row ID template class table { public: using row = std::tuple; struct row_id { size_t offset; }; row operator[](row_id id) { auto offset = index_[id.offset].offset; auto access = [&](std::index_sequence) { return row{std::get(data_)[offset]...}; }; return std::invoke(access, indexes); } private: static constexpr auto indexes = std::index_sequence_for{}; std::tuple...> data_; std::vector reverse_index_; std::vector index_; row_id first_free_ = {0}; }; The rest of the access as before

Slide 91

Slide 91 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 91/183 STOP WAR Stable row ID template class table { public: using row = std::tuple; struct row_id { size_t offset; }; row operator[](row_id id) { auto offset = index_[id.offset].offset; auto access = [&](std::index_sequence) { return row{std::get(data_)[offset]...}; }; return std::invoke(access, indexes); } private: static constexpr auto indexes = std::index_sequence_for{}; std::tuple...> data_; std::vector reverse_index_; std::vector index_; row_id first_free_ = {0}; };

Slide 92

Slide 92 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 92/183 STOP WAR Stable row ID template class table { public: using row = std::tuple; struct row_id { size_t offset; }; row operator[](row_id id) { auto offset = index_[id.offset].offset; auto access = [&](std::index_sequence) { return row{std::get(data_)[offset]...}; }; return std::invoke(access, indexes); } private: static constexpr auto indexes = std::index_sequence_for{}; std::tuple...> data_; std::vector reverse_index_; std::vector index_; row_id first_free_ = {0}; }; template class table { public: using row = std::tuple; struct row_id { size_t offset; }; row_id push_back(Ts... ts); void pop_back(); row operator[](row_id i); row back(); void erase(row_id i); void reserve(size_t size); bool empty() const; size_t size() const; bool has_row(row_id i) const; private: ... };

Slide 93

Slide 93 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 93/183 STOP WAR Stable row ID template class table { public: using row = std::tuple; struct row_id { size_t offset; }; row operator[](row_id id) { auto offset = index_[id.offset].offset; auto access = [&](std::index_sequence) { return row{std::get(data_)[offset]...}; }; return std::invoke(access, indexes); } private: static constexpr auto indexes = std::index_sequence_for{}; std::tuple...> data_; std::vector reverse_index_; std::vector index_; row_id first_free_ = {0}; }; template class table { public: using row = std::tuple; struct row_id { size_t offset; }; row_id push_back(Ts... ts); void pop_back(); row operator[](row_id i); row back(); void erase(row_id i); void reserve(size_t size); bool empty() const; size_t size() const; bool has_row(row_id i) const; private: ... }; table t; auto i1 = t.push_back(1,2,3,4); auto i2 = t.push_back(2,3,4,5); auto i3 = t.push_back(3,4,5,6); auto i4 = t.push_back(4,5,6,7); t.erase(i2); t.erase(i3); assert(t[i1] == std::tuple(1,2,3,4)); assert(t[i4] == std::tuple(4,5,6,7)); assert(! t.has_row(i2)); assert(! t.has_row(i3));

Slide 94

Slide 94 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 94/183 STOP WAR Stable row ID template class table { public: using row = std::tuple; struct row_id { size_t offset; }; row operator[](row_id id) { auto offset = index_[id.offset].offset; auto access = [&](std::index_sequence) { return row{std::get(data_)[offset]...}; }; return std::invoke(access, indexes); } private: static constexpr auto indexes = std::index_sequence_for{}; std::tuple...> data_; std::vector reverse_index_; std::vector index_; row_id first_free_ = {0}; }; template class table { public: using row = std::tuple; struct row_id { size_t offset; }; row_id push_back(Ts... ts); void pop_back(); row operator[](row_id i); row back(); void erase(row_id i); void reserve(size_t size); bool empty() const; size_t size() const; bool has_row(row_id i) const; private: ... }; table t; auto i1 = t.push_back(1,2,3,4); auto i2 = t.push_back(2,3,4,5); auto i3 = t.push_back(3,4,5,6); auto i4 = t.push_back(4,5,6,7); t.erase(i2); t.erase(i3); assert(t[i1] == std::tuple(1,2,3,4)); assert(t[i4] == std::tuple(4,5,6,7)); assert(! t.has_row(i2)); assert(! t.has_row(i3));

Slide 95

Slide 95 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 95/183 STOP WAR Live Demo row_id

Slide 96

Slide 96 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 96/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements

Slide 97

Slide 97 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 97/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements ✔️

Slide 98

Slide 98 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 98/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements ✔️

Slide 99

Slide 99 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 99/183 STOP WAR Iterators ● Iterating in storage order is fast! ● The prefetcher loves predictable access patterns in contiguous memory ● Let the value type be a tuple of references to the elements of each column ● Like table::operator[] ● Compare with ranges::zip_view

Slide 100

Slide 100 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 100/183 STOP WAR Iterators struct sentinel {}; struct iterator { using value_type = row; using difference_type = ssize_t; value_type operator*() const { return std::invoke([&](std::index_sequence){ return value_type{std::get(t->data_)[offset]...}; }, t->indexes); } iterator& operator++() { ++offset; return *this; } iterator operator++(int) { auto copy = *this; ++*this; return copy; } bool operator==(const iterator&) const = default; bool operator==(sentinel) const { return offset == t->size(); } table* t; size_t offset; };

Slide 101

Slide 101 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 101/183 STOP WAR Iterators struct sentinel {}; struct iterator { using value_type = row; using difference_type = ssize_t; value_type operator*() const { return std::invoke([&](std::index_sequence){ return value_type{std::get(t->data_)[offset]...}; }, t->indexes); } iterator& operator++() { ++offset; return *this; } iterator operator++(int) { auto copy = *this; ++*this; return copy; } bool operator==(const iterator&) const = default; bool operator==(sentinel) const { return offset == t->size(); } table* t; size_t offset; }; Instead of one iterator per column, use the table and the column offset.

Slide 102

Slide 102 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 102/183 STOP WAR Iterators struct sentinel {}; struct iterator { using value_type = row; using difference_type = ssize_t; value_type operator*() const { return std::invoke([&](std::index_sequence){ return value_type{std::get(t->data_)[offset]...}; }, t->indexes); } iterator& operator++() { ++offset; return *this; } iterator operator++(int) { auto copy = *this; ++*this; return copy; } bool operator==(const iterator&) const = default; bool operator==(sentinel) const { return offset == t->size(); } table* t; size_t offset; }; Dereferencing becomes the same as the old table::operator[] on offset.

Slide 103

Slide 103 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 103/183 STOP WAR Iterators struct sentinel {}; struct iterator { using value_type = row; using difference_type = ssize_t; value_type operator*() const { return std::invoke([&](std::index_sequence){ return value_type{std::get(t->data_)[offset]...}; }, t->indexes); } iterator& operator++() { ++offset; return *this; } iterator operator++(int) { auto copy = *this; ++*this; return copy; } bool operator==(const iterator&) const = default; bool operator==(sentinel) const { return offset == t->size(); } table* t; size_t offset; }; Comparing with the end-of-range sentinel becomes checking if the one-past-the-end offset of the table has been reached.

Slide 104

Slide 104 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 104/183 STOP WAR template class table { public: struct sentinel {}; struct iterator { ... }; friend class iterator; iterator begin() { return { this, 0 }; } sentinel end() const { return {}; }    size_t size() const { return std::get<0>(data_).size(); } private: static constexpr auto indexes = std::index_sequence_for{}; std::tuple...> data_; std::vector reverse_index_; std::vector index_; row_id first_free_ = {0}; }; Iterators

Slide 105

Slide 105 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 105/183 STOP WAR template class table { public: struct sentinel {}; struct iterator { ... }; friend class iterator; iterator begin() { return { this, 0 }; } sentinel end() const { return {}; }    size_t size() const { return std::get<0>(data_).size(); } private: static constexpr auto indexes = std::index_sequence_for{}; std::tuple...> data_; std::vector reverse_index_; std::vector index_; row_id first_free_ = {0}; }; begin() and end() becomes trivially simple. Iterators

Slide 106

Slide 106 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 106/183 STOP WAR Live Demo iterators

Slide 107

Slide 107 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 107/183 STOP WAR row_id It would be nice to be able to get the row_id from *iterator, but how? std::tuple does not naturally lend itself to that.

Slide 108

Slide 108 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 108/183 STOP WAR row_id It would be nice to be able to get the row_id from *iterator, but how? std::tuple does not naturally lend itself to that. Inheritance as row : std::tuple { ... }; could take care of it, but then we’d lose destructuring as auto [a,b,c] = *iterator; or auto [a,b,c] = table[id];

Slide 109

Slide 109 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 109/183 STOP WAR row_id It would be nice to be able to get the row_id from *iterator, but how? std::tuple does not naturally lend itself to that. Inheritance as row : std::tuple { ... }; could take care of it, but then we’d lose destructuring as auto [a,b,c] = *iterator; or auto [a,b,c] = table[id]; The standard offers a way to implement destructuring for your own types by:

Slide 110

Slide 110 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 110/183 STOP WAR row_id It would be nice to be able to get the row_id from *iterator, but how? std::tuple does not naturally lend itself to that. Inheritance as row : std::tuple { ... }; could take care of it, but then we’d lose destructuring as auto [a,b,c] = *iterator; or auto [a,b,c] = table[id]; The standard offers a way to implement destructuring for your own types by: Specializing std::tuple_size

Slide 111

Slide 111 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 111/183 STOP WAR row_id It would be nice to be able to get the row_id from *iterator, but how? std::tuple does not naturally lend itself to that. Inheritance as row : std::tuple { ... }; could take care of it, but then we’d lose destructuring as auto [a,b,c] = *iterator; or auto [a,b,c] = table[id]; The standard offers a way to implement destructuring for your own types by: Specializing std::tuple_size Specializing std::tuple_element

Slide 112

Slide 112 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 112/183 STOP WAR row_id It would be nice to be able to get the row_id from *iterator, but how? std::tuple does not naturally lend itself to that. Inheritance as row : std::tuple { ... }; could take care of it, but then we’d lose destructuring as auto [a,b,c] = *iterator; or auto [a,b,c] = table[id]; The standard offers a way to implement destructuring for your own types by: Specializing std::tuple_size Specializing std::tuple_element Implementing your own get(T&&) or T::get()

Slide 113

Slide 113 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 113/183 STOP WAR Our own row type template class table { public: struct row_id { size_t offset; }; row operator[](row_id id) { return { this, index_[id.offset].offset; } } private: std::tuple...> data_; std::vector reverse_index_; std::vector index_; ... }; template struct row; template struct row> { template friend Ts...[I]& get(row& r) { return std::get(r.t->data_)[offset]; } row_id id() const { return t->reverse_index_[offset]; } table* t; size_t offset; };

Slide 114

Slide 114 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 114/183 STOP WAR Our own row type template class table { public: struct row_id { size_t offset; }; row operator[](row_id id) { return { this, index_[id.offset].offset; } } private: std::tuple...> data_; std::vector reverse_index_; std::vector index_; ... }; template struct row; template struct row> { template friend Ts...[I]& get(row& r) { return std::get(r.t->data_)[offset]; } row_id id() const { return t->reverse_index_[offset]; } table* t; size_t offset; }; Which row in the table

Slide 115

Slide 115 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 115/183 STOP WAR Our own row type template class table { public: struct row_id { size_t offset; }; row operator[](row_id id) { return { this, index_[id.offset].offset; } } private: std::tuple...> data_; std::vector reverse_index_; std::vector index_; ... }; template struct row; template struct row> { template friend Ts...[I]& get(row& r) { return std::get(r.t->data_)[offset]; } row_id id() const { return t->reverse_index_[offset]; } table* t; size_t offset; }; Get the row offset by lookup in the index

Slide 116

Slide 116 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 116/183 STOP WAR Our own row type template class table { public: struct row_id { size_t offset; }; row operator[](row_id id) { return { this, index_[id.offset].offset; } } private: std::tuple...> data_; std::vector reverse_index_; std::vector index_; ... }; template struct row; template struct row> { template friend Ts...[I]& get(row& r) { return std::get(r.t->data_)[offset]; } row_id id() const { return t->reverse_index_[offset]; } table* t; size_t offset; }; Getting the row_id back is trivial

Slide 117

Slide 117 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 117/183 STOP WAR Our own row type template class table { public: struct row_id { size_t offset; }; row operator[](row_id id) { return { this, index_[id.offset].offset; } } private: std::tuple...> data_; std::vector reverse_index_; std::vector index_; ... }; template struct row; template struct row> { template friend Ts...[I]& get(row& r) { return std::get(r.t->data_)[offset]; } row_id id() const { return t->reverse_index_[offset]; } table* t; size_t offset; }; Reference the desired column with tuple lookup.

Slide 118

Slide 118 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 118/183 STOP WAR Our own row type template class table { public: struct row_id { size_t offset; }; row operator[](row_id id) { return { this, index_[id.offset].offset; } } private: std::tuple...> data_; std::vector reverse_index_; std::vector index_; ... }; template struct row; template struct row> { template friend Ts...[I]& get(row& r) { return std::get(r.t->data_)[offset]; } row_id id() const { return t->reverse_index_[offset]; } table* t; size_t offset; }; C++26 parameter pack indexing magic!

Slide 119

Slide 119 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 119/183 STOP WAR Our own row type template class table { public: struct row_id { size_t offset; }; row operator[](row_id id) { return { this, index_[id.offset].offset; } } private: std::tuple...> data_; std::vector reverse_index_; std::vector index_; ... }; template struct row; template struct row> { template friend Ts...[I]& get(row& r) { return std::get(r.t->data_)[offset]; } row_id id() const { return t->reverse_index_[offset]; } table* t; size_t offset; }; template struct std::tuple_element { using type = Ts...[I]&; }; template struct std::tuple_size> : std::integral_constant {}; Opt in to destructuring

Slide 120

Slide 120 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 120/183 STOP WAR Live Demo iterators_row

Slide 121

Slide 121 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 121/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements ✔️

Slide 122

Slide 122 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 122/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements ✔️ ✔️

Slide 123

Slide 123 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 123/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements ✔️ ✔️

Slide 124

Slide 124 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 124/183 STOP WAR Selecting columns

Slide 125

Slide 125 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 125/183 STOP WAR Selecting columns With the current row type, we can select columns by explicitly using get(row).

Slide 126

Slide 126 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 126/183 STOP WAR Selecting columns With the current row type, we can select columns by explicitly using get(row). void func(const table& data) { for (auto r : data) { std::println(“{} {}”, get<0>(r), get<3>(r)); } }

Slide 127

Slide 127 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 127/183 STOP WAR Selecting columns With the current row type, we can select columns by explicitly using get(row). void func(const table& data) { for (auto r : data) { std::println(“{} {}”, get<0>(r), get<3>(r)); } }

Slide 128

Slide 128 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 128/183 STOP WAR Selecting columns With the current row type, we can select columns by explicitly using get(row). void func(const table& data) { for (auto r : data) { std::println(“{} {}”, get<0>(r), get<3>(r)); } } Not exactly convenient, is it?

Slide 129

Slide 129 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 129/183 STOP WAR Different row type template struct table; template struct row; template struct row, std::index_sequence> { using row_id = typename table::row_id; template friend auto& get(const row& r) { static constexpr std::array columns{Cs...}; return std::get(r.t->data_)[r.offset]; } ... table* t; size_t offset; };

Slide 130

Slide 130 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 130/183 STOP WAR Different row type template struct table; template struct row; template struct row, std::index_sequence> { using row_id = typename table::row_id; template friend auto& get(const row& r) { static constexpr std::array columns{Cs...}; return std::get(r.t->data_)[r.offset]; } ... table* t; size_t offset; }; The columns in the table that are referenced by this row type

Slide 131

Slide 131 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 131/183 STOP WAR Different row type template struct table; template struct row; template struct row, std::index_sequence> { using row_id = typename table::row_id; template friend auto& get(const row& r) { static constexpr std::array columns{Cs...}; return std::get(r.t->data_)[r.offset]; } ... table* t; size_t offset; }; The columns in the table that are referenced by this row type row, std::index_sequence<0,2,3>> r; get<1>(r);

Slide 132

Slide 132 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 132/183 STOP WAR Different row type template struct table; template struct row; template struct row, std::index_sequence> { using row_id = typename table::row_id; template friend auto& get(const row& r) { static constexpr std::array columns{Cs...}; return std::get(r.t->data_)[r.offset]; } ... table* t; size_t offset; }; The columns in the table that are referenced by this row type row, std::index_sequence<0,2,3>> r; get<1>(r);

Slide 133

Slide 133 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 133/183 STOP WAR Different row type template struct table; template struct row; template struct row, std::index_sequence> { using row_id = typename table::row_id; template friend auto& get(const row& r) { static constexpr std::array columns{Cs...}; return std::get(r.t->data_)[r.offset]; } ... table* t; size_t offset; }; The columns in the table that are referenced by this row type row, std::index_sequence<0,2,3>> r; get<1>(r);

Slide 134

Slide 134 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 134/183 STOP WAR Different row type template struct table; template struct row; template struct row, std::index_sequence> { using row_id = typename table::row_id; template friend auto& get(const row& r) { static constexpr std::array columns{Cs...}; return std::get(r.t->data_)[r.offset]; } ... table* t; size_t offset; }; The columns in the table that are referenced by this row type row, std::index_sequence<0,2,3>> r; get<1>(r);

Slide 135

Slide 135 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 135/183 STOP WAR Different row type template struct table; template struct row; template struct row, std::index_sequence> { using row_id = typename table::row_id; template friend auto& get(const row& r) { static constexpr std::array columns{Cs...}; return std::get(r.t->data_)[r.offset]; } ... table* t; size_t offset; };

Slide 136

Slide 136 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 136/183 STOP WAR Different row type template struct table; template struct row; template struct row, std::index_sequence> { using row_id = typename table::row_id; template friend auto& get(const row& r) { static constexpr std::array columns{Cs...}; return std::get(r.t->data_)[r.offset]; } ... table* t; size_t offset; }; Get the I-th of the columns

Slide 137

Slide 137 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 137/183 STOP WAR Different row type template struct table; template struct row; template struct row, std::index_sequence> { using row_id = typename table::row_id; template friend auto& get(const row& r) { static constexpr std::array columns{Cs...}; return std::get(r.t->data_)[r.offset]; } ... table* t; size_t offset; }; template struct std::tuple_size>> : std::integral_constant {}; template struct std::tuple_element, std::index_sequence>> {     static constexpr std::array indexes = { Cs... };     using type = Ts...[indexes[I]]&; };

Slide 138

Slide 138 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 138/183 STOP WAR Different row type template struct table; template struct row; template struct row, std::index_sequence> { using row_id = typename table::row_id; template friend auto& get(const row& r) { static constexpr std::array columns{Cs...}; return std::get(r.t->data_)[r.offset]; } ... table* t; size_t offset; }; template struct std::tuple_size>> : std::integral_constant {}; template struct std::tuple_element, std::index_sequence>> {     static constexpr std::array indexes = { Cs... };     using type = Ts...[indexes[I]]&; }; The size is the number of columns referenced

Slide 139

Slide 139 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 139/183 STOP WAR Different row type template struct table; template struct row; template struct row, std::index_sequence> { using row_id = typename table::row_id; template friend auto& get(const row& r) { static constexpr std::array columns{Cs...}; return std::get(r.t->data_)[r.offset]; } ... table* t; size_t offset; }; template struct std::tuple_size>> : std::integral_constant {}; template struct std::tuple_element, std::index_sequence>> {     static constexpr std::array indexes = { Cs... };     using type = Ts...[indexes[I]]&; }; The type of the I-th element is the type of the I-th column referenced

Slide 140

Slide 140 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 140/183 STOP WAR Select columns in a row template auto select(const row>& r) {     static constexpr size_t columns[] { Cs... };     return row>(r); }

Slide 141

Slide 141 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 141/183 STOP WAR Select columns in a row template auto select(const row>& r) {     static constexpr size_t columns[] { Cs... };     return row>(r); } Select a subset of columns to reference

Slide 142

Slide 142 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 142/183 STOP WAR Select columns in a row template auto select(const row>& r) {     static constexpr size_t columns[] { Cs... };     return row>(r); } Return a row that references that subset

Slide 143

Slide 143 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 143/183 STOP WAR Select columns in a row template auto select(const row>& r) {     static constexpr size_t columns[] { Cs... };     return row>(r); } drop_if(values, [](auto r) { auto [x,z] = select<0,2>(r); return x < z; });

Slide 144

Slide 144 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 144/183 STOP WAR Select columns in a row template auto select(const row>& r) {     static constexpr size_t columns[] { Cs... };     return row>(r); } drop_if(values, [](auto r) { auto [x,z] = select<0,2>(r); return x < z; }); Select only the columns we’re interested in for the expression

Slide 145

Slide 145 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 145/183 STOP WAR Select columns in a row template auto select(const row>& r) {     static constexpr size_t columns[] { Cs... };     return row>(r); } drop_if(values, [](auto r) { auto [x,z] = select<0,2>(r); return x < z; }); Select only the columns we’re interested in for the expression

Slide 146

Slide 146 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 146/183 STOP WAR Select columns in an iteration template struct range_selector { using r_iterator = decltype(std::declval().begin()); struct iterator : r_iterator { using difference_type = ssize_t; using value_type = decltype(select(*std::declval())); iterator(const r_iterator& i) : r_iterator(i) {} auto operator*() const { const r_iterator& i = *this; return select(*i); } }; iterator begin() { return iterator{ r.begin() }; } auto end() { return r.end(); } R& r; };

Slide 147

Slide 147 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 147/183 STOP WAR Select columns in an iteration template struct range_selector { using r_iterator = decltype(std::declval().begin()); struct iterator : r_iterator { using difference_type = ssize_t; using value_type = decltype(select(*std::declval())); iterator(const r_iterator& i) : r_iterator(i) {} auto operator*() const { const r_iterator& i = *this; return select(*i); } }; iterator begin() { return iterator{ r.begin() }; } auto end() { return r.end(); } R& r; }; range_selector refers to another range which has row<> as its value type. range_selector refers to another range which has row<> as its value type.

Slide 148

Slide 148 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 148/183 STOP WAR Select columns in an iteration template struct range_selector { using r_iterator = decltype(std::declval().begin()); struct iterator : r_iterator { using difference_type = ssize_t; using value_type = decltype(select(*std::declval())); iterator(const r_iterator& i) : r_iterator(i) {} auto operator*() const { const r_iterator& i = *this; return select(*i); } }; iterator begin() { return iterator{ r.begin() }; } auto end() { return r.end(); } R& r; };

Slide 149

Slide 149 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 149/183 STOP WAR Select columns in an iteration template struct range_selector { using r_iterator = decltype(std::declval().begin()); struct iterator : r_iterator { using difference_type = ssize_t; using value_type = decltype(select(*std::declval())); iterator(const r_iterator& i) : r_iterator(i) {} auto operator*() const { const r_iterator& i = *this; return select(*i); } }; iterator begin() { return iterator{ r.begin() }; } auto end() { return r.end(); } R& r; }; Find the iterator type of the referenced range

Slide 150

Slide 150 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 150/183 STOP WAR Select columns in an iteration template struct range_selector { using r_iterator = decltype(std::declval().begin()); struct iterator : r_iterator { using difference_type = ssize_t; using value_type = decltype(select(*std::declval())); iterator(const r_iterator& i) : r_iterator(i) {} auto operator*() const { const r_iterator& i = *this; return select(*i); } }; iterator begin() { return iterator{ r.begin() }; } auto end() { return r.end(); } R& r; }; Create a new iterator type via inheritance

Slide 151

Slide 151 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 151/183 STOP WAR Select columns in an iteration template struct range_selector { using r_iterator = decltype(std::declval().begin()); struct iterator : r_iterator { using difference_type = ssize_t; using value_type = decltype(select(*std::declval())); iterator(const r_iterator& i) : r_iterator(i) {} auto operator*() const { const r_iterator& i = *this; return select(*i); } }; iterator begin() { return iterator{ r.begin() }; } auto end() { return r.end(); } R& r; }; The new value type is the result of select<> on the underlying iterator’s value type

Slide 152

Slide 152 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 152/183 STOP WAR Select columns in an iteration template struct range_selector { using r_iterator = decltype(std::declval().begin()); struct iterator : r_iterator { using difference_type = ssize_t; using value_type = decltype(select(*std::declval())); iterator(const r_iterator& i) : r_iterator(i) {} auto operator*() const { const r_iterator& i = *this; return select(*i); } }; iterator begin() { return iterator{ r.begin() }; } auto end() { return r.end(); } R& r; }; Return a row with the selected columns

Slide 153

Slide 153 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 153/183 STOP WAR template struct range_selector { using r_iterator = decltype(std::declval().begin()); struct iterator : r_iterator { using difference_type = ssize_t; using value_type = decltype(select(*std::declval())); iterator(const r_iterator& i) : r_iterator(i) {} auto operator*() const { const r_iterator& i = *this; return select(*i); } }; iterator begin() { return iterator{ r.begin() }; } auto end() { return r.end(); } R& r; }; Select columns in an iteration template struct range_selector_maker { template friend range_selector operator|(R& r, range_selector_maker) {     return { r }; } }; template range_selector_maker select() { return {}; }

Slide 154

Slide 154 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 154/183 STOP WAR template struct range_selector { using r_iterator = decltype(std::declval().begin()); struct iterator : r_iterator { using difference_type = ssize_t; using value_type = decltype(select(*std::declval())); iterator(const r_iterator& i) : r_iterator(i) {} auto operator*() const { const r_iterator& i = *this; return select(*i); } }; iterator begin() { return iterator{ r.begin() }; } auto end() { return r.end(); } R& r; }; Select columns in an iteration template struct range_selector_maker { template friend range_selector operator|(R& r, range_selector_maker) {     return { r }; } }; template range_selector_maker select() { return {}; } Create a range_selector<> by piping a range to the result of select<>()

Slide 155

Slide 155 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 155/183 STOP WAR template struct range_selector { using r_iterator = decltype(std::declval().begin()); struct iterator : r_iterator { using difference_type = ssize_t; using value_type = decltype(select(*std::declval())); iterator(const r_iterator& i) : r_iterator(i) {} auto operator*() const { const r_iterator& i = *this; return select(*i); } }; iterator begin() { return iterator{ r.begin() }; } auto end() { return r.end(); } R& r; }; Select columns in an iteration template struct range_selector_maker { template friend range_selector operator|(R& r, range_selector_maker) {     return { r }; } }; template range_selector_maker select() { return {}; } for (auto [x,d] : values | select<0,3>()) {     std::println("d={} x={}", d, x); }

Slide 156

Slide 156 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 156/183 STOP WAR template struct range_selector { using r_iterator = decltype(std::declval().begin()); struct iterator : r_iterator { using difference_type = ssize_t; using value_type = decltype(select(*std::declval())); iterator(const r_iterator& i) : r_iterator(i) {} auto operator*() const { const r_iterator& i = *this; return select(*i); } }; iterator begin() { return iterator{ r.begin() }; } auto end() { return r.end(); } R& r; }; Select columns in an iteration template struct range_selector_maker { template friend range_selector operator|(R& r, range_selector_maker) {     return { r }; } }; template range_selector_maker select() { return {}; } for (auto [x,d] : values | select<0,3>()) {     std::println("d={} x={}", d, x); }

Slide 157

Slide 157 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 157/183 STOP WAR Live Demo row_select

Slide 158

Slide 158 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 158/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements ✔️ ✔️

Slide 159

Slide 159 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 159/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements ✔️ ✔️ ✔️

Slide 160

Slide 160 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 160/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements ✔️ ✔️ ✔️

Slide 161

Slide 161 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 161/183 STOP WAR drop_if(values, [](auto r) { auto [x,z] = select<0,2>(r); return x < z; }); Apply functions to selected columns

Slide 162

Slide 162 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 162/183 STOP WAR drop_if(values, [](auto r) { auto [x,z] = select<0,2>(r); return x < z; }); Apply functions to selected columns template auto select(F&& f) requires (! row_type>) { return [f = std::forward(f)](row r) { return f(select(r)); }; }

Slide 163

Slide 163 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 163/183 STOP WAR drop_if(values, [](auto r) { auto [x,z] = select<0,2>(r); return x < z; }); Apply functions to selected columns template auto select(F&& f) requires (! row_type>) { return [f = std::forward(f)](row r) { return f(select(r)); }; } Conflicts with template auto select(const row>& r);

Slide 164

Slide 164 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 164/183 STOP WAR drop_if(values, [](auto r) { auto [x,z] = select<0,2>(r); return x < z; }); Apply functions to selected columns template auto select(F&& f) requires (! row_type>) { return [f = std::forward(f)](row r) { return f(select(r)); }; } Conflicts with template auto select(const row>& r);

Slide 165

Slide 165 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 165/183 STOP WAR drop_if(values, [](auto r) { auto [x,z] = select<0,2>(r); return x < z; }); Apply functions to selected columns template auto select(F&& f) requires (! row_type>) { return [f = std::forward(f)](row r) { return f(select(r)); }; } template inline constexpr bool row_type_v = false; template inline constexpr bool row_type_v> = true; template concept row_type = row_type_v;

Slide 166

Slide 166 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 166/183 STOP WAR drop_if(values, [](auto r) { auto [x,z] = select<0,2>(r); return x < z; }); Apply functions to selected columns template auto select(F&& f) requires (! row_type>) { return [f = std::forward(f)](row r) { return f(select(r)); }; } template inline constexpr bool row_type_v = false; template inline constexpr bool row_type_v> = true; template concept row_type = row_type_v;

Slide 167

Slide 167 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 167/183 STOP WAR drop_if(values, [](auto r) { auto [x,z] = select<0,2>(r); return x < z; }); Apply functions to selected columns template auto select(F&& f) requires (! row_type>) { return [f = std::forward(f)](row r) { return f(select(r)); }; }

Slide 168

Slide 168 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 168/183 STOP WAR drop_if(values, [](auto r) { auto [x,z] = select<0,2>(r); return x < z; }); drop_if(values, select<0,2>([](auto r) { auto [x,z] = r; return x < z; })); Apply functions to selected columns template auto select(F&& f) requires (! row_type>) { return [f = std::forward(f)](row r) { return f(select(r)); }; }

Slide 169

Slide 169 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 169/183 STOP WAR Apply functions to selected columns drop_if(values, select<0,2>([](auto r) { auto [x,z] = r; return x < z; })); template auto apply(F&& f) { return [f = std::forward(f)](R r) { return std::invoke([&](std::index_sequence) { return f(get(r)...); }, std::make_index_sequence>{}); }; }

Slide 170

Slide 170 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 170/183 STOP WAR Apply functions to selected columns drop_if(values, select<0,2>([](auto r) { auto [x,z] = r; return x < z; })); template auto apply(F&& f) { return [f = std::forward(f)](R r) { return std::invoke([&](std::index_sequence) { return f(get(r)...); }, std::make_index_sequence>{}); }; } apply() takes a function and returns a lambda that takes a row.

Slide 171

Slide 171 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 171/183 STOP WAR Apply functions to selected columns drop_if(values, select<0,2>([](auto r) { auto [x,z] = r; return x < z; })); template auto apply(F&& f) { return [f = std::forward(f)](R r) { return std::invoke([&](std::index_sequence) { return f(get(r)...); }, std::make_index_sequence>{}); }; } When called, it calls the bound function f with every element in the row.

Slide 172

Slide 172 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 172/183 STOP WAR Apply functions to selected columns drop_if(values, select<0,2>([](auto r) { auto [x,z] = r; return x < z; })); drop_if(values, select<0,2>(apply([](auto x, auto z) { return x < z; }))); template auto apply(F&& f) { return [f = std::forward(f)](R r) { return std::invoke([&](std::index_sequence) { return f(get(r)...); }, std::make_index_sequence>{}); }; }

Slide 173

Slide 173 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 173/183 STOP WAR Apply functions to selected columns drop_if(values, select<0,2>([](auto r) { auto [x,z] = r; return x < z; })); drop_if(values, select<0,2>(apply([](auto x, auto z) { return x < z; }))); template auto apply(F&& f) { return [f = std::forward(f)](R r) { return std::invoke([&](std::index_sequence) { return f(get(r)...); }, std::make_index_sequence>{}); }; }

Slide 174

Slide 174 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 174/183 STOP WAR Apply functions to selected columns drop_if(values, select<0,2>([](auto r) { auto [x,z] = r; return x < z; })); drop_if(values, select<0,2>(apply([](auto x, auto z) { return x < z; }))); template auto apply(F&& f) { return [f = std::forward(f)](R r) { return std::invoke([&](std::index_sequence) { return f(get(r)...); }, std::make_index_sequence>{}); }; } Isn’t there a name for this?

Slide 175

Slide 175 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 175/183 STOP WAR Apply functions to selected columns drop_if(values, select<0,2>([](auto r) { auto [x,z] = r; return x < z; })); drop_if(values, select<0,2>(apply([](auto x, auto z) { return x < z; }))); drop_if(values, select<0,2>(apply(std::less{}))); template auto apply(F&& f) { return [f = std::forward(f)](R r) { return std::invoke([&](std::index_sequence) { return f(get(r)...); }, std::make_index_sequence>{}); }; }

Slide 176

Slide 176 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 176/183 STOP WAR Apply functions to selected columns drop_if(values, select<0,2>([](auto r) { auto [x,z] = r; return x < z; })); drop_if(values, select<0,2>(apply([](auto x, auto z) { return x < z; }))); drop_if(values, select<0,2>(apply(std::less{}))); template auto apply(F&& f) { return [f = std::forward(f)](R r) { return std::invoke([&](std::index_sequence) { return f(get(r)...); }, std::make_index_sequence>{}); }; }

Slide 177

Slide 177 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 177/183 STOP WAR Live Demo function_select

Slide 178

Slide 178 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 178/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements ✔️ ✔️ ✔️ ✔️

Slide 179

Slide 179 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 179/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements ✔️ ✔️ ✔️ ✔️ ✔️

Slide 180

Slide 180 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 180/183 STOP WAR Status But, we also need… … a stable row-ID to lookup objects … an iterator interface to work with ranges And want... … a convenient way to look at only some columns … a convenient way to call functions with selected elements ✔️ ✔️ ✔️ ✔️ ✔️

Slide 181

Slide 181 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 181/183 STOP WAR Cache Friendly + Functional + Ranges = ❤️ Give it a go, and please help out if you can https://github.com/rollbear/columnist

Slide 182

Slide 182 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 182/183 STOP WAR Cache Friendly + Functional + Ranges = ❤️ Give it a go, and please help out if you can https://github.com/rollbear/columnist https://github.com/rollbear/cache_functional_ranges Source examples from this presentation

Slide 183

Slide 183 text

Cache Friendly + Functional + Ranges = – C++OnSea 2024 © Björn Fahller ❤️ @[email protected] 183/183 STOP WAR Björn Fahller [email protected] @[email protected] @rollbear Cache Friendly + Functional + Ranges = ❤️