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

Higher order functions for ordinary C++ developers

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

January 25, 2018
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 1/40 Higher Order Functions in C++ compose([](const auto& s) { return s == "foo";}, std::mem_fn(&Bar::name))
  2. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 2/40 Definition A higher-order function is a function that takes other functions as arguments or returns a function as result.
  3. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 3/40 #include <algorithm> #include <vector> std::vector<int> v; ... if (std::any_of(std::begin(v), std::end(v), [](int x) { return x == 0; })) { ... }
  4. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

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

    @bjorn_fahller 5/40 #include <algorithm> #include <vector> std::vector<int> v; ... if (std::any_of(std::begin(v), std::end(v), [](int x) { return x == 0; })) { ... } ... int num; ... auto n = std::count_if(std::begin(v), std::end(v), [num](int x){ return x == num; });
  6. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 6/40 [num](int x){ return x == num; }
  7. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 7/40 auto equals(int num) { return [num](int x){ return x == num; }; }
  8. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 8/40 template <typename T> auto equals(T num) { return [num](const auto& x){ return x == num; }; }
  9. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 9/40 template <typename T> auto equals(T&& num) { return [n = std::forward<T>(num)](const auto& x){ return x == n; }; }
  10. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 10/40 #include <algorithm> #include <vector> std::vector<int> v; ... if (std::any_of(std::begin(v), std::end(v), equals(0)) { ... } ... int num; ... auto n = std::count_if(std::begin(v), std::end(v), equals(num));
  11. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 12/40 struct ipaddress { ipaddress(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) {} ipaddress(uint32_t n) : num(n) {} bool operator==(ipaddress rh) const { return num == rh.num;} bool operator!=(ipaddress rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ipaddress { using ipaddress::ipaddress; }; inline ipaddress operator&(ipaddress lh, netmask rh) { return {lh.num & rh.num}; };
  12. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 13/40 struct ipaddress { ipaddress(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) {} ipaddress(uint32_t n) : num(n) {} bool operator==(ipaddress rh) const { return num == rh.num;} bool operator!=(ipaddress rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ipaddress { using ipaddress::ipaddress; }; inline ipaddress operator&(ipaddress lh, netmask rh) { return {lh.num & rh.num}; }; auto ip_matches(ipaddress address, netmask mask = netmask{255,255,255,255}) { return [needle = address & mask,mask](ipaddress addr) { return needle == (addr & mask); }; }
  13. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 14/40 struct ipaddress { ipaddress(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) {} ipaddress(uint32_t n) : num(n) {} bool operator==(ipaddress rh) const { return num == rh.num;} bool operator!=(ipaddress rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ipaddress { using ipaddress::ipaddress; }; inline ipaddress operator&(ipaddress lh, netmask rh) { return {lh.num & rh.num}; }; auto ip_matches(ipaddress address, netmask mask = netmask{255,255,255,255}) { return [needle = address & mask,mask](ipaddress addr) { return needle == (addr & mask); }; } std::vector<ip> v; ... auto i = std::remove_if(v.begin(), v.end(), ip_matches({192,168,1,1}, {255,255,0,0));
  14. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 15/40 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ipaddress address() const { return addr_;} netmask mask() const { return mask_; } ipaddress gateway() const { return gw_; } private: ipaddress addr_; netmask mask_; ipaddress gw_; state_type state_; };
  15. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 16/40 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ipaddress address() const { return addr_; } netmask mask() const { return mask_; } ipaddress gateway() const { return gw_; } private: ipaddress addr_; netmask mask_; ipaddress 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.
  16. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 17/40 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ipaddress address() const { return addr_;} netmask mask() const { return mask_; } ipaddress gateway() const { return gw_; } private: ipaddress addr_; netmask mask_; ipaddress gw_; state_type state_; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x))
  17. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 18/40 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ipaddress address() const { return addr_;} netmask mask() const { return mask_; } ipaddress gateway() const { return gw_; } private: ipaddress addr_; netmask mask_; ipaddress gw_; state_type state_; }; Given: f1(y) -> z ip_matches(ip) -> bool and f2(x) -> y address_of(ipif) -> ip We want a composition f(x)->z as f1(f2(x))
  18. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 19/40 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ipaddress address() const { return addr_;} netmask mask() const { return mask_; } ipaddress gateway() const { return gw_; } private: ipaddress addr_; netmask mask_; ipaddress 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) -> bool and f2(x) -> y address_of(ipif) -> ip We want a composition f(x)->z as f1(f2(x))
  19. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 20/40 class ipif { public: ... ipaddress address() const { return addr_; } ... private: ipaddress addr_; ... }; std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), std::mem_fn(&ipif::address));
  20. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 21/40 class ipif { public: ... ipaddress address() const { return addr_; } ... private: ipaddress addr_; ... }; ip address_of(const ipif& interface) { return interface.address(); } std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), address_of));
  21. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 22/40 class ipif { public: ... ipaddress address() const { return addr_; } ... private: ipaddress addr_; ... }; ip address_of(const ipif& interface) { return interface.address(); } std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), address_of)); auto address_of = [](const auto& obj) { return obj.address(); }
  22. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 23/40 class ipif { public: ... ipaddress address() const { return addr_; } ... private: ipaddress addr_; ... }; auto address_matches(ip addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), address_of); } std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), address_of);
  23. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 24/40 class ipif { public: ... ipaddress address() const { return addr_; } ... private: ipaddress addr_; ... }; auto address_matches(ip addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), address_of); } std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), address_matches({192,168,1,1}));
  24. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 25/40 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ipaddress address() const { return addr_; } netmask mask() const { return mask_; } ipaddress gateway() const { return gw_; } ... }; inline ip gateway_of(const ipif& interface) { return interface.gateway(); } inline ipif::state_type state_of(const ipif& interface) { return interface.state(); }
  25. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 26/40 auto i = find_if(v.begin(), v.end(), when_all(address_matches({192,168,1,1}), state_is(ipif::off)));
  26. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

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

    @bjorn_fahller 28/40 template <typename ... Predicates> auto when_all(Predicates ... ps) { return [=](const auto& x) { return (ps(x) && ...); }; } auto address_matches(ip addr, netmask mask=netmask{255,255,255,255}) { return compose(match_ip(addr, mask), address_of); } auto i = find_if(v.begin(), v.end(), when_all(address_matches({192,168,1,1}), state_is(ipif::off)));
  28. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 29/40 template <typename ... Predicates> auto when_all(Predicates ... ps) { return [=](const auto& x) { return (ps(x) && ...); }; } auto address_matches(ip addr, netmask mask=netmask{255,255,255,255}) { return compose(match_ip(addr, mask), address_of); } auto state_is(ipif::state_type state) { return compose(equals(state), state_of); } auto i = find_if(v.begin(), v.end(), when_all(addr_matches({192,168,1,1}), state_is(ipif::off)));
  29. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 30/40 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)));
  30. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 31/40 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)));
  31. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 32/40 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)));
  32. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 34/40 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) • address_of(ipif) • gateway_of(ipif) • state_of(ipif) • set_state(ipif&) Composed domain specific functions • address_matches(ip) • state_is(state_type)
  33. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 35/40 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) • address_of(ipif) • gateway_of(ipif) • state_of(ipif) • set_state(ipif&) Composed domain specific functions • address_matches(ip) • state_is(state_type)
  34. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 36/40 Take away messages • Write functions that uses auto return type to create lambdas
  35. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 37/40 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state
  36. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 38/40 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state • Compose functions
  37. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 39/40 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state • Compose functions • and write named functions that returns compositions
  38. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller

    @bjorn_fahller 40/40 Björn Fahller https://github.com/rollbear/lift [email protected] @bjorn_fahller @rollbear cpplang, swedencpp Higher order functions in C++