Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 2/172 stop Putin Background https://youtu.be/rX0ItVEVjHc Mike Acton – CppCon Keynote 2014 Data Oriented Design https://youtu.be/OOOEU2FWCg4 Mathieu Ropert – StockholmCpp 2023 Data Storage in Entity Component System

Slide 3

Slide 3 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 3/172 stop Putin 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 4

Slide 4 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 4/172 stop Putin 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 5

Slide 5 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 5/172 stop Putin 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 6

Slide 6 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 6/172 stop Putin 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 7

Slide 7 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 7/172 stop Putin 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 8

Slide 8 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 8/172 stop Putin 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 9

Slide 9 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 9/172 stop Putin 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 10

Slide 10 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 10/172 stop Putin 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 11

Slide 11 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 11/172 stop Putin 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 12

Slide 12 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 12/172 stop Putin 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 13

Slide 13 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 13/172 stop Putin 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 14

Slide 14 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 14/172 stop Putin 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 15

Slide 15 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 15/172 stop Putin 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 16

Slide 16 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 16/172 stop Putin 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 17

Slide 17 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 17/172 stop Putin 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 18

Slide 18 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 18/172 stop Putin 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 19

Slide 19 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 19/172 stop Putin 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 20

Slide 20 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 20/172 stop Putin 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 21

Slide 21 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 21/172 stop Putin 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 22

Slide 22 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 22/172 stop Putin 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 23

Slide 23 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 23/172 stop Putin 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 24

Slide 24 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 24/172 stop Putin 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 25

Slide 25 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 25/172 stop Putin 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 26

Slide 26 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 26/172 stop Putin 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 27

Slide 27 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 27/172 stop Putin 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 28

Slide 28 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 28/172 stop Putin 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 29

Slide 29 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 29/172 stop Putin 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 30

Slide 30 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 30/172 stop Putin 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 31

Slide 31 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 31/172 stop Putin 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 32

Slide 32 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 32/172 stop Putin 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 33

Slide 33 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 33/172 stop Putin Live Demo vector_of_structs vs struct_of_vectors

Slide 34

Slide 34 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 34/172 stop Putin Observations In this exampe, struct of vectors...

Slide 35

Slide 35 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 35/172 stop Putin Observations In this exampe, struct of vectors... … has way fewer memory access

Slide 36

Slide 36 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 36/172 stop Putin Observations In this exampe, struct of vectors... … has way fewer memory access … has way fewer cache misses

Slide 37

Slide 37 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 37/172 stop Putin Observations In this exampe, struct of vectors... … has way fewer memory access … has way fewer cache misses … has slightly fewer instructions

Slide 38

Slide 38 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 38/172 stop Putin Observations In this exampe, struct of vectors... … has way fewer memory access … has way fewer cache misses … has slightly fewer instructions … is faster

Slide 39

Slide 39 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 39/172 stop Putin Observations In this exampe, struct of vectors... … has way fewer memory access … has way fewer cache misses … has slightly fewer instructions … is faster … is horrible code to work with

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 41/172 stop Putin 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 42

Slide 42 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 42/172 stop Putin 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 43

Slide 43 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 43/172 stop Putin 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 44

Slide 44 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 44/172 stop Putin 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 45

Slide 45 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 45/172 stop Putin table

Slide 46

Slide 46 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 46/172 stop Putin 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 47

Slide 47 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 47/172 stop Putin 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 48

Slide 48 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 48/172 stop Putin 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 49

Slide 49 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 49/172 stop Putin Live Demo templated

Slide 50

Slide 50 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 50/172 stop Putin Observations In this exampe...

Slide 51

Slide 51 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 51/172 stop Putin Observations In this exampe... … the ergonomics is roughly the same as with vector of structs

Slide 52

Slide 52 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 52/172 stop Putin 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 53

Slide 53 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 53/172 stop Putin 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 54

Slide 54 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 54/172 stop Putin 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 55

Slide 55 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 55/172 stop Putin 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 56

Slide 56 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 56/172 stop Putin 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 57

Slide 57 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 57/172 stop Putin 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 58

Slide 58 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 58/172 stop Putin 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 59

Slide 59 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 59/172 stop Putin 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 60

Slide 60 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 60/172 stop Putin 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 61

Slide 61 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 61/172 stop Putin 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 62

Slide 62 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 62/172 stop Putin 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 63

Slide 63 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 63/172 stop Putin 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 64

Slide 64 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 64/172 stop Putin 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 65

Slide 65 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 65/172 stop Putin 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 66

Slide 66 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 66/172 stop Putin 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 67

Slide 67 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 67/172 stop Putin 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 68

Slide 68 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 68/172 stop Putin 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 69

Slide 69 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 69/172 stop Putin 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 70

Slide 70 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 70/172 stop Putin 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 71

Slide 71 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 71/172 stop Putin 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 72

Slide 72 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 72/172 stop Putin 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 73

Slide 73 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 73/172 stop Putin 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 74

Slide 74 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 74/172 stop Putin 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 75

Slide 75 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 75/172 stop Putin 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 76

Slide 76 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 76/172 stop Putin 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 77

Slide 77 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 77/172 stop Putin 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 78

Slide 78 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 78/172 stop Putin 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 79

Slide 79 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 79/172 stop Putin 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 80

Slide 80 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 80/172 stop Putin 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 81

Slide 81 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 81/172 stop Putin 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 82

Slide 82 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 82/172 stop Putin 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 83

Slide 83 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 83/172 stop Putin 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 84

Slide 84 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 84/172 stop Putin 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 85

Slide 85 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 85/172 stop Putin 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 86

Slide 86 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 86/172 stop Putin 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 87

Slide 87 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 87/172 stop Putin 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 88

Slide 88 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 88/172 stop Putin 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 89

Slide 89 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 89/172 stop Putin 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 90

Slide 90 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 90/172 stop Putin 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 91

Slide 91 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 91/172 stop Putin 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 92

Slide 92 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 92/172 stop Putin 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 93

Slide 93 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 93/172 stop Putin 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 94

Slide 94 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 94/172 stop Putin 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 95

Slide 95 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 95/172 stop Putin 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 96

Slide 96 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 96/172 stop Putin 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 97

Slide 97 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 97/172 stop Putin 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 98

Slide 98 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 98/172 stop Putin 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 99

Slide 99 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 99/172 stop Putin 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 100

Slide 100 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 100/172 stop Putin Live Demo iterators

Slide 101

Slide 101 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 101/172 stop Putin 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 102

Slide 102 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 102/172 stop Putin 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 103

Slide 103 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 103/172 stop Putin 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 104

Slide 104 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 104/172 stop Putin 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 105

Slide 105 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 105/172 stop Putin 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 106

Slide 106 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 106/172 stop Putin 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 107

Slide 107 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 107/172 stop Putin 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 108

Slide 108 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 108/172 stop Putin 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 109

Slide 109 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 109/172 stop Putin 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 110

Slide 110 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 110/172 stop Putin 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 111

Slide 111 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 111/172 stop Putin 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 112

Slide 112 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 112/172 stop Putin 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 113

Slide 113 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 113/172 stop Putin 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 114

Slide 114 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 114/172 stop Putin Live Demo iterators_row

Slide 115

Slide 115 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 115/172 stop Putin 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 116

Slide 116 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 116/172 stop Putin 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 117

Slide 117 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 117/172 stop Putin 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 118

Slide 118 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 118/172 stop Putin Selecting columns

Slide 119

Slide 119 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 119/172 stop Putin Selecting columns With the current row type, we can select columns by explicitly using get(row).

Slide 120

Slide 120 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 120/172 stop Putin 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 121

Slide 121 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 121/172 stop Putin 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 122

Slide 122 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 122/172 stop Putin 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 123

Slide 123 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 123/172 stop Putin 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 124

Slide 124 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 124/172 stop Putin 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 125

Slide 125 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 125/172 stop Putin 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 126

Slide 126 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 126/172 stop Putin 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 127

Slide 127 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 127/172 stop Putin 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 128

Slide 128 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 128/172 stop Putin 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 129

Slide 129 text

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

Slide 130

Slide 130 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 130/172 stop Putin 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 131

Slide 131 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 131/172 stop Putin 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 132

Slide 132 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 132/172 stop Putin 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 133

Slide 133 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 133/172 stop Putin 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 134

Slide 134 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 134/172 stop Putin 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 135

Slide 135 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 135/172 stop Putin 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 136

Slide 136 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 136/172 stop Putin 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 137

Slide 137 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 137/172 stop Putin 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 138

Slide 138 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 138/172 stop Putin 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 139

Slide 139 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 139/172 stop Putin 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 140

Slide 140 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 140/172 stop Putin 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 141

Slide 141 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 141/172 stop Putin 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 142

Slide 142 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 142/172 stop Putin 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 143

Slide 143 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 143/172 stop Putin 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 144

Slide 144 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 144/172 stop Putin 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 145

Slide 145 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 145/172 stop Putin 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 146

Slide 146 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 146/172 stop Putin Live Demo row_select

Slide 147

Slide 147 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 147/172 stop Putin 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 148

Slide 148 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 148/172 stop Putin 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 149

Slide 149 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 149/172 stop Putin 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 150

Slide 150 text

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

Slide 151

Slide 151 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 151/172 stop Putin 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 152

Slide 152 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 152/172 stop Putin 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 153

Slide 153 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 153/172 stop Putin 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 154

Slide 154 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 154/172 stop Putin 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 155

Slide 155 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 155/172 stop Putin 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 156

Slide 156 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 156/172 stop Putin 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 157

Slide 157 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 157/172 stop Putin 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 158

Slide 158 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 158/172 stop Putin 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 159

Slide 159 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 159/172 stop Putin 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 160

Slide 160 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 160/172 stop Putin 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 161

Slide 161 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 161/172 stop Putin 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 162

Slide 162 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 162/172 stop Putin 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 163

Slide 163 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 163/172 stop Putin 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 164

Slide 164 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 164/172 stop Putin 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 165

Slide 165 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 165/172 stop Putin 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 166

Slide 166 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 166/172 stop Putin Live Demo function_select

Slide 167

Slide 167 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 167/172 stop Putin 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 168

Slide 168 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 168/172 stop Putin 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 169

Slide 169 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 169/172 stop Putin 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 170

Slide 170 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 170/172 stop Putin Cache Friendly + Functional + Ranges = ❤️ Give it a go, and please help out if you can https://github.com/rollbear/columnist

Slide 171

Slide 171 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 171/172 stop Putin 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 172

Slide 172 text

Cache Friendly + Functional + Ranges = – StockholmCpp 2024 © Björn Fahller ❤️ @[email protected] 172/172 stop Putin Björn Fahller [email protected] @[email protected] @rollbear Cache Friendly + Functional + Ranges = ❤️