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

Modern Techniques for Keeping Your Code DRY

Modern Techniques for Keeping Your Code DRY

We have learned to avoid repeating ourselves in code, and yet we keep writing things like:

if (a > 0 && b > 0 && c > 0) ...

In this session I will show techniques from modern C++ that helps you construct abstractions for those micro repetitions. These will allow you to write:

if (all_of(a, b, c) > 0) ...

Code like this expresses intent more clearly, and therefore makes it easier to follow the logic of the intended functionality instead of focussing on code details. This makes it easier to understand the functionality, and also makes it easier to spot mistakes. Better yet, these abstractions carry no run time cost.

After this session, you will be able to write your own zero-cost abstractions that helps getting rid of the patterns that keeps repeating in your code.

Björn Fahller

August 29, 2019
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 1/124

    Modern Techniques for Keeping Your Code DRY Björn Fahller <C++>
  2. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 2/124

    assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  3. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 3/124

    assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  4. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 4/124

    Modern Techniques for Keeping Your Code DRY Björn Fahller <C++>
  5. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 5/124

    Modern Techniques for Keeping Your Code DRY Björn Fahller <C++>
  6. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 6/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  7. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 7/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED); variadic function template
  8. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 8/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> bool is_any_of(state_type s, const Ts& ... ts) { return ((s == ts) || ...); } assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  9. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 9/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> bool is_any_of(state_type s, const Ts& ... ts) { return ((s == ts) || ...); } assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  10. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 10/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> bool is_any_of(state_type s, const Ts& ... ts) { return ((s == ts) || ...); } assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  11. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 11/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> bool is_any_of(state_type s, const Ts& ... ts) { return ((s == ts) || ...); } assert(is_any_of(state, IDLE, DISCONNECTING, DISCONNECTED));
  12. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 12/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> bool is_any_of(state_type s, const Ts& ... ts) { return ((s == ts) || ...); } assert(is_any_of(state, IDLE, DISCONNECTING, DISCONNECTED)); Utterly unreadable trash code!
  13. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 13/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED); variadic non-type template parameter function
  14. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 14/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <state_type ... states> bool is_any_of(state_type t) { return ((t == states) || ...); } assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  15. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 15/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <state_type ... states> bool is_any_of(state_type t) { return ((t == states) || ...); } assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  16. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 16/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <state_type ... states> bool is_any_of(state_type t) { return ((t == states) || ...); } assert(is_any_of<IDLE, DISCONNECTING, DISCONNECTED>(state));
  17. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 17/124

    auto for non-type template parameters from C++17 enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <auto ... alternatives, typename T> bool is_any_of(const T& t) { return ((t == alternatives) || ...); } assert(is_any_of<IDLE, DISCONNECTING, DISCONNECTED>(state));
  18. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 18/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <auto ... alternatives, typename T> bool is_any_of(const T& t) { return ((t == alternatives) || ...); } assert(is_any_of<IDLE, DISCONNECTING, DISCONNECTED>(state)); Not all types can be used as non-type template parameters Only constexpr values Yoda speak
  19. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 19/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED); construct then test
  20. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 20/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  21. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 21/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename T> auto is_in(const T& t) { return [t](const auto& ... vs) { return ((t == vs) || ...); }; } assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  22. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 22/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename T> auto is_in(const T& t) { return [t](const auto& ... vs) { return ((t == vs) || ...); }; } assert(is_in(state)(IDLE, DISCONNECTING, DISCONNECTED));
  23. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 23/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename T> auto is_in(const T& t) { return [t](const auto& ... vs) { return ((t == vs) || ...); }; } assert(is_in(state)(IDLE, DISCONNECTING, DISCONNECTED));
  24. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 24/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename T> auto is_in(const T& t) { return [t](const auto& ... vs) { return ((t == vs) || ...); }; } assert(is_in(state)(IDLE, DISCONNECTING, DISCONNECTED)); Works with all types Looks horrible Is a bit too terse
  25. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 25/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED); explicit construct and compare function
  26. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 26/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename T> struct is { T t; template <typename ... Ts> bool any_of(const Ts& ... ts) const { return ((t == ts) || ...); } }; template <typename T> is(T) -> is<T>; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  27. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 27/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename T> struct is { T t; template <typename ... Ts> bool any_of(const Ts& ... ts) const { return ((t == ts) || ...); } }; template <typename T> is(T) -> is<T>; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  28. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 28/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename T> struct is { T t; template <typename ... Ts> bool any_of(const Ts& ... ts) const { return ((t == ts) || ...); } }; template <typename T> is(T) -> is<T>; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED); C++17 Deduction Guide for Constructor Template Argument Deduction
  29. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 29/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename T> struct is { T t; template <typename ... Ts> bool any_of(const Ts& ... ts) const { return ((t == ts) || ...); } }; template <typename T> is(T) -> is<T>; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED); C++17 Deduction Guide for Constructor Template Argument Deduction https://youtu.be/-H-ut6j1BYU https://youtu.be/UDs90b0yjjQ
  30. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 30/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename T> struct is { T t; template <typename ... Ts> bool any_of(const Ts& ... ts) const { return ((t == ts) || ...); } }; template <typename T> is(T) -> is<T>; assert(is{state}.any_of(IDLE, DISCONNECTING, DISCONNECTED)); Works with all types Explicit Still Yoda speak
  31. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 31/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED); time passes...
  32. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 32/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED); your mildly annoyed developer stays annoyed...
  33. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 33/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED); Then one day, two years later!
  34. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 34/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  35. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 35/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&… ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } }; template <typename ... Ts> any_of(Ts...) -> any_of<Ts...>; assert(any_of(IDLE, DISCONNECTING, DISCONNECTED) == state);
  36. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 36/124

    Pure laziness, but these give me 18 constructors enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } }; template <typename ... Ts> any_of(Ts...) -> any_of<Ts...>; assert(any_of(IDLE, DISCONNECTING, DISCONNECTED) == state);
  37. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 37/124

    Call function with each member of the tuple as a parameter. enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } }; template <typename ... Ts> any_of(Ts...) -> any_of<Ts...>; assert(state == any_of(IDLE, DISCONNECTING, DISCONNECTED));
  38. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 38/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } template <typename T> friend bool operator==(const T& lh, const any_of& rh) { return rh == lh;} }; template <typename ... Ts> any_of(Ts...) -> any_of<Ts...>; assert(state == any_of(IDLE, DISCONNECTING, DISCONNECTED)); Provide symmetric operator==
  39. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 39/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } template <typename T> friend bool operator==(const T& lh, const any_of& rh) { return rh == lh;} }; template <typename ... Ts> any_of(Ts...) -> any_of<Ts...>; assert(state == any_of(IDLE, DISCONNECTING, DISCONNECTED)); Maybe exaggerated cuteness, but I like this!
  40. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 40/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } template <typename T> friend bool operator==(const T& lh, const any_of& rh) { return rh == lh;} }; template <typename ... Ts> any_of(Ts...) -> any_of<Ts...>; assert(state == any_of(IDLE, DISCONNECTING, DISCONNECTED)); https://gcc.godbolt.org/z/IY865r
  41. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 41/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } template <typename T> friend bool operator==(const T& lh, const any_of& rh) { return rh == lh;} }; template <typename ... Ts> any_of(Ts...) -> any_of<Ts...>; assert(state == any_of(IDLE, DISCONNECTING, DISCONNECTED));
  42. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 42/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } template <typename T> friend bool operator==(const T& lh, const any_of& rh) { return rh == lh;} }; template <typename ... Ts> any_of(Ts...) -> any_of<Ts...>; assert(state == any_of(IDLE, DISCONNECTING, DISCONNECTED)); Add relational operators!
  43. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 43/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } template <typename T> bool operator<(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts < t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } ... }; while (any_of(a, b, c) < 0) ...
  44. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 44/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } template <typename T> bool operator<(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts < t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } ... }; while (any_of(a, b, c) < 0) ...
  45. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 45/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } template <typename T> bool operator<(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts < t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } ... }; while (any_of(a, b, c) < 0) ... https://gcc.godbolt.org/z/YyvAtT
  46. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 46/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } template <typename T> bool operator<(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts < t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } ... }; while (any_of(a, b, c) < 0) ...
  47. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 47/124

    enum state_type { IDLE, CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED }; template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts == t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } template <typename T> bool operator<(const T& t) const { return std::apply([&t](const auto&... ts){ return ((ts < t) || ...);}, static_cast<const std::tuple<Ts...>&>(*this)); } ... }; while (any_of(a, b, c) < 0) ... An awful lot of repetition here!
  48. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 48/124

    template <typename F, typename ... Ts> bool or_elements(const F& f, const std::tuple<Ts...>& t) { return std::apply([&f](const auto& ... ts){ return (f(ts) || ...);}, t); }
  49. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 49/124

    template <typename F, typename ... Ts> bool or_elements(const F& f, const std::tuple<Ts...>& t) { return std::apply([&f](const auto& ... ts){ return (f(ts) || ...);}, t); } template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return or_elements([&t](const auto& v){ return v == t;}, *this); } template <typename T> bool operator<(const T& t) const { return or_elements([&t](const auto& v){ return v < t;}, *this); } }; while (any_of(a, b, c) < 0) ...
  50. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 50/124

    template <typename F, typename ... Ts> bool or_elements(const F& f, const std::tuple<Ts...>& t) { return std::apply([&f](const auto& ... ts){ return (f(ts) || ...);}, t); } template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return or_elements([&t](const auto& v){ return v == t;}, *this); } template <typename T> bool operator<(const T& t) const { return or_elements([&t](const auto& v){ return v < t;}, *this); } }; while (any_of(a, b, c) < 0) ... https://godbolt.org/z/SP8POJ
  51. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 51/124

    template <typename F, typename ... Ts> bool or_elements(const F& f, const std::tuple<Ts...>& t) { return std::apply([&f](const auto& ... ts){ return (f(ts) || ...);}, t); } template <typename ... Ts> struct any_of : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return or_elements([&t](const auto& v){ return v == t;}, *this); } template <typename T> bool operator<(const T& t) const { return or_elements([&t](const auto& v){ return v < t;}, *this); } }; while (any_of(a, b, c) < 0) ... all_of? Repeat Everything Again?
  52. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 52/124

    struct or_elements { template <typename F, typename ... Ts> static bool apply(const F& f, const std::tuple<Ts...>& t) { return std::apply([&](const auto& ... ts){ return (f(ts) || ...);}, t); } }; template <typename Op, typename ... Ts> struct op_t : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return Op::apply([&t](const auto& v){ return v == t;}, *this); } ... }; template <typename ... Ts> struct any_of : op_t<or_elements, Ts...> { using op_t<or_elements, Ts...>::op_t; };
  53. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 53/124

    struct or_elements { template <typename F, typename ... Ts> static bool apply(const F& f, const std::tuple<Ts...>& t) { return std::apply([&](const auto& ... ts){ return (f(ts) || ...);}, t); } }; template <typename Op, typename ... Ts> struct op_t : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return Op::apply([&t](const auto& v){ return v == t;}, *this); } ... }; template <typename ... Ts> struct any_of : op_t<or_elements, Ts...> { using op_t<or_elements, Ts...>::op_t; };
  54. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 54/124

    struct or_elements { template <typename F, typename ... Ts> static bool apply(const F& f, const std::tuple<Ts...>& t) { return std::apply([&](const auto& ... ts){ return (f(ts) || ...);}, t); } }; template <typename Op, typename ... Ts> struct op_t : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return Op::apply([&t](const auto& v){ return v == t;}, *this); } ... }; template <typename ... Ts> struct any_of : op_t<or_elements, Ts...> { using op_t<or_elements, Ts...>::op_t; };
  55. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 55/124

    struct or_elements { template <typename F, typename ... Ts> static bool apply(const F& f, const std::tuple<Ts...>& t) { return std::apply([&](const auto& ... ts){ return (f(ts) || ...);}, t); } }; template <typename Op, typename ... Ts> struct op_t : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return Op::apply([&t](const auto& v){ return v == t;}, *this); } ... }; template <typename ... Ts> struct any_of : op_t<or_elements, Ts...> { using op_t<or_elements, Ts...>::op_t; };
  56. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 56/124

    struct and_elements { template <typename F, typename ... Ts> static bool apply(const F& f, const std::tuple<Ts...>& t) { return std::apply([&](const auto& ... ts){ return (f(ts) && ...);}, t); } }; template <typename Op, typename ... Ts> struct op_t : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return Op::apply([&t](const auto& v){ return v == t;}, *this); } ... }; template <typename ... Ts> struct all_of : op_t<and_elements, Ts...> { using op_t<and_elements, Ts...>::op_t; };
  57. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 57/124

    struct and_elements { template <typename F, typename ... Ts> static bool apply(const F& f, const std::tuple<Ts...>& t) { return std::apply([&](const auto& ... ts){ return (f(ts) && ...);}, t); } }; template <typename Op, typename ... Ts> struct op_t : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return Op::apply([&t](const auto& v){ return v == t;}, *this); } ... }; template <typename ... Ts> struct all_of : op_t<and_elements, Ts...> { using op_t<and_elements, Ts...>::op_t; }; template <typename ... Ts> using all_of = op_t<and_elements, Ts...>; In a better world we could deduce constructor template args for aliases too
  58. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 58/124

    struct and_elements { template <typename F, typename ... Ts> static bool apply(const F& f, const std::tuple<Ts...>& t) { return std::apply([&](const auto& ... ts){ return (f(ts) && ...);}, t); } }; template <typename Op, typename ... Ts> struct op_t : private std::tuple<Ts...> { using std::tuple<Ts...>::tuple; template <typename T> bool operator==(const T& t) const { return Op::apply([&t](const auto& v){ return v == t;}, *this); } ... }; template <typename ... Ts> struct all_of : op_t<and_elements, Ts...> { using op_t<and_elements, Ts...>::op_t; }; https://gcc.godbolt.org/z/Ay1TA_
  59. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 60/124

    Can we try something else? Lambdas are so cool!
  60. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 61/124

    constexpr auto tuple = [](auto ... ts) { return [=](const auto& func) { return func(ts...); }; }; assert(tuple(a,b,c)([](auto ... e){ return ((e > 0) && ...); }));
  61. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 62/124

    constexpr auto tuple = [](auto ... ts) { return [=](const auto& func) { return func(ts...); }; }; assert(tuple(a,b,c)([](auto ... e){ return ((e > 0) && ...); }));
  62. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 63/124

    constexpr auto tuple = [](auto ... ts) { return [=](const auto& func) { return func(ts...); }; }; assert(tuple(a,b,c)([](auto ... e){ return ((e > 0) && ...); }));
  63. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 64/124

    constexpr auto tuple = [](auto ... ts) { return [=](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = [](auto func) { return [=](auto ... elements) { return (func(elements) && ...); }; }; assert(tuple(a,b,c)(and_elements([](auto e){ return e > 0;}));
  64. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 65/124

    constexpr auto tuple = [](auto ... ts) { return [=](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = [](auto func) { return [=](auto ... elements) { return (func(elements) && ...); }; }; assert(tuple(a,b,c)(and_elements([](auto e){ return e > 0;}));
  65. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 66/124

    constexpr auto tuple = [](auto ... ts) { return [=](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = [](auto func) { return [=](auto ... elements) { return (func(elements) && ...); }; }; assert(tuple(a,b,c)(and_elements([](auto e){ return e > 0;}));
  66. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 67/124

    constexpr auto tuple = [](auto ... ts) { return [=](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = [](auto func) { return [=](auto ... elements) { return (func(elements) && ...); }; }; constexpr auto bind_rh = [](auto func, auto rh) { return [=](auto lh) { return func(lh, rh); }; }; assert(tuple(a,b,c)(and_elements([](auto e){ return e > 0;}));
  67. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 68/124

    constexpr auto tuple = [](auto ... ts) { return [=](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = [](auto func) { return [=](auto … elements) { return (func(elements) && ...); }; }; constexpr auto bind_rh = [](auto func, auto rh) { return [=](auto lh) { return func(lh, rh); }; }; constexpr auto greater_than = [](auto rh) { return bind_rh(std::greater{}, rh); }; assert(tuple(a,b,c)(and_elements([](auto e){ return e > 0;}));
  68. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 69/124

    constexpr auto tuple = [](auto ... ts) { return [=](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = [](auto func) { return [=](auto … elements) { return (func(elements) && ...); }; }; constexpr auto bind_rh = [](auto func, auto rh) { return [=](auto lh) { return func(lh, rh); }; }; constexpr auto greater_than = [](auto rh) { return bind_rh(std::greater{}, rh); }; assert(tuple(a,b,c)(and_elements(greater_than(0)));
  69. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 70/124

    constexpr auto tuple = [](auto ... ts) { return [=](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = [](auto func) { return [=](auto … elements) { return (func(elements) && ...); }; }; constexpr auto bind_rh = [](auto func, auto rh) { return [=](auto lh) { return func(lh, rh); }; }; constexpr auto greater_than = [](auto rh) { return bind_rh(std::greater{}, rh); }; assert(tuple(a,b,c)(and_elements(greater_than(0)));
  70. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 71/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; Func func; template <typename F> auto apply(F f) const{ return tup(func(f)); } public: op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> auto operator>(const T& t) const{ return apply(greater_than(t)); } }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0);
  71. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 72/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; Func func; template <typename F> auto apply(F f) const{ return tup(func(f)); } public: op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> auto operator>(const T& t) const{ return apply(greater_than(t)); } }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0);
  72. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 73/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; Func func; template <typename F> auto apply(F f) const{ return tup(func(f)); } public: op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> auto operator>(const T& t) const{ return apply(greater_than(t)); } }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0);
  73. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 74/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; Func func; template <typename F> auto apply(F f) const{ return tup(func(f)); } public: op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> auto operator>(const T& t) const{ return apply(greater_than(t)); } }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0);
  74. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 75/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; Func func; template <typename F> auto apply(F f) const{ return tup(func(f)); } public: op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> auto operator>(const T& t) const{ return apply(greater_than(t)); } }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0);
  75. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 76/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> auto apply(F f) const{ return tup(func(f)); } public: op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> auto operator>(const T& t) const{ return apply(greater_than(t)); } }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0); C++20 attribute to make empty member take no space. If saving this byte is important pre C++20, use private inheritance for Empty Base Class Optimization.
  76. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 77/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> auto apply(F f) const{ return tup(func(f)); } public: op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> auto operator>(const T& t) const{ return apply(greater_than(t)); } }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0);
  77. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 78/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> auto apply(F f) const{ return tup(func(f)); } public: op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> auto operator>(const T& t) const{ return apply(greater_than(t)); } }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0); https://gcc.godbolt.org/z/MZvLSF
  78. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 79/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> auto apply(F f) const{ return tup(func(f)); } public: op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> auto operator>(const T& t) const{ return apply(greater_than(t)); } }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0); Things to improve:
  79. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 80/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> auto apply(F f) const{ return tup(func(f)); } public: op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> auto operator>(const T& t) const{ return apply(greater_than(t)); } }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0); Things to improve: • constexpr
  80. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 81/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F f) const{ return tup(func(f)); } public: constexpr op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> constexpr auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> constexpr auto operator>(const T& t) const{ return apply(greater_than(t));} }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0); Things to improve: • constexpr
  81. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 82/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F f) const{ return tup(func(f)); } public: constexpr op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> constexpr auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> constexpr auto operator>(const T& t) const{ return apply(greater_than(t));} }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0); Things to improve: ✔ constexpr • Perfect forwarding
  82. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 83/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F f) const{ return tup(func(f)); } public: constexpr op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> constexpr auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> constexpr auto operator>(const T& t) const{ return apply(greater_than(t));} }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0); Things to improve: ✔ constexpr • Perfect forwarding • Conditional noexcept
  83. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 84/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F f) const{ return tup(func(f)); } public: constexpr op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> constexpr auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> constexpr auto operator>(const T& t) const{ return apply(greater_than(t));} }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0); Things to improve: ✔ constexpr • Perfect forwarding • Conditional noexcept • Explicit return type
  84. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 85/124

    constexpr auto tuple = [](auto ... ts) ... constexpr auto and_elements = [](auto func) ... constexpr auto equal_to = [](auto rh) ... constexpr auto greater_than = [](auto rh) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F f) const{ return tup(func(f)); } public: constexpr op_t(Func f, Tuple t) : tup(t), func(f) {} template <typename T> constexpr auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> constexpr auto operator>(const T& t) const{ return apply(greater_than(t));} }; constexpr auto all_of=[](auto... ts){ return op_t(and_elements,tuple(ts...));}; assert(all_of(a,b,c) > 0); Things to improve: ✔ constexpr • Perfect forwarding • Conditional noexcept • Explicit return type
  85. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 86/124

    constexpr auto and_elements = [](auto func) { return [=](auto ... elements) { return (func(elements) && ...); }; };
  86. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 87/124

    constexpr auto and_elements = [](auto&& func) { return [func = std::forward<decltype(func)>(func)](auto ... elements) { return (func(elements) && ...); }; };
  87. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 88/124

    constexpr auto and_elements = [](auto&& func) { return [func = std::forward<decltype(func)>(func)](auto ... elements) { return (func(elements) && ...); }; };
  88. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 89/124

    constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto ... elements) { return (func(elements) && ...); }; }; C++20 allows us to explicitly state template parameters to lambdas.
  89. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 90/124

    constexpr auto tuple = [](auto ... ts) { return [=](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto ... elements) { return (func(elements) && ...); }; };
  90. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 91/124

    constexpr auto tuple = [](auto&& ... ts) { return [=](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto ... elements) { return (func(elements) && ...); }; }; Not possible to forward parameter packs in C++14 or C++17!
  91. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 92/124

    constexpr auto tuple = [](auto&& ... ts) { return [...ts = std::forward<decltype(ts)>(ts)](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto ... elements) { return (func(elements) && ...); }; }; C++20 parameter pack capture with perfect forwarding.
  92. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 93/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) { return [...ts = std::forward<Ts>(ts)](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto ... elements) { return (func(elements) && ...); }; };
  93. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 94/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) ... constexpr auto and_elements = []<typename T>(T&& func) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F&& f) const { return tup(func(std::forward<F>(f))); } public: constexpr op_t(Func f, Tuple t) : Func(std::move(f)), tup(std::move(t)) {} template <typename T> constexpr auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> constexpr auto operator>(const T& t) const{ return apply(greater_than(t));} }; constexpr auto all_of=[]<typename ... Ts>(Ts&&... ts){ return op_t(and_elements,tuple(std::forward<Ts>(ts)...)); };
  94. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 95/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) ... constexpr auto and_elements = []<typename T>(T&& func) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F&& f) const { return tup(func(std::forward<F>(f))); } public: constexpr op_t(Func f, Tuple t) : Func(std::move(f)), tup(std::move(t)) {} template <typename T> constexpr auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> constexpr auto operator>(const T& t) const{ return apply(greater_than(t));} }; constexpr auto all_of=[]<typename ... Ts>(Ts&&... ts){ return op_t(and_elements,tuple(std::forward<Ts>(ts)...)); }; Things to improve: ✔ constexpr ✔ Perfect forwarding • Conditional noexcept • Explicit return type
  95. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 96/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) ... constexpr auto and_elements = []<typename T>(T&& func) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F&& f) const { return tup(func(std::forward<F>(f))); } public: constexpr op_t(Func f, Tuple t) : Func(std::move(f)), tup(std::move(t)) {} template <typename T> constexpr auto operator==(const T& t) const { return apply(equal_to(t)); } template <typename T> constexpr auto operator>(const T& t) const{ return apply(greater_than(t));} }; constexpr auto all_of=[]<typename ... Ts>(Ts&&... ts){ return op_t(and_elements,tuple(std::forward<Ts>(ts)...)); }; Things to improve: ✔ constexpr ✔ Perfect forwarding • Conditional noexcept • Explicit return type
  96. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 97/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) { return [...ts = std::forward<Ts>(ts)](const auto& func) { return func(ts...); }; }; constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto … elements) { return (func(elements) && ...); }; };
  97. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 98/124

    7.82 constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) { return [...ts = std::forward<Ts>(ts)](const auto& func) noexcept(noexcept(func(ts...))) { return func(ts...); }; }; constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto ... elements) noexcept(noexcept((func(elements) && ...))) { return (func(elements) && ...); }; }; Yes, double parenthesis
  98. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 99/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) { return [...ts = std::forward<Ts>(ts)](const auto& func) noexcept(noexcept(func(ts...))) { return func(ts...); }; }; constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto ... elements) noexcept(noexecpt((func(elements) && ...))) { return (func(elements) && ...); }; }; There’s no way around this repetition!
  99. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 100/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) ... constexpr auto and_elements = []<typename T>(T&& func) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F&& f) const noexcept(noexcept(tup(func(std::forward<F>(f))))) { return tup(func(std::forward<F>(f))); } public: constexpr op_t(Func f, Tuple t) : tup(std::move(t)), func(std::move(f)) {} template <typename T> constexpr auto operator==(const T& t) const noexcept(noexcept(apply(equal_to(t)))) { return apply(equal_to(t)); } ... };
  100. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 101/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) ... constexpr auto and_elements = []<typename T>(T&& func) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F&& f) const noexcept(noexcept(tup(func(std::forward<F>(f))))) { return tup(func(std::forward<F>(f))); } public: constexpr op_t(Func f, Tuple t) : tup(std::move(t)), func(std::move(f)) {} template <typename T> constexpr auto operator==(const T& t) const noexcept(noexcept(apply(equal_to(t)))) { return apply(equal_to(t)); } ... };
  101. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 102/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) ... constexpr auto and_elements = []<typename T>(T&& func) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F&& f) const noexcept(noexcept(tup(func(std::forward<F>(f))))) { return tup(func(std::forward<F>(f))); } public: constexpr op_t(Func f, Tuple t) : tup(std::move(t)), func(std::move(f)) {} template <typename T> constexpr auto operator==(const T& t) const noexcept(noexcept(apply(equal_to(t)))) { return apply(equal_to(t)); } ... }; Ugly ugly repetition!
  102. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 103/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) ... constexpr auto and_elements = []<typename T>(T&& func) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F&& f) const noexcept(noexcept(tup(func(std::forward<F>(f))))) { return tup(func(std::forward<F>(f))); } public: constexpr op_t(Func f, Tuple t) : tup(std::move(t)), func(std::move(f)) {} template <typename T> constexpr auto operator==(const T& t) const noexcept(noexcept(apply(equal_to(t)))) { return apply(equal_to(t)); } ... }; Things to improve: ✔ constexpr ✔ Perfect forwarding ✔ Conditional noexcept • Explicit return type
  103. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 104/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) ... constexpr auto and_elements = []<typename T>(T&& func) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F&& f) const noexcept(noexcept(tup(func(std::forward<F>(f))))) { return tup(func(std::forward<F>(f))); } public: constexpr op_t(Func f, Tuple t) : tup(std::move(t)), func(std::move(f)) {} template <typename T> constexpr auto operator==(const T& t) const noexcept(noexcept(apply(equal_to(t)))) { return apply(equal_to(t)); } ... }; Things to improve: ✔ constexpr ✔ Perfect forwarding ✔ Conditional noexcept • Explicit return type
  104. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 105/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) { return [...ts = std::forward<Ts>(ts)](const auto& func) noexcept(noexcept(func(ts…))) { return func(ts...); }; }; constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto ... elements) noexcept(noexcept((func(elements) && ...))) { return (func(elements) && ...); }; };
  105. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 106/124

    Yes, double parenthesis constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) { return [...ts = std::forward<Ts>(ts)](const auto& func) noexcept(noexcept(func(ts...))) -> decltype(func(ts...)) { return func(ts...); }; }; constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto ... elements) noexcept(noexcept((func(elements) && ...))) -> decltype((func(elements) && ...)) { return (func(elements) && ...); }; }; 7.82
  106. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 107/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) { return [...ts = std::forward<Ts>(ts)](const auto& func) noexcept(noexcept(func(ts...))) -> decltype(func(ts...)) { return func(ts...); }; }; constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto ... elements) noexcept(noexcept((func(elements) && ...))) -> decltype((func(elements) && ...)) { return (func(elements) && ...); }; }; https://sacratomatovillepost.com/2017/10/03/stop-being -silly/
  107. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 108/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) { return [...ts = std::forward<Ts>(ts)](const auto& func) noexcept(noexcept(func(ts...))) -> decltype(func(ts...)) { return func(ts...); }; }; constexpr auto and_elements = []<typename T>(T&& func) { return [func = std::forward<T>(func)](auto ... elements) noexcept(noexcept((func(elements) && ...))) -> decltype((func(elements) && ...)) { return (func(elements) && ...); }; }; www.youtube.com/watch?v=I3T4lePH-yA
  108. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 109/124

    constexpr auto tuple = []<typename ... Ts>(Ts&& ... ts) ... constexpr auto and_elements = []<typename T>(T&& func) ... template <typename Func, typename Tuple> class op_t { Tuple tup; [[no_unique_address]] Func func; template <typename F> constexpr auto apply(F&& f) const noexcept(noexcept(tup(func(std::forward<F>(f))))) -> decltype(tup(func(std::forward<F>(f)))) { return tup(func(std::forward<F>(f))); } public: constexpr op_t(Func f, Tuple t) : tup(std::move(t)), func(std::move(f)) {} template <typename T> constexpr auto operator==(const T& t) const noexcept(noexcept(apply(equal_to(t)))) -> decltype(apply(equal_to(t))) { return apply(equal_to(t)); } Things to improve: ✔ constexpr ✔ Perfect forwarding ✔ Conditional noexcept ✔ Explicit return type
  109. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 111/124

    Modern Techniques for Keeping Your Code DRY That was
  110. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 112/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY That was
  111. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 113/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY That was Remember
  112. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 114/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY That was Remember • Fold expressions are awesome
  113. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 115/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY Remember • Fold expressions are awesome • std::tuple<> and std::apply() is awsome
  114. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 116/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY Remember • Fold expressions are awesome • std::tuple<> and std::apply() is awsome • Higher order functions are awesome
  115. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 117/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY Remember • Fold expressions are awesome • std::tuple<> and std::apply() is awsome • Higher order functions are awesome • Lambdas are awesome
  116. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 118/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY Remember • Fold expressions are awesome • std::tuple<> and std::apply() is awsome • Higher order functions are awesome • Lambdas are awesome • C++20 lambdas are awsomer!
  117. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 119/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY Remember • Fold expressions are awesome • std::tuple<> and std::apply() is awsome • Higher order functions are awesome • Lambdas are awesome • C++20 lambdas are awsomer! • Compilers are awesome
  118. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 120/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY Remember • Fold expressions are awesome • std::tuple<> and std::apply() is awsome • Higher order functions are awesome • Lambdas are awesome • C++20 lambdas are awsomer! • Compilers are awesome • noexcept/SFINAE-return is aweso^H^Hful
  119. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 121/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY Remember • Fold expressions are awesome • std::tuple<> and std::apply() is awsome • Higher order functions are awesome • Lambdas are awesome • C++20 lambdas are awsomer! • Compilers are awesome • noexcept/SFINAE-return is aweso^H^Hful • Sweating the small stuff makes you annoyed
  120. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 122/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY Remember • Fold expressions are awesome • std::tuple<> and std::apply() is awsome • Higher order functions are awesome • Lambdas are awesome • C++20 lambdas are awsomer! • Compilers are awesome • noexcept/SFINAE-return is aweso^H^Hful • Sweating the small stuff makes you annoyed • But it can lead to neat code
  121. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 123/124

    Björn Fahller Modern Techniques for Keeping Your Code DRY Remember • Fold expressions are awesome • std::tuple<> and std::apply() is awsome • Higher order functions are awesome • Lambdas are awesome • C++20 lambdas are awsomer! • Compilers are awesome • noexcept/SFINAE-return is aweso^H^Hful • Sweating the small stuff makes you annoyed • But it can lead to neat code <C++>
  122. Modern DRY C++ – StockholmCpp © Björn Fahller @bjorn_fahller 124/124

    [email protected] @bjorn_fahller @rollbear Björn Fahller Modern Techniques for Keeping Your Code DRY