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

    View Slide

  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.

    View Slide

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

    View Slide

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

    View Slide

  5. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller @bjorn_fahller 5/40
    #include
    #include
    std::vector 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; });

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  11. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller @bjorn_fahller 11/40
    Live demo!

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

  33. Higher Order Functions – Stockholm C++ 0x09 – Björn Fahller @bjorn_fahller 33/40
    Live demo!

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  40. 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++

    View Slide