Upgrade to Pro — share decks privately, control downloads, hide ads and more …

MUC++ - Function Moar with C++23

MUC++ - Function Moar with C++23

Higher order functions, functions that take functions as input, or return functions, are useful for making your code more composable and expressive. The evolution of the standard library with ranges, std::expected<> and the latest extensions to std::optional<>, makes the use of higher order functions so much more desirable. At the same time, the evolution of the core language has made them much easier to write.

I will show you techniques for increasing the expressiveness of your code
without sacrificing performance, and demonstrate how some of the recent
additions to the language and library improve the developer experience.

Björn Fahller

October 19, 2023
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 1/208 Function Moar with C++23 Björn Fahller f x y ( , ) + f x y = ( )( ) Currying?
  2. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 4/208 Function Moar with C++23 I did a talk on this topic in 2018
  3. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 5/208 Function Moar with C++23 I did a talk on this topic in 2018 The core language has evolved since then
  4. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 6/208 Function Moar with C++23 I did a talk on this topic in 2018 The core language has evolved since then The standard library has evolved since then
  5. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 7/208 Function Moar with C++23 I did a talk on this topic in 2018 The core language has evolved since then The standard library has evolved since then I have gained experience since then
  6. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 8/208 template <typename T> auto func(T t) { return t.a; } auto lambda = [](auto t) { return t.a; }; Function and “function” What are the differences between func and lambda?
  7. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 9/208 template <typename T> auto func(T t) { return t.a; } auto lambda = [](auto t) { return t.a; }; Function and “function” What are the differences between func and lambda? func can expand to several functions, one for every type T. You cannot get a pointer to func, only to specializations.
  8. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 10/208 template <typename T> auto func(T t) { return t.a; } auto lambda = [](auto t) { return t.a; }; Function and “function” What are the differences between func and lambda? func can expand to several functions, one for every type T. You cannot get a pointer to func, only to specializations. lambda is one object with several overloaded operator(), one for every type T.
  9. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 11/208 template <typename T> auto func(T t) { return t.a; } auto lambda = [](auto t) { return t.a; }; Function and “function” What are the differences between func and lambda? func can expand to several functions, one for every type T. You cannot get a pointer to func, only to specializations. lambda is one object with several overloaded operator(), one for every type T. You can take the address of lambda, and you can pass lambda as one object to a higher order function.
  10. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 12/208 template <typename T> auto func(T t) { return t.a; } auto lambda = [](auto t) { return t.a; }; Function and “function” What are the differences between func and lambda? There is no name for the overload set of specializations for func
  11. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 13/208 template <typename T> auto func(T t) { return t.a; } auto lambda = [](auto t) { return t.a; }; Function and “function” What are the differences between func and lambda? There is no name for the overload set of specializations for func Whereas lambda is a name for all overloaded function call operators
  12. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 14/208 Test for all numbers being positive template <std::ranges::forward_range R> bool all_positive(const R& r) { return std::ranges::all_of(r, [](const auto& v){ return v > 0;}); }
  13. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 15/208 Test for all numbers being positive template <std::ranges::forward_range R> bool all_positive(const R& r) { return std::ranges::all_of(r, [](const auto& v){ return v > 0;}); } You will probably write this lambda many times in your program, and it’s noisy
  14. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 17/208 auto greater_than = [](auto rh){ return [rh](const auto& lh) { return lh > rh;}; }; template <std::ranges::forward_range R> bool all_positive(const R& r) { return std::ranges::all_of(r, [](const auto& v){ return v > 0;}); } Test for all numbers being positive
  15. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 18/208 auto greater_than = [](auto rh){ return [rh](const auto& lh) { return lh > rh;}; }; template <std::ranges::forward_range R> bool all_positive(const R& r) { return std::ranges::all_of(r, [](const auto& v){ return v > 0;}); } Test for all numbers being positive Return a lambda that captures the value to compare with
  16. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 19/208 auto greater_than = [](auto rh){ return [rh](const auto& lh) { return lh > rh;}; }; template <std::ranges::forward_range R> bool all_positive(const R& r) { return std::ranges::all_of(r, [](const auto& v){ return v > 0;}); } Test for all numbers being positive Return a lambda that captures the value to compare with And returns the result of the comparison when called
  17. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 20/208 auto greater_than = [](auto rh){ return [rh](const auto& lh) { return lh > rh;}; }; template <std::ranges::forward_range R> bool all_positive(const R& r) { return std::ranges::all_of(r, [](const auto& v){ return v > 0;}); } Test for all numbers being positive greater_than(0)); Return a lambda that captures the value to compare with And returns the result of the comparison when called
  18. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 21/208 auto greater_than = [](auto rh){ return [rh](const auto& lh) { return lh > rh;}; }; template <std::ranges::forward_range R> bool all_positive(const R& r) { return std::ranges::all_of(r, [](const auto& v){ return v > 0;}); } Test for all numbers being positive greater_than(0)); Return a lambda that captures the value to compare with And returns the result of the comparison when called
  19. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 23/208 Test for all numbers being positive Added to the standard library in C++23
  20. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 24/208 Test for all numbers being positive Added to the standard library in C++23
  21. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 25/208 Test for all numbers being positive Returns something that when called, calls the function with the direct args and then the bound args
  22. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 26/208 Test for all numbers being positive Compares every value in r with std::greater{}(value, 0)
  23. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 27/208 Test for all numbers being positive Sadly missing in most stdlib implementations
  24. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 28/208 Test for all numbers being positive auto bind_back = [](auto f, auto rh) { return [f, rh](auto lh) { return f(lh, rh); }; };
  25. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 29/208 Test for all numbers being positive auto bind_back = [](auto f, auto rh) { return [f, rh](auto lh) { return f(lh, rh); }; }; Bind the function to call and the right hand side value.
  26. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 30/208 Test for all numbers being positive auto bind_back = [](auto f, auto rh) { return [f, rh](auto lh) { return f(lh, rh); }; }; Call the bound function with the left hand side argument and the bound right hand side value
  27. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 31/208 Test for all numbers being positive auto bind_back = [](auto f, auto rh) { return [f, rh](auto lh) { return f(lh, rh); }; }; auto less_than = [](auto t) { return bind_back(std::less{}, t); }; auto greater_than = [](auto t) { return bind_back(std::greater{}, t); }; template <std::ranges::forward_range R> bool all_positive(const R& r) { return std::ranges::all_of(r, greater_than(0)); }
  28. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 32/208 Test for all numbers being positive auto bind_back = [](auto f, auto rh) { return [f, rh](auto lh) { return f(lh, rh); }; }; auto less_than = [](auto t) { return bind_back(std::less{}, t); }; auto greater_than = [](auto t) { return bind_back(std::greater{}, t); }; template <std::ranges::forward_range R> bool all_positive(const R& r) { return std::ranges::all_of(r, greater_than(0)); } And now add the rest of the comparisons
  29. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 34/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } }
  30. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 35/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } Makes a sequence of neighbouring elements as tuples of references
  31. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 36/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } Makes a sequence of neighbouring elements as tuples of references
  32. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 37/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } Makes a sequence of neighbouring elements as tuples of references
  33. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 38/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } Makes a sequence of neighbouring elements as tuples of references
  34. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 39/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } Makes a sequence of neighbouring elements as tuples of references
  35. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 40/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } Makes a sequence of neighbouring elements as tuples of references
  36. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 41/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } Makes a sequence of neighbouring elements as tuples of references
  37. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 42/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } }
  38. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 43/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } }
  39. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 44/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } t is a tuple.
  40. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 45/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } Destructure it to make the code easier on the eyes.
  41. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 46/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } This construction gets old very quickly
  42. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 47/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } Can we use std::apply somehow?
  43. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 48/208 Get the differences in a sequence int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } Can we use std::apply somehow?
  44. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 50/208 Get the differences in a sequence auto apply = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename T>(T&& t) { return std::apply(f, std::forward<T>(t)); }; };
  45. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 51/208 Get the differences in a sequence auto apply = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename T>(T&& t) { return std::apply(f, std::forward<T>(t)); }; }; New syntax since C++23 to get explicitly named deduced types to lambda arguments
  46. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 52/208 Get the differences in a sequence auto apply = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename T>(T&& t) { return std::apply(f, std::forward<T>(t)); }; }; New syntax since C++23 to get explicitly named deduced types to lambda arguments
  47. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 53/208 Get the differences in a sequence auto apply = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename T>(T&& t) { return std::apply(f, std::forward<T>(t)); }; }; Return a lambda that captures the function
  48. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 54/208 Get the differences in a sequence auto apply = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename T>(T&& t) { return std::apply(f, std::forward<T>(t)); }; }; Call std::apply with the captured function, and the tuple from the argument list
  49. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 55/208 Get the differences in a sequence auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } }
  50. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 56/208 Get the differences in a sequence auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } apply([](auto a, auto b) { return b - a;})
  51. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 57/208 Get the differences in a sequence auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } apply([](auto a, auto b) { return b - a;}) Less noise. Good!
  52. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 58/208 Get the differences in a sequence auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( [](auto&& t){ auto&& [a,b] = t; return b - a;} )) { std::cout << d << '\n'; } } apply([](auto a, auto b) { return b - a;}) But the standard library has std::minus, can we use it?
  53. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 59/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence Hmm, std::minus, would return a – b. Can we change the order?
  54. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 60/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts)
  55. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 61/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts) Take a function to call
  56. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 62/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts) Take a function to call Is is the order of the arguments to pass
  57. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 63/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts) swizzle<2,0,1>(func)(“string”, 3, nullptr) becomes a call to func(nullptr, “string”, 3)
  58. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 64/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts) As usual, return a lambda that captures the function, and accepts a number of arguments
  59. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 65/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts)
  60. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 66/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts) Create a tuple type with references to all parameters
  61. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 67/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts)
  62. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 68/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts) Create an instance that refers to all parameters
  63. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 69/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts)
  64. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 70/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts) And finally call the function, picking the arguments in the order of Is
  65. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 71/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts) requires ((Is < sizeof...(ts)) && ...) { using Tup = std::tuple<Ts&&...>; auto tup = Tup(std::forward<Ts>(ts)...); return f(std::get<Is>(std::move(tup))...); }; };
  66. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 72/208 auto apply = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << '\n'; } } Get the differences in a sequence template <size_t ... Is> auto swizzle = []<typename F>(F&& f) { return [f = std::forward<F>(f)]<typename ... Ts>(Ts&& ... ts) requires ((Is < sizeof...(ts)) && ...) { using Tup = std::tuple<Ts&&...>; auto tup = Tup(std::forward<Ts>(ts)...); return f(std::get<Is>(std::move(tup))...); }; }; And maybe a constraint for slightly less confusing error messages
  67. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 73/208 auto apply = []<typename F>(F&& f) ... template <size_t ... Is> auto swizzle = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << ' '; } } Get the differences in a sequence
  68. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 74/208 auto apply = []<typename F>(F&& f) ... template <size_t ... Is> auto swizzle = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << ' '; } } Get the differences in a sequence apply(swizzle<1,0>(std::minus{}))
  69. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 75/208 auto apply = []<typename F>(F&& f) ... template <size_t ... Is> auto swizzle = []<typename F>(F&& f) ... int main() { std::array sequence{1,3,8,2,3,8,1}; for (auto d : sequence | std::ranges::views::pairwise | std::ranges::views::transform( apply([](auto a, auto b) { return b - a;}) )) { std::cout << d << ' '; } } Get the differences in a sequence apply(swizzle<1,0>(std::minus{}))
  70. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 77/208 IP addresses struct ip_address { ip_address(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip_address(uint32_t n) : num(n) {} bool operator==(const ip_address& rh) const = default; uint32_t num; }; struct netmask : private ip_address { using ip_address::ip_address; friend ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; } };
  71. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 78/208 IP addresses struct ip_address { ip_address(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip_address(uint32_t n) : num(n) {} bool operator==(const ip_address& rh) const = default; uint32_t num; }; struct netmask : private ip_address { using ip_address::ip_address; friend ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; } };
  72. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 79/208 IP addresses struct ip_address { ip_address(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip_address(uint32_t n) : num(n) {} bool operator==(const ip_address& rh) const = default; uint32_t num; }; struct netmask : private ip_address { using ip_address::ip_address; friend ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; } }; C++20. Default operator== and get != for free!
  73. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 80/208 IP addresses struct ip_address { ip_address(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip_address(uint32_t n) : num(n) {} bool operator==(const ip_address& rh) const = default; uint32_t num; }; struct netmask : private ip_address { using ip_address::ip_address; friend ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; } };
  74. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 81/208 IP addresses struct ip_address { ip_address(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip_address(uint32_t n) : num(n) {} bool operator==(const ip_address& rh) const = default; uint32_t num; }; struct netmask : private ip_address { using ip_address::ip_address; friend ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; } }; net masks are used to filter addresses for subnets.
  75. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 82/208 IP addresses struct ip_address { ip_address(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip_address(uint32_t n) : num(n) {} bool operator==(const ip_address& rh) const = default; uint32_t num; }; struct netmask : private ip_address { using ip_address::ip_address; friend ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; } };
  76. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 83/208 IP addresses struct ip_address { ip_address(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip_address(uint32_t n) : num(n) {} bool operator==(const ip_address& rh) const = default; uint32_t num; }; struct netmask : private ip_address { using ip_address::ip_address; friend ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; } }; The filtering is done by bitwise-and between an address and a mask
  77. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 84/208 IP addresses struct ip_address { ip_address(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip_address(uint32_t n) : num(n) {} bool operator==(const ip_address& rh) const = default; uint32_t num; }; struct netmask : private ip_address { using ip_address::ip_address; friend ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; } };
  78. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 85/208 IP addresses struct ip_address { ip_address(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip_address(uint32_t n) : num(n) {} bool operator==(const ip_address& rh) const = default; uint32_t num; }; struct netmask : private ip_address { using ip_address::ip_address; friend ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; } }; auto ip_matches = [](ip_address desired, netmask mask = {255,255,255,255}) { return [desired, mask](ip_address actual) { return (desired & mask) == (actual & mask); }; };
  79. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 86/208 IP addresses struct ip_address { ip_address(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip_address(uint32_t n) : num(n) {} bool operator==(const ip_address& rh) const = default; uint32_t num; }; struct netmask : private ip_address { using ip_address::ip_address; friend ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; } }; auto ip_matches = [](ip_address desired, netmask mask = {255,255,255,255}) { return [desired, mask](ip_address actual) { return (desired & mask) == (actual & mask); }; }; Who sees the pattern?
  80. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 87/208 IP addresses struct ip_address { ip_address(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip_address(uint32_t n) : num(n) {} bool operator==(const ip_address& rh) const = default; uint32_t num; }; struct netmask : private ip_address { using ip_address::ip_address; friend ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; } }; auto ip_matches = [](ip_address desired, netmask mask = {255,255,255,255}) { return [desired, mask](ip_address actual) { return (desired & mask) == (actual & mask); }; }; auto it = std::ranges::find_if(addresses, ip_matches({192,168,0,0}, {255,255,0,0})); Use like
  81. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 88/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; };
  82. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 89/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto get_state = std::mem_fn(&ipif::state);
  83. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 90/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto get_state = std::mem_fn(&ipif::state);
  84. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 91/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto get_state = std::mem_fn(&ipif::state); Call to get_state(ipif_obj) becomes a call to ipif_obj.state()
  85. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 92/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto get_state = std::mem_fn(&ipif::state); auto get_address = std::mem_fn(&ipif::address);
  86. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 93/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto get_state = std::mem_fn(&ipif::state); auto get_address = std::mem_fn(&ipif::address); auto get_mask = std::mem_fn(&ipif::address);
  87. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 94/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto get_state = std::mem_fn(&ipif::state); auto get_address = std::mem_fn(&ipif::address); auto get_mask = std::mem_fn(&ipif::address); auto get_gateway = std::mem_fn(&ipif::gateway);
  88. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 95/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto get_state = std::mem_fn(&ipif::state); auto get_address = std::mem_fn(&ipif::address); auto get_mask = std::mem_fn(&ipif::address); auto get_gateway = std::mem_fn(&ipif::gateway); auto set_state = [](ipif::state_type state) { return bind_back(std::mem_fn(&ipif::set_state), state); };
  89. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 96/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; };
  90. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 97/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; How can we find an ipif in a range, given an ip_address?
  91. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 98/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x))
  92. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 99/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x)) get_address(ipif) -> ip_address
  93. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 100/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x)) ip_matches(ip_address) -> bool get_address(ipif) -> ip_address
  94. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 101/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = [] (auto f1, auto f2) { return [f1 , f2 ] (auto ... ts) { return f1(f2( ts ...)); }; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x)) ip_matches(ip_address) -> bool get_address(ipif) -> ip_address
  95. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 102/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = [] (auto f1, auto f2) { return [f1 , f2 ] (auto ... ts) { return f1(f2( ts ...)); }; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x)) ip_matches(ip_address) -> bool get_address(ipif) -> ip_address Return a lambda that captures the functions
  96. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 103/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = [] (auto f1, auto f2) { return [f1 , f2 ] (auto ... ts) { return f1(f2( ts ...)); }; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x)) ip_matches(ip_address) -> bool get_address(ipif) -> ip_address Forward the arguments to the captured functions
  97. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 104/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = [] (auto f1, auto f2) { return [f1 , f2 ] (auto ... ts) { return f1(f2( ts ...)); }; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x)) ip_matches(ip_address) -> bool get_address(ipif) -> ip_address auto compose = [] (auto f1, auto f2) { return [f1 , f2 ] (auto ... ts) -> decltype(auto) { return f1(f2( ts ...)); }; }; In case f1 returns a reference
  98. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 105/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = [] (auto f1, auto f2) { return [f1 , f2 ] (auto ... ts) { return f1(f2( ts ...)); }; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x)) ip_matches(ip_address) -> bool get_address(ipif) -> ip_address auto compose = [] (auto f1, auto f2) { return [f1 , f2 ] (auto ... ts) -> decltype(auto) { return f1(f2( ts ...)); }; }; auto compose = []<typename F1, typename F2>(F1&& f1, F2&& f2) { return [f1 = std::forward<F1>(f1), f2 = std::forward<F2>(f2)] (auto ... ts) -> decltype(auto) { return f1(f2( ts ...)); }; }; Perfect forwarding of the functions to call
  99. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 106/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = [] (auto f1, auto f2) { return [f1 , f2 ] (auto ... ts) { return f1(f2( ts ...)); }; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x)) ip_matches(ip_address) -> bool get_address(ipif) -> ip_address auto compose = [] (auto f1, auto f2) { return [f1 , f2 ] (auto ... ts) -> decltype(auto) { return f1(f2( ts ...)); }; }; auto compose = []<typename F1, typename F2>(F1&& f1, F2&& f2) { return [f1 = std::forward<F1>(f1), f2 = std::forward<F2>(f2)] (auto ... ts) -> decltype(auto) { return f1(f2( ts ...)); }; }; auto compose = []<typename F1, typename F2>(F1&& f1, F2&& f2) { return [f1 = std::forward<F1>(f1), f2 = std::forward<F2>(f2)] <typename ... Ts>(Ts&& ... ts) -> decltype(auto) { return f1(f2(std::forward<Ts>(ts)...)); }; }; Perfect forwarding of arguments
  100. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 108/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators
  101. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 109/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators • There are many well known combinators
  102. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 110/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators • There are many well known combinators https://youtu.be/JELcdZLre3s
  103. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 111/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators • There are many well known combinators
  104. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 112/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators • There are many well known combinators • They have names like B, C*, O, F**, J...
  105. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 113/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators • There are many well known combinators • They have names like B, C*, O, F**, J... • And their ornithological counterparts Bluebird, Cardinal once removed, Owl, Finch twice removed, Jay...
  106. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 114/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators • There are many well known combinators • They have names like B, C*, O, F**, J... • And their ornithological counterparts Bluebird, Cardinal once removed, Owl, Finch twice removed, Jay...
  107. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 115/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators • There are many well known combinators • They have names like B, C*, O, F**, J... • And their ornithological counterparts Bluebird, Cardinal once removed, Owl, Finch twice removed, Jay... • https://hackage.haskell.org/package/data-aviary-0.4.0/docs/Data-Aviary-Birds.html
  108. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 116/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators • There are many well known combinators • They have names like B, C*, O, F**, J... • And their ornithological counterparts Bluebird, Cardinal once removed, Owl, Finch twice removed, Jay... • https://hackage.haskell.org/package/data-aviary-0.4.0/docs/Data-Aviary-Birds.html • The one I wrote is the B-combinator or Bluebird
  109. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 117/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators • There are many well known combinators • They have names like B, C*, O, F**, J... • And their ornithological counterparts Bluebird, Cardinal once removed, Owl, Finch twice removed, Jay... • https://hackage.haskell.org/package/data-aviary-0.4.0/docs/Data-Aviary-Birds.html • The one I wrote is the B-combinator or Bluebird • I just cant…
  110. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 118/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators • There are many well known combinators • They have names like B, C*, O, F**, J... • And their ornithological counterparts Bluebird, Cardinal once removed, Owl, Finch twice removed, Jay... • https://hackage.haskell.org/package/data-aviary-0.4.0/docs/Data-Aviary-Birds.html • The one I wrote is the B-combinator or Bluebird • I just cant… • https://en.wikipedia.org/wiki/Function_composition
  111. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 119/208 <rant> </rant> • In functional programming, a family of higher order functions that only deals with composing functions, is called combinators • There are many well known combinators • They have names like B, C*, O, F**, J... • And their ornithological counterparts Bluebird, Cardinal once removed, Owl, Finch twice removed, Jay... • https://hackage.haskell.org/package/data-aviary-0.4.0/docs/Data-Aviary-Birds.html • The one I wrote is the B-combinator or Bluebird • I just cant… • • I’m sticking with compose(x, y), sorry... https://en.wikipedia.org/wiki/Function_composition
  112. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 120/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = []<typename F1, typename F2>(F1&& f1, F2&& f2) { return [f1 = std::forward<F1>(f1), f2 = std::forward<F2>(f2)] <typename ... Ts>(Ts&& ... ts) -> decltype(auto) { return f1(f2(std::forward<Ts>(ts)...)); }; };
  113. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 121/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = []<typename F1, typename F2>(F1&& f1, F2&& f2) { return [f1 = std::forward<F1>(f1), f2 = std::forward<F2>(f2)] <typename ... Ts>(Ts&& ... ts) -> decltype(auto) { return f1(f2(std::forward<Ts>(ts)...)); }; }; auto with_address = [](auto addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), get_address); }; auto it = std::ranges::find_if(interfaces, compose(ip_matches({192,168,0,1}, get_address))); Use like
  114. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 122/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = []<typename F1, typename F2>(F1&& f1, F2&& f2) { return [f1 = std::forward<F1>(f1), f2 = std::forward<F2>(f2)] <typename ... Ts>(Ts&& ... ts) -> decltype(auto) { return f1(f2(std::forward<Ts>(ts)...)); }; }; auto with_address = [](auto addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), get_address); }; auto it = std::ranges::find_if(interfaces, compose(ip_matches({192,168,0,1}, get_address)));
  115. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 123/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = []<typename F1, typename F2>(F1&& f1, F2&& f2) { return [f1 = std::forward<F1>(f1), f2 = std::forward<F2>(f2)] <typename ... Ts>(Ts&& ... ts) -> decltype(auto) { return f1(f2(std::forward<Ts>(ts)...)); }; }; auto with_address = [](auto addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), get_address); }; auto it = std::ranges::find_if(interfaces, compose(ip_matches({192,168,0,1}, get_address)));
  116. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 124/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = []<typename F1, typename F2>(F1&& f1, F2&& f2) { return [f1 = std::forward<F1>(f1), f2 = std::forward<F2>(f2)] <typename ... Ts>(Ts&& ... ts) -> decltype(auto) { return f1(f2(std::forward<Ts>(ts)...)); }; }; auto with_address = [](auto addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), get_address); }; auto it = std::ranges::find_if(interfaces, compose(ip_matches({192,168,0,1}, get_address))); with_address({192,168,0,1}));
  117. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 125/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto compose = []<typename F1, typename F2>(F1&& f1, F2&& f2) { return [f1 = std::forward<F1>(f1), f2 = std::forward<F2>(f2)] <typename ... Ts>(Ts&& ... ts) -> decltype(auto) { return f1(f2(std::forward<Ts>(ts)...)); }; }; auto with_address = [](auto addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), get_address); }; auto it = std::ranges::find_if(interfaces, compose(ip_matches({192,168,0,1}, get_address))); with_address({192,168,0,1})); But what if I want to search with more than one criteria, e.g. address and state?
  118. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 126/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; };
  119. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 127/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto when_all = []<typename ... Fs>(Fs&& ... fs) { return [...fs = std::forward<Fs>(fs)](const auto& ... ts) { return (fs(ts...) && ...);         }; };
  120. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 128/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto when_all = []<typename ... Fs>(Fs&& ... fs) { return [...fs = std::forward<Fs>(fs)](const auto& ... ts) { return (fs(ts...) && ...);         }; }; C++23 syntax to capture a whole parameter pack without resorting to tuples
  121. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 129/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto when_all = []<typename ... Fs>(Fs&& ... fs) { return [...fs = std::forward<Fs>(fs)](const auto& ... ts) { return (fs(ts...) && ...);         }; }; Fold expression for logical and of the result of each function, with short cirquiting
  122. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 130/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto when_all = []<typename ... Fs>(Fs&& ... fs) { return [...fs = std::forward<Fs>(fs)](const auto& ... ts) { return (fs(ts...) && ...);         }; }; Note that we don’t want perfect forwarding of the ts...
  123. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 131/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto when_all = []<typename ... Fs>(Fs&& ... fs) { return [...fs = std::forward<Fs>(fs)](const auto& ... ts) { return (fs(ts...) && ...);         }; }; We can now improve bind_back from earlier
  124. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 132/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto when_all = []<typename ... Fs>(Fs&& ... fs) { return [...fs = std::forward<Fs>(fs)](const auto& ... ts) { return (fs(ts...) && ...);         }; }; auto bind_back = []<typename F, typename ... Ts>(F&& f, Ts&& ... ts) { return [f = std::forward<F>(f), ...ts=std::forward<Ts>(ts)] <typename ... Vs>(Vs&& ... vs) -> decltype(auto) { return f(std::forward<Vs>(vs)..., ts...); }; }; We can now improve bind_back from earlier
  125. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 133/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto with_state = [](auto state) { return compose(equals(state), get_state); }; void activate(std::span<ipif> interfaces, ip_address addr, netmask mask) { auto to_activate = interfaces | std::ranges::views::filter( when_all(with_state(ipif::off),         with_address(addr, mask))); std::ranges::for_each(to_activate, set_state(ipif::on)); }
  126. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 134/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto with_state = [](auto state) { return compose(equals(state), get_state); }; void activate(std::span<ipif> interfaces, ip_address addr, netmask mask) { auto to_activate = interfaces | std::ranges::views::filter( when_all(with_state(ipif::off),         with_address(addr, mask))); std::ranges::for_each(to_activate, set_state(ipif::on)); } We now have the tools to do something really cool with our network interfaces!
  127. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 135/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto with_state = [](auto state) { return compose(equals(state), get_state); }; void activate(std::span<ipif> interfaces, ip_address addr, netmask mask) { auto to_activate = interfaces | std::ranges::views::filter( when_all(with_state(ipif::off),         with_address(addr, mask))); std::ranges::for_each(to_activate, set_state(ipif::on)); } We now have the tools to do something really cool with our network interfaces!
  128. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 136/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto with_state = [](auto state) { return compose(equals(state), get_state); }; void activate(std::span<ipif> interfaces, ip_address addr, netmask mask) { auto to_activate = interfaces | std::ranges::views::filter( when_all(with_state(ipif::off),         with_address(addr, mask))); std::ranges::for_each(to_activate, set_state(ipif::on)); } Get all interfaces that are off We now have the tools to do something really cool with our network interfaces!
  129. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 137/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto with_state = [](auto state) { return compose(equals(state), get_state); }; void activate(std::span<ipif> interfaces, ip_address addr, netmask mask) { auto to_activate = interfaces | std::ranges::views::filter( when_all(with_state(ipif::off),         with_address(addr, mask))); std::ranges::for_each(to_activate, set_state(ipif::on)); } ...and has a matching address We now have the tools to do something really cool with our network interfaces!
  130. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 138/208 Network interfaces class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; auto with_state = [](auto state) { return compose(equals(state), get_state); }; void activate(std::span<ipif> interfaces, ip_address addr, netmask mask) { auto to_activate = interfaces | std::ranges::views::filter( when_all(with_state(ipif::off),         with_address(addr, mask))); std::ranges::for_each(to_activate, set_state(ipif::on)); } Then set their state to on We now have the tools to do something really cool with our network interfaces!
  131. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 140/208 std::optional<T> Has become extraordinarily well suited for higher order functions from C++23
  132. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 141/208 std::optional<T> https://en.cppreference.com/w/cpp/utility/optional Has become extraordinarily well suited for higher order functions from C++23
  133. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 142/208 std::optional<T> https://en.cppreference.com/w/cpp/utility/optional Has become extraordinarily well suited for higher order functions from C++23 Calls a function(T) if the optional<T> holds a value. The function must return an optional<U>. If the optional<T> did not hold a value .and_then() returns an empty optional<U>
  134. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 143/208 std::optional<T> https://en.cppreference.com/w/cpp/utility/optional Has become extraordinarily well suited for higher order functions from C++23 Calls a function(T) if the optional<T> holds a value. If the function returns a type U, then .transform() returns optional<U>. If the optional<T> did not hold a value, it returns an empty optional<U>
  135. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 144/208 std::optional<T> https://en.cppreference.com/w/cpp/utility/optional Has become extraordinarily well suited for higher order functions from C++23 If the optional<T> holds a value .or_else() returns the optional itself, otherwise it calls a function. The function must return an optional<T>.
  136. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 145/208 std::optional<T> https://en.cppreference.com/w/cpp/utility/optional Has become extraordinarily well suited for higher order functions from C++23 T can not be a reference
  137. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 146/208 std::optional<T> https://en.cppreference.com/w/cpp/utility/optional Has become extraordinarily well suited for higher order functions from C++23 T can not be a reference You can work around that with std::reference_wrapper<T>
  138. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 147/208 std::optional<T> https://en.cppreference.com/w/cpp/utility/optional Has become extraordinarily well suited for higher order functions from C++23 T can not be a reference You can work around that with std::reference_wrapper<T> T cannot be void
  139. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 148/208 std::optional<T> https://en.cppreference.com/w/cpp/utility/optional Has become extraordinarily well suited for higher order functions from C++23 T can not be a reference You can work around that with std::reference_wrapper<T> T cannot be void There is no work around for that
  140. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 149/208 std::optional<T> https://en.cppreference.com/w/cpp/utility/optional Has become extraordinarily well suited for higher order functions from C++23 T can not be a reference You can work around that with std::reference_wrapper<T> T cannot be void There is no work around for that What would optional<void> mean if it were allowed?
  141. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 150/208 std::optional<T> https://en.cppreference.com/w/cpp/utility/optional Has become extraordinarily well suited for higher order functions from C++23 T can not be a reference You can work around that with std::reference_wrapper<T> T cannot be void There is no work around for that What would optional<void> mean if it were allowed? The function to .transform() must return something
  142. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 151/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; };
  143. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 152/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; Won’t work with .transform()
  144. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 153/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; state_type set_state(state_type);
  145. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 154/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; void set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; state_type set_state(state_type); But this will!
  146. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 155/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; state_type set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; };
  147. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 156/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; state_type set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; template <typename Predicate> std::optional<std::reference_wrapper<ipif>> find_interface(std::span<ipif> interfaces, Predicate pred) { if (auto it = std::ranges::find_if(interfaces, pred); it != interfaces.end()) { return *it; } return std::nullopt; }
  148. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 157/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; state_type set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; template <typename Predicate> std::optional<std::reference_wrapper<ipif>> find_interface(std::span<ipif> interfaces, Predicate pred) { if (auto it = std::ranges::find_if(interfaces, pred); it != interfaces.end()) { return *it; } return std::nullopt; } Find an interface, given a predicate
  149. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 158/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; state_type set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; template <typename Predicate> std::optional<std::reference_wrapper<ipif>> find_interface(std::span<ipif> interfaces, Predicate pred) { if (auto it = std::ranges::find_if(interfaces, pred); it != interfaces.end()) { return *it; } return std::nullopt; } Return a reference to it, if found, or std::nullopt otherwise
  150. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 159/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; state_type set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; template <typename Predicate> std::optional<std::reference_wrapper<ipif>> find_interface(std::span<ipif> interfaces, Predicate pred) { if (auto it = std::ranges::find_if(interfaces, pred); it != interfaces.end()) { return *it; } return std::nullopt; } bool activate(std::span<ipif> interfaces, ip_address addr) { return find_interface(interfaces, with_address(addr)) .transform(set_state(ipif::on)) .has_value(); }
  151. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 160/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; state_type set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; template <typename Predicate> std::optional<std::reference_wrapper<ipif>> find_interface(std::span<ipif> interfaces, Predicate pred) { if (auto it = std::ranges::find_if(interfaces, pred); it != interfaces.end()) { return *it; } return std::nullopt; } bool activate(std::span<ipif> interfaces, ip_address addr) { return find_interface(interfaces, with_address(addr)) .transform(set_state(ipif::on)) .has_value(); } Lookup the interface from its address
  152. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 161/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; state_type set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; template <typename Predicate> std::optional<std::reference_wrapper<ipif>> find_interface(std::span<ipif> interfaces, Predicate pred) { if (auto it = std::ranges::find_if(interfaces, pred); it != interfaces.end()) { return *it; } return std::nullopt; } bool activate(std::span<ipif> interfaces, ip_address addr) { return find_interface(interfaces, with_address(addr)) .transform(set_state(ipif::on)) .has_value(); } If it was found, set its state
  153. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 162/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; state_type set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; template <typename Predicate> std::optional<std::reference_wrapper<ipif>> find_interface(std::span<ipif> interfaces, Predicate pred) { if (auto it = std::ranges::find_if(interfaces, pred); it != interfaces.end()) { return *it; } return std::nullopt; } bool activate(std::span<ipif> interfaces, ip_address addr) { return find_interface(interfaces, with_address(addr)) .transform(set_state(ipif::on)) .has_value(); } Tell if it was found
  154. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 163/208 std::optional<T> class ipif { public: using state_type = enum { off, on }; state_type set_state(state_type); state_type state() const { return state_; } ip_address address() const { return addr_; } netmask mask() const { return mask_; } ip_address gateway() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; }; template <typename Predicate> std::optional<std::reference_wrapper<ipif>> find_interface(std::span<ipif> interfaces, Predicate pred) { if (auto it = std::ranges::find_if(interfaces, pred); it != interfaces.end()) { return *it; } return std::nullopt; } bool activate(std::span<ipif> interfaces, ip_address addr) { return find_interface(interfaces, with_address(addr)) .transform(set_state(ipif::on)) .has_value(); }
  155. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 166/208 deducing this A small new C++23 feature with huge impact
  156. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 167/208 deducing this A small new C++23 feature with huge impact There will be several conference talks about this feature alone
  157. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 168/208 deducing this A small new C++23 feature with huge impact There will be several conference talks about this feature alone Currently available in MSVC and in clang-main (but not in any released version). Not yet available in gcc.
  158. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 169/208 deducing this A small new C++23 feature with huge impact There will be several conference talks about this feature alone Currently available in MSVC and in clang-main (but not in any released version). Not yet available in gcc. Thanks Corentin
  159. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 170/208 deducing this A small new C++23 feature with huge impact There will be several conference talks about this feature alone Currently available in MSVC and in clang-main (but not in any released version). Not yet available in gcc. Allows you to know the qualification of the object that a function was called in, by making “this” an explicit template parameter.
  160. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 171/208 deducing this A small new C++23 feature with huge impact There will be several conference talks about this feature alone Currently available in MSVC and in clang-main (but not in any released version). Not yet available in gcc. Allows you to know the qualification of the object that a function was called in, by making “this” an explicit template parameter. A good companion with new library utility std::forward_like<T>(u)
  161. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 172/208 deducing this auto f = []<typename Self>(this Self&&, int) { if constexpr (std::is_const_v<std::remove_reference_t<Self>>) {   std::cout << "const "; } if constexpr (std::is_lvalue_reference_v<Self>) { std::cout << "&"; } else { std::cout << "&&"; } std::cout << '\n'; };
  162. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 173/208 deducing this auto f = []<typename Self>(this Self&&, int) { if constexpr (std::is_const_v<std::remove_reference_t<Self>>) {   std::cout << "const "; } if constexpr (std::is_lvalue_reference_v<Self>) { std::cout << "&"; } else { std::cout << "&&"; } std::cout << '\n'; }; New syntax to make the this parameter explicit.
  163. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 174/208 deducing this auto f = []<typename Self>(this Self&&, int) { if constexpr (std::is_const_v<std::remove_reference_t<Self>>) {   std::cout << "const "; } if constexpr (std::is_lvalue_reference_v<Self>) { std::cout << "&"; } else { std::cout << "&&"; } std::cout << '\n'; };
  164. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 175/208 deducing this auto f = []<typename Self>(this Self&&, int) { if constexpr (std::is_const_v<std::remove_reference_t<Self>>) {   std::cout << "const "; } if constexpr (std::is_lvalue_reference_v<Self>) { std::cout << "&"; } else { std::cout << "&&"; } std::cout << '\n'; }; f(3); std::as_const(f)(5); std::move(f)(8); std::move(std::as_const(f))(13);
  165. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 176/208 deducing this auto f = []<typename Self>(this Self&&, int) { if constexpr (std::is_const_v<std::remove_reference_t<Self>>) {   std::cout << "const "; } if constexpr (std::is_lvalue_reference_v<Self>) { std::cout << "&"; } else { std::cout << "&&"; } std::cout << '\n'; }; f(3); std::as_const(f)(5); std::move(f)(8); std::move(std::as_const(f))(13); &
  166. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 177/208 deducing this auto f = []<typename Self>(this Self&&, int) { if constexpr (std::is_const_v<std::remove_reference_t<Self>>) {   std::cout << "const "; } if constexpr (std::is_lvalue_reference_v<Self>) { std::cout << "&"; } else { std::cout << "&&"; } std::cout << '\n'; }; f(3); std::as_const(f)(5); std::move(f)(8); std::move(std::as_const(f))(13); & const &
  167. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 178/208 deducing this auto f = []<typename Self>(this Self&&, int) { if constexpr (std::is_const_v<std::remove_reference_t<Self>>) {   std::cout << "const "; } if constexpr (std::is_lvalue_reference_v<Self>) { std::cout << "&"; } else { std::cout << "&&"; } std::cout << '\n'; }; f(3); std::as_const(f)(5); std::move(f)(8); std::move(std::as_const(f))(13); & const & &&
  168. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 179/208 deducing this auto f = []<typename Self>(this Self&&, int) { if constexpr (std::is_const_v<std::remove_reference_t<Self>>) {   std::cout << "const "; } if constexpr (std::is_lvalue_reference_v<Self>) { std::cout << "&"; } else { std::cout << "&&"; } std::cout << '\n'; }; f(3); std::as_const(f)(5); std::move(f)(8); std::move(std::as_const(f))(13); & const & && const &&
  169. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 180/208 deducing this auto f = []<typename Self>(this Self&&, int) { if constexpr (std::is_const_v<std::remove_reference_t<Self>>) {   std::cout << "const "; } if constexpr (std::is_lvalue_reference_v<Self>) { std::cout << "&"; } else { std::cout << "&&"; } std::cout << '\n'; }; f(3); std::as_const(f)(5); std::move(f)(8); std::move(std::as_const(f))(13); & const & && const &&
  170. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 181/208 deducing this auto f = []<typename Self>(this Self&&, int) { if constexpr (std::is_const_v<std::remove_reference_t<Self>>) {   std::cout << "const "; } if constexpr (std::is_lvalue_reference_v<Self>) { std::cout << "&"; } else { std::cout << "&&"; } std::cout << '\n'; }; f(3); std::as_const(f)(5); std::move(f)(8); std::move(std::as_const(f))(13); & const & && const &&
  171. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 182/208 deducing this auto f = []<typename Self>(this Self&&, int) { if constexpr (std::is_const_v<std::remove_reference_t<Self>>) {   std::cout << "const "; } if constexpr (std::is_lvalue_reference_v<Self>) { std::cout << "&"; } else { std::cout << "&&"; } std::cout << '\n'; }; f(3); std::as_const(f)(5); std::move(f)(8); std::move(std::as_const(f))(13); & const & && const &&
  172. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 183/208 Fixing bind_back auto bind_back = []<typename F, typename ... Ts>(F&& f, Ts&& ... ts) { return [f = std::forward<F>(f), ...ts=std::forward<Ts>(ts)] <typename Self, typename ... Vs> (this Self&&, Vs&& ... vs) -> decltype(auto) { return std::forward_like<Self>(f)( std::forward<Vs>(vs)..., std::forward_like<Self>(ts)… );         }; };
  173. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 184/208 Fixing bind_back auto bind_back = []<typename F, typename ... Ts>(F&& f, Ts&& ... ts) { return [f = std::forward<F>(f), ...ts=std::forward<Ts>(ts)] <typename Self, typename ... Vs> (this Self&&, Vs&& ... vs) -> decltype(auto) { return std::forward_like<Self>(f)( std::forward<Vs>(vs)..., std::forward_like<Self>(ts)… );         }; }; Learn if we’re called as an lvalue or rvalue, const or not, etc.
  174. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 185/208 Fixing bind_back auto bind_back = []<typename F, typename ... Ts>(F&& f, Ts&& ... ts) { return [f = std::forward<F>(f), ...ts=std::forward<Ts>(ts)] <typename Self, typename ... Vs> (this Self&&, Vs&& ... vs) -> decltype(auto) { return std::forward_like<Self>(f)( std::forward<Vs>(vs)..., std::forward_like<Self>(ts)… );         }; }; Move the captured function if the lambda is called as an rvalue, otherwise use as lvalue.
  175. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 186/208 Fixing bind_back auto bind_back = []<typename F, typename ... Ts>(F&& f, Ts&& ... ts) { return [f = std::forward<F>(f), ...ts=std::forward<Ts>(ts)] <typename Self, typename ... Vs> (this Self&&, Vs&& ... vs) -> decltype(auto) { return std::forward_like<Self>(f)( std::forward<Vs>(vs)..., std::forward_like<Self>(ts)… );         }; }; Forward the direct function call parameters as before.
  176. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 187/208 Fixing bind_back auto bind_back = []<typename F, typename ... Ts>(F&& f, Ts&& ... ts) { return [f = std::forward<F>(f), ...ts=std::forward<Ts>(ts)] <typename Self, typename ... Vs> (this Self&&, Vs&& ... vs) -> decltype(auto) { return std::forward_like<Self>(f)( std::forward<Vs>(vs)..., std::forward_like<Self>(ts)… );         }; }; Move the captured values if the lambda is an rvalue, otherwise pass as lvalue-references
  177. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 188/208 Fixing bind_back auto bind_back = []<typename F, typename ... Ts>(F&& f, Ts&& ... ts) { return [f = std::forward<F>(f), ...ts=std::forward<Ts>(ts)] <typename Self, typename ... Vs> (this Self&&, Vs&& ... vs) -> decltype(auto) { return std::forward_like<Self>(f)( std::forward<Vs>(vs)..., std::forward_like<Self>(ts)… );         }; }; Now bind_back can safely be used with types like std::unique_ptr<>, which will only be moved if called with an rvalue.
  178. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 190/208 curry https://en.wikipedia.org/wiki/Currying
  179. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 191/208 curry auto curry = []<typename F>(this auto& curry, F&& f) { return    [&curry, f = std::forward<F>(f)] ( auto ... ts) -> decltype(auto) { if constexpr (requires { f ( ts ...); }) { return f ( ts ...); } else { return curry(std::bind_front( f , ts ...)); } }; };
  180. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 192/208 curry auto curry = []<typename F>(this auto& curry, F&& f) { return    [&curry, f = std::forward<F>(f)] ( auto ... ts) -> decltype(auto) { if constexpr (requires { f ( ts ...); }) { return f ( ts ...); } else { return curry(std::bind_front( f , ts ...)); } }; }; Lambda deducing itself as a reference
  181. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 193/208 curry auto curry = []<typename F>(this auto& curry, F&& f) { return    [&curry, f = std::forward<F>(f)] ( auto ... ts) -> decltype(auto) { if constexpr (requires { f ( ts ...); }) { return f ( ts ...); } else { return curry(std::bind_front( f , ts ...)); } }; }; Lambda deducing itself as a reference So it can call itself recursively
  182. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 194/208 curry auto curry = []<typename F>(this auto& curry, F&& f) { return    [&curry, f = std::forward<F>(f)] ( auto ... ts) -> decltype(auto) { if constexpr (requires { f ( ts ...); }) { return f ( ts ...); } else { return curry(std::bind_front( f , ts ...)); } }; }; The call is made if it can be
  183. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 195/208 curry auto curry = []<typename F>(this auto& curry, F&& f) { return    [&curry, f = std::forward<F>(f)] ( auto ... ts) -> decltype(auto) { if constexpr (requires { f ( ts ...); }) { return f ( ts ...); } else { return curry(std::bind_front( f , ts ...)); } }; }; Otherwise bind the arguments and curry the tail
  184. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 196/208 curry auto curry = []<typename F>(this auto& curry, F&& f) { return    [&curry, f = std::forward<F>(f)]<typename Self > (this Self&&, auto ... ts) -> decltype(auto) { if constexpr (requires { std::forward_like<Self>(f)( ts ...); }) { return std::forward_like<Self>(f)( ts ...); } else { return curry(std::bind_front(std::forward_like<Self>(f), ts ...)); } }; };
  185. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 197/208 curry auto curry = []<typename F>(this auto& curry, F&& f) { return    [&curry, f = std::forward<F>(f)]<typename Self > (this Self&&, auto ... ts) -> decltype(auto) { if constexpr (requires { std::forward_like<Self>(f)( ts ...); }) { return std::forward_like<Self>(f)( ts ...); } else { return curry(std::bind_front(std::forward_like<Self>(f), ts ...)); } }; }; Now deduce the type of the inner lambda
  186. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 198/208 curry auto curry = []<typename F>(this auto& curry, F&& f) { return    [&curry, f = std::forward<F>(f)]<typename Self > (this Self&&, auto ... ts) -> decltype(auto) { if constexpr (requires { std::forward_like<Self>(f)( ts ...); }) { return std::forward_like<Self>(f)( ts ...); } else { return curry(std::bind_front(std::forward_like<Self>(f), ts ...)); } }; }; So the bound function can be called as r/l- value So the bound function can be called as r/l- value So the bound function can be called as r/l- value
  187. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 199/208 curry auto curry = []<typename F>(this auto& curry, F&& f) { return    [&curry, f = std::forward<F>(f)]<typename Self, typename ... Ts> (this Self&&, Ts&& ... ts) -> decltype(auto) { if constexpr (requires { std::forward_like<Self>(f)(std::forward<Ts>(ts)...); }) { return std::forward_like<Self>(f)(std::forward<Ts>(ts)...); } else { return curry(std::bind_front(std::forward_like<Self>(f), std::forward<Ts>(ts)...)); } }; };
  188. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 200/208 curry auto curry = []<typename F>(this auto& curry, F&& f) { return    [&curry, f = std::forward<F>(f)]<typename Self, typename ... Ts> (this Self&&, Ts&& ... ts) -> decltype(auto) { if constexpr (requires { std::forward_like<Self>(f)(std::forward<Ts>(ts)...); }) { return std::forward_like<Self>(f)(std::forward<Ts>(ts)...); } else { return curry(std::bind_front(std::forward_like<Self>(f), std::forward<Ts>(ts)...)); } }; }; Lambda deducing itself as a reference Perfect forwarding of the arguments Perfect forwarding of the arguments Perfect forwarding of the arguments
  189. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 203/208 Summary • Higher order functions in general, and function composition in particular, is powerful!
  190. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 204/208 Summary • Higher order functions in general, and function composition in particular, is powerful! • They are very suitable for new library features like ranges and the new functionality of optional<T> and expected<T,E>
  191. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 205/208 Summary • Higher order functions in general, and function composition in particular, is powerful! • They are very suitable for new library features like ranges and the new functionality of optional<T> and expected<T,E> • New language features like variadic lambda capture makes life easier
  192. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 206/208 Summary • Higher order functions in general, and function composition in particular, is powerful! • They are very suitable for new library features like ranges and the new functionality of optional<T> and expected<T,E> • New language features like variadic lambda capture makes life easier • “deducing this” is a new super power!
  193. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 207/208 Summary • Higher order functions in general, and function composition in particular, is powerful! • They are very suitable for new library features like ranges and the new functionality of optional<T> and expected<T,E> • New language features like variadic lambda capture makes life easier • “deducing this” is a new super power! – https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/
  194. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @[email protected] 208/208 Björn Fahller Function Moar with C++23 [email protected] @[email protected] @rollbear Björn Fahller