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

NDC{Oslo} Higher order functions for ordinary C...

NDC{Oslo} 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

June 13, 2018
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    1/94 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 – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    2/94 Very brief intro to higher order functions Gradually expanding example optional<T> (and extension) Working with overloads Higher Order Functions for Ordinary C++ Developers
  3. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    3/94 Definition A higher-order function is a function that takes other functions as arguments or returns a function as result.
  4. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    4/94 #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 – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    5/94 #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 – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    6/94 #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 – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    8/94 auto equals(int num) { return [num](int x){ return x == num; }; }
  8. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    9/94 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; }; }
  9. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

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

    11/94 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; }; }
  11. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    12/94 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
  12. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    13/94 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.
  13. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

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

    15/94 #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)) { ==. }
  15. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    17/94 ✔Very brief intro to higher order functions Gradually expanding example optional<T> (and extension) Working with overloads Higher Order Functions for Ordinary C++ Developers
  16. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    18/94 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}; };
  17. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    19/94 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 – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    20/94 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 – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    21/94 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 – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    22/94 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); }; }
  21. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    23/94 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}));
  22. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    24/94 class ipif { public: using state_type = enum { off, on }; ==. void set_state(state_type); state_type state() const { return state_; } ip_address addr() const { return addr_; } netmask mask() const { return mask_; } ip_address gw() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; };
  23. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    25/94 class ipif { public: using state_type = enum { off, on }; ==. void set_state(state_type); state_type state() const { return state_; } ip_address addr() const { return addr_; } netmask mask() const { return mask_; } ip_address gw() const { return gw_; } private: ip_address addr_; netmask mask_; ip_address gw_; state_type state_; };
  24. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    26/94 class ipif { public: using state_type = enum { off, on }; ==. void set_state(state_type); state_type state() const { return state_; } ip_address addr() const { return addr_; } netmask mask() const { return mask_; } ip_address gw() 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.
  25. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    27/94 class ipif { public: using state_type = enum { off, on }; ==. void set_state(state_type); state_type state() const { return state_; } ip_address addr() const { return addr_; } netmask mask() const { return mask_; } ip_address gw() 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))
  26. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    28/94 class ipif { public: using state_type = enum { off, on }; ==. void set_state(state_type); state_type state() const { return state_; } ip_address addr() const { return addr_; } netmask mask() const { return mask_; } ip_address gw() 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_addr(ipif) -> ip_address We want a composition f(x)->z as f1(f2(x))
  27. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    29/94 class ipif { public: using state_type = enum { off, on }; ==. void set_state(state_type); state_type state() const { return state_; } ip_address addr() const { return addr_; } netmask mask() const { return mask_; } ip_address gw() 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 [=](const auto& x) { return f1(f2(x)); }; } Given: f1(y) -> z ip_matches(ip_address) -> bool and f2(x) -> y select_addr(ipif) -> ip_address We want a composition f(x)->z as f1(f2(x))
  28. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    30/94 class ipif { public: ==. ip_address addr() 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_addr));
  29. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    31/94 class ipif { public: ==. ip_address addr() const { return addr_;} ==. private: ip_address addr_; ==. }; ip_address select_addr(ipif const& interface) { return interface.addr(); } std=:vector<ipif> interfaces; auto i = std=:find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), select_addr));
  30. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    32/94 class ipif { public: ==. ip_address addr() const { return addr_;} ==. private: ip_address addr_; ==. }; auto addr_matches(ip_address addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), select_addr); } std=:vector<ipif> interfaces; auto i = std=:find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), select_addr);
  31. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    33/94 class ipif { public: ==. ip_address addr() const { return addr_;} ==. private: ip_address addr_; ==. }; auto addr_matches(ip_address addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), select_addr); } std=:vector<ipif> interfaces; auto i = std=:find_if(interfaces.begin(), interfaces.end(), addr_matches({192,168,1,1}));
  32. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    34/94 class ipif { public: using state_type = enum { off, on }; ==. void set_state(state_type); state_type state() const { return state_; } ip_address addr() const { return addr_;} netmask mask() const { return mask_; } ip_address gw() const { return gw_; } ==. }; inline ip_address select_gw(ipif const& interface) { return interface.gw(); } inline ipif=:state_type select_state(ipif const& interface) { return interface.state(); }
  33. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    35/94 auto i = find_if(v.begin(), v.end(), when_all(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif=:off)));
  34. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    36/94 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(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif=:off)));
  35. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    37/94 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(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif=:off))); template <typename P, typename ==. Ps> auto when_all(P p, Ps ==. ps) { return [=](auto const& x) { return p(x) =& when_all(ps==.)(x); }; }
  36. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    38/94 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(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif=:off))); auto when_all() { return [](auto const&){ return true; }; } template <typename P, typename ==. Ps> auto when_all(P p, Ps ==. ps) { return [=](auto const& x) { return p(x) =& when_all(ps==.)(x); }; }
  37. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    39/94 template <typename ==. Predicates> auto when_all(Predicates ==. ps) { return [=](auto const& x) { return (ps(x) =& ==.); }; } auto addr_matches(ip addr, netmask mask=netmask{255,255,255,255}) { return compose(match_ip(addr, mask), select_addr); } auto i = find_if(v.begin(), v.end(), when_all(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif=:off)));
  38. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    40/94 template <typename ==. Predicates> auto when_all(Predicates ==. ps) { return [=](auto const& x) { return (ps(x) =& ==.); }; } auto addr_matches(ip addr, netmask mask=netmask{255,255,255,255}) { return compose(match_ip(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(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif=:off)));
  39. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    41/94 for_each(v.begin(), v.end(), if_then(when_all(addr_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif=:off)), set_state(ipif=:on)));
  40. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    42/94 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(addr_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif=:off)), set_state(ipif=:on)));
  41. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    43/94 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(addr_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif=:off)), set_state(ipif=:on)));
  42. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    45/94 ✔Very brief intro to higher order functions ✔Gradually expanding example optional<T> (and extension) Working with overloads Higher Order Functions for Ordinary C++ Developers
  43. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    47/94 std=:optional<T> • The class template std=:optional manages an optional contained value, i.e. a value that may or may not be present.
  44. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    48/94 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
  45. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    49/94 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
  46. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    50/94 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>
  47. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    51/94 template <typename Predicate> std=:optional<std=:reference_wrapper<ipif=> lookup(std=:vector<ipif>& ifs, Predicate pred) { std=:optional<std=:reference_wrapper<ipif=> rv; auto iter = std=:find_if(ifs.begin(), ifs.end(), pred); if (iter == ifs.end()) rv = std=:ref(*iter); return rv; }
  48. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    52/94 template <typename Predicate> std=:optional<std=:reference_wrapperip_if=> lookup(std=:vector<ipif>& ifs, Predicate pred) { std=:optional<std=:reference_wrapper<ipif=> rv; auto iter = std=:find_if(ifs.begin(), ifs.end(), pred); if (iter == ifs.end()) rv = std=:ref(*iter); return rv; } void do_stuff(ipif&); void some_func() { =/==. auto found = lookup(ip_interfaces, addr_matches({192,168,1,1})); if (found) { do_stuff(found.value()); } =/ }
  49. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    53/94 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
  50. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    54/94 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 P0798R0: Monadic operations for std::optional.
  51. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    55/94 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
  52. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    56/94 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
  53. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    57/94 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
  54. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    58/94 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};});
  55. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    59/94 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};});
  56. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

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

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

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

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

    65/94 ✔Very brief intro to higher order functions ✔Gradually expanding example ✔optional<T> (and extension) Working with overloads Higher Order Functions for Ordinary C++ Developers
  61. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    66/94 class ipif { public: ==. ip_address addr() const { return addr_;} }; ip_address select_addr(ipif const& interface) { return interface.addr(); }
  62. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    67/94 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; }
  63. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    68/94 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; }
  64. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    69/94 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'
  65. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    70/94 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, ^
  66. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    71/94 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; }
  67. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    72/94 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?
  68. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    73/94 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
  69. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    74/94 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; } ¯\_( ツ )_/¯
  70. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    75/94 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.
  71. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    76/94 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
  72. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    77/94 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
  73. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    78/94 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
  74. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    79/94 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
  75. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    80/94 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; } • Call through a generic lambda for the access #define LIFT(func) \ [](auto=& ==. p) \ { \ return func(std=:forward<decltype(p)>(p)==.)); \ }
  76. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    81/94 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; } • Call through a generic lambda for the access #define LIFT(func) \ [](auto=& ==. p) \ noexcept(noexcept(func(std=:forward<decltype(p)>(p)==.)))\ => decltype(func(std=:forward<decltype(p)>(p)==.)) \ { \ return func(std=:forward<decltype(p)>(p)==.); \ }
  77. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    82/94 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; } • Call through a generic lambda for the access #define LIFT(func) \ [](auto=& ==. p) \ noexcept(noexcept(func(std=:forward<decltype(p)>(p)==.)))\ => decltype(func(std=:forward<decltype(p)>(p)==.)) \ { \ return func(std=:forward<decltype(p)>(p)==.); \ } + Very easy to use - Macros are evil
  78. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    84/94 ✔Very brief intro to higher order functions ✔Gradually expanding example ✔optional<T> (and extension) ✔Working with overloads Higher Order Functions for Ordinary C++ Developers
  79. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    85/94 Generic library functions • equals(value) ... • compose(function...) • if_then(pred,action) • when_all(predicate...) • when_none(predicate...) • when_any(predicate...) • do_all(action...) Domain specific functions • ip_matches(ip, mask) • select_addr(ipif) • select_gw(ipif) • select_state(ipif) • set_state(ipif&) Composed domain specific functions • addr_matches(ip) • state_is(state_type)
  80. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    86/94 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...) Domain specific functions • ip_matches(ip, mask) • select_addr(ipif) • select_gw(ipif) • select_state(ipif) • set_state(ipif&) Composed domain specific functions • addr_matches(ip) • state_is(state_type)
  81. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    87/94 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...) Domain specific functions • ip_matches(ip, mask) • select_addr(ipif) • select_gw(ipif) • select_state(ipif) • set_state(ipif&) Composed domain specific functions • addr_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_67_0/libs/hof/
  82. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    88/94 Take away messages • Write functions that uses auto return type to create lambdas
  83. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    89/94 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 – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    90/94 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state • Compose functions
  85. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    91/94 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
  86. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    92/94 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
  87. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    93/94 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 requires an extra level of indirection
  88. Higher Order Functions – NDC{Oslo} 2018 © Björn Fahller @bjorn_fahller

    94/94 Björn Fahller https:=/github.com/rollbear/lift [email protected] @bjorn_fahller @rollbear cpplang, swedencpp Higher Order Functions for Ordinary C++ Developers