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

    View full-size slide

  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 (and extension)
    Managing overload sets
    Higher Order Functions for Ordinary C++ Developers

    View full-size slide

  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.

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  6. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 6/93
    #include
    #include
    std=:vector 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; })) {
    ==.
    }

    View full-size slide

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

    View full-size slide

  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; };
    }

    View full-size slide

  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 equals(int num)
    {
    return [num](int x) { return x == num; };
    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  13. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 13/93
    template
    auto equals(T key)
    {
    return [key](auto const& x){ return x == key; };
    }
    template
    std=:function 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.

    View full-size slide

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

    View full-size slide

  15. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 15/93
    #include
    #include
    std=:vector 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)) {
    ==.
    }

    View full-size slide

  16. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 16/93
    Live demo!

    View full-size slide

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

    View full-size slide

  18. 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};
    };

    View full-size slide

  19. 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};
    };

    View full-size slide

  20. 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};
    };

    View full-size slide

  21. 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};
    };

    View full-size slide

  22. 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);
    };
    }

    View full-size slide

  23. 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);
    };
    }

    View full-size slide

  24. 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);
    };
    }

    View full-size slide

  25. 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 v;
    ==.
    auto i = std=:remove_if(v.begin(), v.end(),
    ip_matches({192,168,1,1}, {255,255,0,0}));

    View full-size slide

  26. 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_;
    };

    View full-size slide

  27. 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_;
    };

    View full-size slide

  28. 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.

    View full-size slide

  29. 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))

    View full-size slide

  30. 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))

    View full-size slide

  31. 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
    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))

    View full-size slide

  32. 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 interfaces;
    auto i = std=:find_if(interfaces.begin(), interfaces.end(),
    compose(ip_matches({192,168,1,1}),
    select_address));

    View full-size slide

  33. 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 interfaces;
    auto i = std=:find_if(interfaces.begin(), interfaces.end(),
    compose(ip_matches({192,168,1,1}),
    select_address));

    View full-size slide

  34. 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 interfaces;
    auto i = std=:find_if(interfaces.begin(), interfaces.end(),
    compose(ip_matches({192,168,1,1}),
    select_address);

    View full-size slide

  35. 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 interfaces;
    auto i = std=:find_if(interfaces.begin(), interfaces.end(),
    address_matches({192,168,1,1}));

    View full-size slide

  36. 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();
    }

    View full-size slide

  37. 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)));

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  41. 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)));

    View full-size slide

  42. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 42/93
    template
    auto if_then(Predicate predicate, Action action)
    {
    return [=](auto=& x)
    {
    if (predicate(x)) {
    action(std=:forward(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)));

    View full-size slide

  43. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 43/93
    template
    auto if_then(Predicate predicate, Action action)
    {
    return [=](auto=& x)
    {
    if (predicate(x)) {
    action(std=:forward(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)));

    View full-size slide

  44. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 44/93
    Live demo!

    View full-size slide

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

    View full-size slide

  46. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 46/93
    std=:optional

    View full-size slide

  47. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 47/93
    std=:optional

    The class template std=:optional manages an optional
    contained value, i.e. a value that may or may not be present.

    View full-size slide

  48. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 48/93
    std=:optional

    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

    View full-size slide

  49. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 49/93
    std=:optional

    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

    View full-size slide

  50. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 50/93
    std=:optional

    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

    View full-size slide

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

    View full-size slide

  52. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 52/93
    template
    std=:optional lookup(std=:vector& 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());
    }
    =/
    }

    View full-size slide

  53. 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

    View full-size slide

  54. 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.

    View full-size slide

  55. 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
    int i = 3;
    tl=:optional v; =/ no value
    v = i; =/ v refers to i

    View full-size slide

  56. 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
    int i = 3;
    tl=:optional v; =/ no value
    v = i; =/ v refers to i
    v.value() = 4; =/ i == 4

    View full-size slide

  57. 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
    int i = 3;
    tl=:optional 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

    View full-size slide

  58. 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
    int i = 3;
    tl=:optional 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{i};});

    View full-size slide

  59. 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
    int i = 3;
    tl=:optional 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{i};});

    View full-size slide

  60. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 60/93
    template
    tl=:optional lookup(std=:vector& 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());
    }
    =/
    }

    View full-size slide

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

    View full-size slide

  62. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 62/93
    template
    tl=:optional lookup(std=:vector& 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));
    =/
    }

    View full-size slide

  63. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 63/93
    template
    tl=:optional lookup(std=:vector& 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”;});
    =/
    }

    View full-size slide

  64. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 64/93
    Live demo!

    View full-size slide

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

    View full-size slide

  66. 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();
    }

    View full-size slide

  67. 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
    addresses(std=:vector& const v)
    {
    std=:vector rv;
    std=:transform(std=:begin(v), std=:end(v),
    std=:back_inserter(rv),
    select_addr);
    return rv;
    }

    View full-size slide

  68. 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
    addresses(std=:vector& const v)
    {
    std=:vector rv;
    std=:transform(std=:begin(v), std=:end(v),
    std=:back_inserter(rv),
    select_addr);
    return rv;
    }

    View full-size slide

  69. 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
    addresses(std=:vector& const v)
    {
    std=:vector rv;
    std=:transform(std=:begin(v), std=:end(v),
    std=:back_inserter(rv),
    select_addr);
    return rv;
    }
    example.cpp
    (40): error C2672: 'std=:transform': no matching overloaded function found
    (40): error C2783: '_OutIt std=:transform(_InIt,_InIt,_OutIt,_Fn1)': could not deduce
    template argument for '_Fn1'

    View full-size slide

  70. 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
    addresses(std=:vector& const v)
    {
    std=:vector rv;
    std=:transform(std=:begin(v), std=:end(v),
    std=:back_inserter(rv),
    select_addr);
    return rv;
    }
    :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,
    ^

    View full-size slide

  71. 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
    addresses(std=:vector const& v)
    {
    std=:vector rv;
    std=:transform(std=:begin(v), std=:end(v),
    std=:back_inserter(rv),
    select_addr);
    return rv;
    }

    View full-size slide

  72. 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
    addresses(std=:vector const& v)
    {
    std=:vector rv;
    std=:transform(std=:begin(v), std=:end(v),
    std=:back_inserter(rv),
    select_addr);
    return rv;
    }
    How to solve this?

    View full-size slide

  73. 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
    addresses(std=:vector const& v)
    {
    std=:vector rv;
    std=:transform(std=:begin(v), std=:end(v),
    std=:back_inserter(rv),
    select_addr);
    return rv;
    }
    Solution #1

    View full-size slide

  74. 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
    addresses(std=:vector const& v)
    {
    std=:vector rv;
    std=:transform(std=:begin(v), std=:end(v),
    std=:back_inserter(rv),
    select_addr);
    return rv;
    }
    ¯\_( ツ )_/¯

    View full-size slide

  75. 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
    addresses(std=:vector const& v)
    {
    std=:vector 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.

    View full-size slide

  76. 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
    addresses(std=:vector const& v)
    {
    std=:vector rv;
    std=:transform(std=:begin(v), std=:end(v),
    std=:back_inserter(rv),
    select_addr);
    return rv;
    }
    Solution #2

    View full-size slide

  77. 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
    addresses(std=:vector const& v)
    {
    std=:vector 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

    View full-size slide

  78. 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
    addresses(std=:vector const& v)
    {
    std=:vector 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

    View full-size slide

  79. 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
    addresses(std=:vector const& v)
    {
    std=:vector rv;
    std=:transform(std=:begin(v), std=:end(v),
    std=:back_inserter(rv),
    select_addr);
    return rv;
    }
    Solution #2b

    View full-size slide

  80. 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
    addresses(std=:vector const& v)
    {
    std=:vector 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(p)==.)); \
    }

    View full-size slide

  81. 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
    addresses(std=:vector const& v)
    {
    std=:vector 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(p)==.)); \
    }
    + Fairly easy to use
    - Macros are evil

    View full-size slide

  82. Higher Order Functions – Meeting C++ 2018 © Björn Fahller @bjorn_fahller 82/93
    Live demo!

    View full-size slide

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

    View full-size slide

  84. 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)

    View full-size slide

  85. 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)

    View full-size slide

  86. 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/

    View full-size slide

  87. 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

    View full-size slide

  88. 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

    View full-size slide

  89. 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

    View full-size slide

  90. 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 and expected
    removes the need for many conditionals

    View full-size slide

  91. 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 and expected
    removes the need for many conditionals

    Overloads can be a bit of a bother.

    View full-size slide

  92. 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

    View full-size slide

  93. 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

    View full-size slide