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

    @bjorn_fahller@mastodon.social 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

    @bjorn_fahller@mastodon.social 2/208
  3. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 3/208 Function Moar with C++23
  4. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 4/208 Function Moar with C++23 I did a talk on this topic in 2018
  5. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

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

    @bjorn_fahller@mastodon.social 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
  7. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  8. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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?
  9. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  10. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  11. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  12. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  13. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  14. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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;}); }
  15. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  16. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 16/208 Test for all numbers being positive
  17. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  18. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  19. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  20. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  21. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  22. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 22/208 Test for all numbers being positive
  23. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 23/208 Test for all numbers being positive Added to the standard library in C++23
  24. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 24/208 Test for all numbers being positive Added to the standard library in C++23
  25. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  26. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

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

    @bjorn_fahller@mastodon.social 27/208 Test for all numbers being positive Sadly missing in most stdlib implementations
  28. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

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

    @bjorn_fahller@mastodon.social 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.
  30. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  31. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)); }
  32. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  33. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 33/208 https://godbolt.org/z/47cKvn36T
  34. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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'; } }
  35. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  36. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  37. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  38. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  39. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  40. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  41. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  42. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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'; } }
  43. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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'; } }
  44. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  45. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  46. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  47. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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?
  48. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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?
  49. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 49/208 Get the differences in a sequence
  50. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)); }; };
  51. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  52. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  53. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  54. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  55. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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'; } }
  56. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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;})
  57. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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!
  58. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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?
  59. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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?
  60. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)
  61. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  62. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  63. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)
  64. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  65. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)
  66. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  67. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)
  68. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  69. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)
  70. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  71. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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))...); }; };
  72. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  73. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  74. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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{}))
  75. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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{}))
  76. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 76/208 https://godbolt.org/z/jqdYTrcvr
  77. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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}; } };
  78. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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}; } };
  79. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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!
  80. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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}; } };
  81. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  82. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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}; } };
  83. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  84. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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}; } };
  85. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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); }; };
  86. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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?
  87. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  88. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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_; };
  89. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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);
  90. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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);
  91. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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()
  92. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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);
  93. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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);
  94. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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);
  95. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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); };
  96. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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_; };
  97. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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?
  98. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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))
  99. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  100. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  101. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  102. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  103. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  104. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  105. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  106. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  107. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 107/208 <rant> </rant>
  108. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

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

    @bjorn_fahller@mastodon.social 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
  110. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  111. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  112. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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...
  113. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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...
  114. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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...
  115. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  116. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  117. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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…
  118. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  119. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  120. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)...)); }; };
  121. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  122. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)));
  123. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)));
  124. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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}));
  125. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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?
  126. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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_; };
  127. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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...) && ...);         }; };
  128. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  129. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  130. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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...
  131. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  132. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  133. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)); }
  134. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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!
  135. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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!
  136. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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!
  137. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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!
  138. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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!
  139. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 139/208 https://godbolt.org/z/j1szzjcEv
  140. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 140/208 std::optional<T> Has become extraordinarily well suited for higher order functions from C++23
  141. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  142. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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>
  143. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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>
  144. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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>.
  145. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  146. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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>
  147. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  148. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  149. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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?
  150. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  151. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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_; };
  152. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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()
  153. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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);
  154. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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!
  155. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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_; };
  156. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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; }
  157. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  158. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  159. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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(); }
  160. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  161. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  162. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  163. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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(); }
  164. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 164/208 https://godbolt.org/z/4vW8Gb9e9
  165. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 165/208 deducing this
  166. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 166/208 deducing this A small new C++23 feature with huge impact
  167. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 167/208 deducing this A small new C++23 feature with huge impact There will be several conference talks about this feature alone
  168. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  169. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  170. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  171. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)
  172. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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'; };
  173. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  174. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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'; };
  175. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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);
  176. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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); &
  177. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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 &
  178. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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 & &&
  179. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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 &&
  180. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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 &&
  181. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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 &&
  182. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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 &&
  183. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)… );         }; };
  184. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  185. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  186. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  187. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  188. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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.
  189. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 189/208 https://godbolt.org/z/vf13Y961W
  190. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 190/208 curry https://en.wikipedia.org/wiki/Currying
  191. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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 ...)); } }; };
  192. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  193. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  194. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  195. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  196. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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 ...)); } }; };
  197. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  198. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  199. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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)...)); } }; };
  200. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  201. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 201/208 https://godbolt.org/z/xjTehGs45
  202. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 202/208 Summary
  203. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 203/208 Summary • Higher order functions in general, and function composition in particular, is powerful!
  204. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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>
  205. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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
  206. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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!
  207. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 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/
  208. Function Moar with C++23 – MUC++ 2023 © Björn Fahller

    @bjorn_fahller@mastodon.social 208/208 Björn Fahller Function Moar with C++23 bjorn@fahller.se @bjorn_fahller@mastodon.social @rollbear Björn Fahller