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

MeetingC++ : Higher order functions for ordinary C++ developers

MeetingC++ : Higher order functions for ordinary C++ developers

Higher order functions, i.e. functions that accept functions as parameters, or return functions, are not used much by C++ developers, except for the occasional call to standard algorithms.

This session will show some simple techniques for writing your own, that will lift the level of abstraction in your programs, making code easier to read while reducing code duplication, and maintaining performance.

Björn Fahller

November 16, 2018
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 1/93 compose([](auto const& s) { return s == "foo";}, std=:mem_fn(&foo=:name)) Higher Order Functions for Ordinary C++ Developers Björn Fahller
  2. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 2/93 Very brief intro to higher order functions Gradually expanding example optional<T> (and extension) Managing overload sets Higher Order Functions for Ordinary C++ Developers
  3. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 3/93 Definition A higher-order function is a function that takes other functions as arguments or returns a function as result.
  4. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 4/93 #include <algorithm> #include <vector> std=:vector<int> v; ==. if (std=:none_of(std=:begin(v), std=:end(v), [](int x) { return x == 0; })) { ==. }
  5. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 5/93 #include <algorithm> #include <vector> std=:vector<int> v; ==. if (std=:none_of(std=:begin(v), std=:end(v), [](int x) { return x == 0; })) { ==. } template <typename Iterator, typename Predicate> bool none_of(Iterator i, Iterator e, Predicate predicate) { while (i == e) { if (predicate(*i)) return false; i=+; } return true; }
  6. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 6/93 #include <algorithm> #include <vector> std=:vector<int> v; ==. if (std=:none_of(std=:begin(v), std=:end(v), [](int x) { return x == 0; })) { ==. } ==. int num; ==. while (std=:any_of(std=:begin(v), std=:end(v), [num](int x){ return x == num; })) { ==. }
  7. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 7/93 [num](int x){ return x == num; }
  8. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 8/93 auto equals(int num) { return [num](int x){ return x == num; }; }
  9. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 9/93 auto equals(int num) { return [num](int x){ return x == num; }; } std=:function<bool(int)> equals(int num) { return [num](int x) { return x == num; }; }
  10. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 10/93 template <typename T> auto equals(T key) { return [key](auto const& x){ return x == key; }; }
  11. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 11/93 template <typename T> auto equals(T key) { return [key](auto const& x){ return x == key; }; } template <typename T> std=:function<bool(T)> equals(T key) { return [key](T x) { return x == key; }; }
  12. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 12/93 template <typename T> auto equals(T key) { return [key](auto const& x){ return x == key; }; } template <typename T> std=:function<bool(T)> equals(T key) { return [key](T x) { return x == key; }; } Callable with anything equality comparable with T, for example C-string and std=:string
  13. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 13/93 template <typename T> auto equals(T key) { return [key](auto const& x){ return x == key; }; } template <typename T> std=:function<bool(T)> equals(T key) { return [key](T x) { return x == key; }; } Callable with anything equality comparable with T, for example C-string and std=:string Only callable with T, because std=:function=> does not have a templated function call operator.
  14. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 14/93 template <typename T> auto equals(T key) { return [key](auto const& x){ return x == key; }; }
  15. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 15/93 #include <algorithm> #include <vector> std=:vector<int> v; ==. if (std=:none_of(std=:begin(v), std=:end(v), equals(0)) { ==. } ==. int num; ==. while (std=:any_of(std=:begin(v), std=:end(v), equals(num)) { ==. }
  16. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 17/93 ✔Very brief intro to higher order functions Gradually expanding example optional<T> (and extension) Managing overload sets Higher Order Functions for Ordinary C++ Developers
  17. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 18/93 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==(ip_address rh) const { return num == rh.num;} bool operator==(ip_address rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip_address { using ip_address=:ip_address; }; inline ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; };
  18. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 19/93 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==(ip_address rh) const { return num == rh.num;} bool operator==(ip_address rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip_address { using ip_address=:ip_address; }; inline ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; };
  19. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 20/93 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==(ip_address rh) const { return num == rh.num;} bool operator==(ip_address rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip_address { using ip_address=:ip_address; }; inline ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; };
  20. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 21/93 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==(ip_address rh) const { return num == rh.num;} bool operator==(ip_address rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip_address { using ip_address=:ip_address; }; inline ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; };
  21. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 22/93 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==(ip_address rh) const { return num == rh.num;} bool operator==(ip_address rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip_address { using ip_address=:ip_address; }; inline ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; }; auto ip_matches(ip_address desired, netmask mask = netmask{255,255,255,255}) { return [desired, mask](ip_address actual) { return (desired & mask) == (actual & mask); }; }
  22. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 23/93 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==(ip_address rh) const { return num == rh.num;} bool operator==(ip_address rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip_address { using ip_address=:ip_address; }; inline ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; }; auto ip_matches(ip_address desired, netmask mask = netmask{255,255,255,255}) { return [desired, mask](ip_address actual) { return (desired & mask) == (actual & mask); }; }
  23. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 24/93 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==(ip_address rh) const { return num == rh.num;} bool operator==(ip_address rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip_address { using ip_address=:ip_address; }; inline ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; }; auto ip_matches(ip_address desired, netmask mask = netmask{255,255,255,255}) { return [desired, mask](ip_address actual) { return (desired & mask) == (actual & mask); }; }
  24. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 25/93 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==(ip_address rh) const { return num == rh.num;} bool operator==(ip_address rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip_address { using ip_address=:ip_address; }; inline ip_address operator&(ip_address lh, netmask rh) { return {lh.num & rh.num}; }; auto ip_matches(ip_address desired, netmask mask = netmask{255,255,255,255}) { return [desired, mask](ip_address actual) { return (desired & mask) == (actual & mask); }; } std=:vector<ip_address> v; ==. auto i = std=:remove_if(v.begin(), v.end(), ip_matches({192,168,1,1}, {255,255,0,0}));
  25. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 26/93 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_; };
  26. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 27/93 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_; };
  27. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 28/93 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_; }; To match, for example the address of an ipif, we need to make the ip_matches() predicate work on a member.
  28. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 29/93 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))
  29. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 30/93 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 ip_matches(ip_address) -> bool and f2(x) -> y select_address(ipif) -> ip_address We want a composition f(x)->z as f1(f2(x))
  30. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 31/93 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_; }; template <typename F1, typename F2> auto compose(F1 f1, F2 f2) { return [=](auto const& x) { return f1(f2(x)); }; } Given: f1(y) -> z ip_matches(ip_address) -> bool and f2(x) -> y select_address(ipif) -> ip_address We want a composition f(x)->z as f1(f2(x))
  31. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 32/93 class ipif { public: ==. ip_address address() const { return addr_;} ==. private: ip_address addr_; ==. }; std=:vector<ipif> interfaces; auto i = std=:find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), select_address));
  32. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 33/93 class ipif { public: ==. ip_address address() const { return addr_;} ==. private: ip_address addr_; ==. }; ip_address select_address(ipif const& interface) { return interface.address(); } std=:vector<ipif> interfaces; auto i = std=:find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), select_address));
  33. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 34/93 class ipif { public: ==. ip_address address() const { return addr_;} ==. private: ip_address addr_; ==. }; auto address_matches(ip_address addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), select_address); } std=:vector<ipif> interfaces; auto i = std=:find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), select_address);
  34. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 35/93 class ipif { public: ==. ip_address address() const { return addr_;} ==. private: ip_address addr_; ==. }; auto address_matches(ip_address addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), select_address); } std=:vector<ipif> interfaces; auto i = std=:find_if(interfaces.begin(), interfaces.end(), address_matches({192,168,1,1}));
  35. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 36/93 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_; } ==. }; inline ip_address select_gateway(ipif const& interface) { return interface.gateway(); } inline ipif=:state_type select_state(ipif const& interface) { return interface.state(); }
  36. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 37/93 auto i = find_if(v.begin(), v.end(), when_all(address_matches({192,168,1,1},{255,255,0,0}), state_is(ipif=:off)));
  37. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 38/93 template <typename ==. Predicates> auto when_all(Predicates ==. ps) { return [=](auto const& x) { return (ps(x) =& ==.); }; } auto i = find_if(v.begin(), v.end(), when_all(address_matches({192,168,1,1},{255,255,0,0}), state_is(ipif=:off)));
  38. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 39/93 template <typename ==. Predicates> auto when_all(Predicates ==. ps) { return [=](auto const& x) { return (ps(x) =& ==.); }; } auto address_matches(ip_address addr, netmask mask=netmask{255,255,255,255}) { return compose(ip_matches(addr, mask), select_addr); } auto i = find_if(v.begin(), v.end(), when_all(address_matches({192,168,1,1},{255,255,0,0}), state_is(ipif=:off)));
  39. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 40/93 template <typename ==. Predicates> auto when_all(Predicates ==. ps) { return [=](auto const& x) { return (ps(x) =& ==.); }; } auto address_matches(ip_address addr, netmask mask=netmask{255,255,255,255}) { return compose(ip_matches(addr, mask), select_addr); } auto state_is(ipif=:state_type state) { return compose(equals(state), select_state); } auto i = find_if(v.begin(), v.end(), when_all(address_matches({192,168,1,1},{255,255,0,0}), state_is(ipif=:off)));
  40. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 41/93 for_each(v.begin(), v.end(), if_then(when_all(address_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif=:off)), set_state(ipif=:on)));
  41. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 42/93 template <typename Predicate, typename Action> auto if_then(Predicate predicate, Action action) { return [=](auto=& x) { if (predicate(x)) { action(std=:forward<decltype(x)>(x)); } }; } for_each(v.begin(), v.end(), if_then(when_all(address_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif=:off)), set_state(ipif=:on)));
  42. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 43/93 template <typename Predicate, typename Action> auto if_then(Predicate predicate, Action action) { return [=](auto=& x) { if (predicate(x)) { action(std=:forward<decltype(x)>(x)); } }; } auto set_state(ipif=:state_type state) { return [=](ipif& interface) { interface.set_state(state); }; } for_each(v.begin(), v.end(), if_then(when_all(address_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif=:off)), set_state(ipif=:on)));
  43. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 45/93 ✔Very brief intro to higher order functions ✔Gradually expanding example optional<T> (and extension) Managing overload sets Higher Order Functions for Ordinary C++ Developers
  44. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 47/93 std=:optional<T> • The class template std=:optional manages an optional contained value, i.e. a value that may or may not be present.
  45. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 48/93 std=:optional<T> • The class template std=:optional manages an optional contained value, i.e. a value that may or may not be present. • Since C++17 – available for older versions in various open source libraries
  46. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 49/93 std=:optional<T> • The class template std=:optional manages an optional contained value, i.e. a value that may or may not be present. • Since C++17 – available for older versions in various open source libraries • Very useful in lookup functions
  47. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 50/93 std=:optional<T> • The class template std=:optional manages an optional contained value, i.e. a value that may or may not be present. • Since C++17 – available for older versions in various open source libraries • Very useful in lookup functions • T must not be a reference – Minor inconvenience, use std=:reference_wrapper<T>
  48. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 51/93 template <typename Predicate> std=:optional<std=:reference_wrapper<ipif=> lookup(std=:vector<ipif>& ifs, Predicate pred) { auto iter = std=:find_if(ifs.begin(), ifs.end(), pred); if (iter == ifs.end()) return std=:nullopt; return {*iter}; }
  49. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 52/93 template <typename Predicate> std=:optional<std=:reference_wrapper<ip_if=> lookup(std=:vector<ipif>& ifs, Predicate pred) { auto iter = std=:find_if(ifs.begin(), ifs.end(), pred); if (iter == ifs.end()) return std=:nullopt; return {*iter}; } void do_stuff(ipif&); void some_func() { =/==. auto found = lookup(ip_interfaces, address_matches({192,168,1,1})); if (found) { do_stuff(found.value()); } =/ }
  50. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 53/93 Simon Brand @tartanllama https:=/github.com/TartanLlama/optional C++11/14/17 std=:optional with functional-style extensions and reference support https:=/optional.tartanllama.xyz
  51. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 54/93 Simon Brand @tartanllama https:=/github.com/TartanLlama/optional C++11/14/17 std=:optional with functional-style extensions and reference support https:=/optional.tartanllama.xyz Standards proposal P0798R2: Monadic operations for std::optional.
  52. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 55/93 Simon Brand @tartanllama https:=/github.com/TartanLlama/optional C++11/14/17 std=:optional with functional-style extensions and reference support https:=/optional.tartanllama.xyz #include <tl/optional.hpp> int i = 3; tl=:optional<int&> v; =/ no value v = i; =/ v refers to i
  53. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 56/93 Simon Brand @tartanllama https:=/github.com/TartanLlama/optional C++11/14/17 std=:optional with functional-style extensions and reference support https:=/optional.tartanllama.xyz #include <tl/optional.hpp> int i = 3; tl=:optional<int&> v; =/ no value v = i; =/ v refers to i v.value() = 4; =/ i == 4
  54. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 57/93 Simon Brand @tartanllama https:=/github.com/TartanLlama/optional C++11/14/17 std=:optional with functional-style extensions and reference support https:=/optional.tartanllama.xyz #include <tl/optional.hpp> int i = 3; tl=:optional<int&> v; =/ no value v = i; =/ v refers to i v.value() = 4; =/ i == 4 int j = 2; v = j; =/ i == 4. v bound to j
  55. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 58/93 Simon Brand @tartanllama https:=/github.com/TartanLlama/optional C++11/14/17 std=:optional with functional-style extensions and reference support https:=/optional.tartanllama.xyz #include <tl/optional.hpp> int i = 3; tl=:optional<int&> v; =/ no value v = i; =/ v refers to i v.value() = 4; =/ i == 4 int j = 2; v = j; =/ i == 4. v bound to j ==. v.and_then([&v](int& n) { n = 2; return v;}); v.or_else([](){std=:cout =< "nothing\n"; }); v.or_else([&](){return tl=:optional<int&>{i};});
  56. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 59/93 Simon Brand @tartanllama https:=/github.com/TartanLlama/optional C++11/14/17 std=:optional with functional-style extensions and reference support https:=/optional.tartanllama.xyz #include <tl/optional.hpp> int i = 3; tl=:optional<int&> v; =/ no value v = i; =/ v refers to i v.value() = 4; =/ i == 4 int j = 2; v = j; =/ i == 4. v bound to j ==. v.and_then([&v](int& n) { n = 2; return v;}) .or_else([](){std=:cout =< "nothing\n"; }) .or_else([&](){return tl=:optional<int&>{i};});
  57. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 60/93 template <typename Predicate> tl=:optional<ipif&> lookup(std=:vector<ipif>& ifs, Predicate pred) { auto iter = std=:find_if(ifs.begin(), ifs.end(), pred); if (iter == ifs.end()) return tl=:nullopt; return {*iter}; } void do_stuff(ipif&); void some_func() { =/==. auto found = lookup(ip_interfaces, address_matches({192,168,1,1})); if (found) { do_stuff(found.value()); } =/ }
  58. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 61/93 template <typename Predicate> tl=:optional<ipif&> lookup(std=:vector<ipif>& ifs, Predicate pred) { auto iter = std=:find_if(ifs.begin(), ifs.end(), pred); if (iter == ifs.end()) return tl=:nullopt; return {*iter}; } tl=:optional<ipif&> do_stuff(ipif&); void some_func() { =/==. lookup(ip_interfaces, address_matches({192,168,1,1})) .and_then(do_stuff); =/ }
  59. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 62/93 template <typename Predicate> tl=:optional<ipif&> lookup(std=:vector<ipif>& ifs, Predicate pred) { auto iter = std=:find_if(ifs.begin(), ifs.end(), pred); if (iter == ifs.end()) return tl=:nullopt; return {*iter}; } void some_func() { =/==. lookup(ip_interfaces, address_matches({192,168,1,1})) .and_then(set_state(ipif=:off)); =/ }
  60. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 63/93 template <typename Predicate> tl=:optional<ipif&> lookup(std=:vector<ipif>& ifs, Predicate pred) { auto iter = std=:find_if(ifs.begin(), ifs.end(), pred); if (iter == ifs.end()) return tl=:nullopt; return {*iter}; } void some_func() { =/==. lookup(ip_interfaces, address_matches({192,168,1,1})) .and_then(set_state(ipif=:off)) .or_else([](){std=:cerr =< “Failed\n”;}); =/ }
  61. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 65/93 ✔Very brief intro to higher order functions ✔Gradually expanding example ✔optional<T> (and extension) Managing overload sets Higher Order Functions for Ordinary C++ Developers
  62. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 66/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); }
  63. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 67/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif>& const v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), select_addr); return rv; }
  64. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 68/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif>& const v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), select_addr); return rv; }
  65. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 69/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif>& const v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), select_addr); return rv; } example.cpp <source>(40): error C2672: 'std=:transform': no matching overloaded function found <source>(40): error C2783: '_OutIt std=:transform(_InIt,_InIt,_OutIt,_Fn1)': could not deduce template argument for '_Fn1'
  66. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 70/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif>& const v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), select_addr); return rv; } <source>:38:5: error: no matching function for call to 'transform' std=:transform(std=:begin(v), std=:end(v), ^~~~~~~~~~~~~~ ==./include/c=+/7.2.0/bits/stl_algo.h:4295:5: note: candidate template ignored: couldn't infer template argument '_UnaryOperation' transform(_InputIterator =_first, _InputIterator =_last, ^ ==./include/c=+/7.2.0/bits/stl_algo.h:4332:5: note: candidate function template not viable: requires 5 arguments, but 4 were provided transform(_InputIterator1 =_first1, _InputIterator1 =_last1, ^
  67. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 71/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif> const& v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), select_addr); return rv; }
  68. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 72/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif> const& v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), select_addr); return rv; } How to solve this?
  69. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 73/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif> const& v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), select_addr); return rv; } Solution #1
  70. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 74/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif> const& v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), select_addr); return rv; } ¯\_( ツ )_/¯
  71. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 75/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif> const& v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), select_addr); return rv; } ¯\_( ツ )_/¯ • This is rarely a problem, so why worry? • Arrange your code such that only one version is needed in the same source file.
  72. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 76/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif> const& v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), select_addr); return rv; } Solution #2
  73. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 77/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif> const& v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), [](auto const& p) { return select_addr(p); }); return rv; } • Call through a generic lambda for the access
  74. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 78/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif> const& v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), [](auto const& p) { return select_addr(p); }); return rv; } • Call through a generic lambda for the access + OK to write and use occasionally - Becomes cumbersome if needed frequently
  75. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 79/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif> const& v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), select_addr); return rv; } Solution #2b
  76. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 80/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif> const& v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), LIFT(select_addr)); return rv; } • Macro for a generic lambda for the access #define LIFT(func) \ [](auto=& ==. p) \ { \ return func(std=:forward<decltype(p)>(p)==.)); \ }
  77. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 81/93 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); } class street_address; class customer { ==. street_address addr() const { return addr_; } }; street_address select_addr(customer const& c) { return c.addr(); } std=:vector<ip_address> addresses(std=:vector<ipif> const& v) { std=:vector<ip_address> rv; std=:transform(std=:begin(v), std=:end(v), std=:back_inserter(rv), LIFT(select_addr)); return rv; } • Macro for a generic lambda for the access #define LIFT(func) \ [](auto=& ==. p) \ { \ return func(std=:forward<decltype(p)>(p)==.)); \ } + Fairly easy to use - Macros are evil
  78. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 83/93 ✔Very brief intro to higher order functions ✔Gradually expanding example ✔optional<T> (and extension) ✔Managing overload sets Higher Order Functions for Ordinary C++ Developers
  79. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 84/93 Domain specific functions • ip_matches(ip, mask) • select_address(ipif) • select_gateway(ipif) • select_state(ipif) • set_state(ipif&) Composed domain specific functions • address_matches(ip) • state_is(state_type) Generic library functions • equals(value) ... • compose(function...) • if_then(pred,action) • when_all(predicate...) • when_none(predicate...) • when_any(predicate...) • do_all(action...) • LIFT(overload_set)
  80. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 85/93 https://github.com/rollbear/lift Generic library functions • equals(value) ... • compose(function...) • if_then(pred,action) • when_all(predicate...) • when_none(predicate...) • when_any(predicate...) • do_all(action...) • LIFT(overload_set) Domain specific functions • ip_matches(ip, mask) • select_address(ipif) • select_gateway(ipif) • select_state(ipif) • set_state(ipif&) Composed domain specific functions • address_matches(ip) • state_is(state_type)
  81. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 86/93 https://github.com/rollbear/lift Generic library functions • equals(value) ... • compose(function...) • if_then(pred,action) • when_all(predicate...) • when_none(predicate...) • when_any(predicate...) • do_all(action...) • LIFT(overload_set) Domain specific functions • ip_matches(ip, mask) • select_address(ipif) • select_gateway(ipif) • select_state(ipif) • set_state(ipif&) Composed domain specific functions • address_matches(ip) • state_is(state_type) As of Boost 1.67.0 (April 14th, 2018) boost::hof (higher order functions) https://www.boost.org/doc/libs/1_68_0/libs/hof/
  82. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 87/93 Take away messages • Write functions that uses auto return type to create lambdas
  83. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 88/93 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state
  84. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 89/93 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state • Compose functions • And give names to the compositions
  85. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 90/93 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state • Compose functions • And give names to the compositions • Functional extensions to optional<T> and expected<T,E> removes the need for many conditionals
  86. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 91/93 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state • Compose functions • And give names to the compositions • Functional extensions to optional<T> and expected<T,E> removes the need for many conditionals • Overloads can be a bit of a bother.
  87. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 92/93 Resources Pacific++ 2018: Toby Allsopp “Surfacing Composition” www.youtube.com/watch?v=_x1EvX-q3QU CppCon 2018: Simon Brand “Overloading: The Bane of All Higher-Order Functions” www.youtube.com/watch?v=L_QKlAx31Pw https://www.boost.org/doc/libs/1_68_0/libs/hof/ https://github.com/rollbear/lift Functional Programming in C++ Ivan Čukić ISBN 1617293814
  88. Higher Order Functions – Meeting C++ 2018 © Björn Fahller

    @bjorn_fahller 93/93 Björn Fahller Higher Order Functions for Ordinary C++ Developers [email protected] @bjorn_fahller @rollbear