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

NDC{TechTown} - Modern Techniques for Keeping Your Code DRY

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

September 04, 2019
Tweet

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    1/125 Modern Techniques for Keeping Your Code DRY <C++> Björn Fahller
  2. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    2/125 assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  3. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    3/125 assert(state == IDLE || state == DISCONNECTING || state == DISCONNECTED);
  4. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    4/125 Modern Techniques for Keeping Your Code DRY <C++> Björn Fahller
  5. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    5/125 Modern Techniques for Keeping Your Code DRY Björn Fahller <C++>
  6. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    6/125 Modern Techniques for Keeping Your Code DRY <C++> Björn Fahller
  7. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

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

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

    9/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    10/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    11/125 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);
  12. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    12/125 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));
  13. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    13/125 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!
  14. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

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

    15/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    16/125 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);
  17. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    17/125 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));
  18. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    18/125 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));
  19. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    19/125 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
  20. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

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

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

    22/125 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);
  23. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    23/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    24/125 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));
  25. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    25/125 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
  26. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

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

    27/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    28/125 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);
  29. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    29/125 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
  30. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    30/125 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
  31. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    31/125 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
  32. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

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

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

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

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

    36/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    37/125 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);
  38. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    38/125 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));
  39. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    39/125 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==
  40. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    40/125 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!
  41. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    41/125 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
  42. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    42/125 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));
  43. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    43/125 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!
  44. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    44/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    45/125 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) ...
  46. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    46/125 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
  47. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    47/125 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) ...
  48. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    48/125 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!
  49. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    49/125 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); }
  50. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    50/125 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) ...
  51. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    51/125 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
  52. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    52/125 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) ... each_of? Repeat Everything Again?
  53. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    53/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    54/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    55/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    56/125 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; };
  57. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    57/125 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 each_of : op_t<and_elements, Ts...> { using op_t<and_elements, Ts...>::op_t; };
  58. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    58/125 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 each_of : op_t<and_elements, Ts...> { using op_t<and_elements, Ts...>::op_t; }; template <typename ... Ts> using each_of = op_t<and_elements, Ts...>; In a better world we could deduce constructor template args for aliases too
  59. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    59/125 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 each_of : op_t<and_elements, Ts...> { using op_t<and_elements, Ts...>::op_t; }; https://gcc.godbolt.org/z/EAuUnM
  60. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    61/125 Can we try something else? Lambdas are so cool!
  61. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    62/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    63/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

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

    65/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    66/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    67/125 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;}));
  67. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    68/125 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;}));
  68. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    69/125 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;}));
  69. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    70/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    71/125 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)));
  71. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    72/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0);
  72. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    73/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0);
  73. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    74/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0);
  74. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    75/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0);
  75. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    76/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0);
  76. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    77/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_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.
  77. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    78/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0);
  78. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    79/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0); https://gcc.godbolt.org/z/uyDzC8
  79. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    80/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0); Things to improve:
  80. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    81/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0); Things to improve: • constexpr
  81. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    82/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0); Things to improve: • constexpr
  82. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    83/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0); Things to improve: ✔ constexpr • Perfect forwarding
  83. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    84/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0); Things to improve: ✔ constexpr • Perfect forwarding • Conditional noexcept
  84. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    85/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0); Things to improve: ✔ constexpr • Perfect forwarding • Conditional noexcept • Explicit return type
  85. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    86/125 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 each_of=[](auto...ts){ return op_t(and_elements,tuple(ts...));}; assert(each_of(a,b,c) > 0); Things to improve: ✔ constexpr • Perfect forwarding • Conditional noexcept • Explicit return type
  86. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

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

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

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

    90/125 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.
  90. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    91/125 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) && ...); }; };
  91. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    92/125 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!
  92. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    93/125 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.
  93. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    94/125 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) && ...); }; };
  94. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    95/125 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 each_of=[]<typename ... Ts>(Ts&&... ts){ return op_t(and_elements,tuple(std::forward<Ts>(ts)...)); };
  95. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    96/125 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 each_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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    97/125 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 each_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
  97. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    98/125 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) && ...); }; };
  98. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    99/125 3.08 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
  99. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    100/125 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) && ...); }; }; There’s no way around this repetition!
  100. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    101/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    102/125 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)); } ... };
  102. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    103/125 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!
  103. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    104/125 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++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    105/125 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
  105. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    106/125 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) && ...); }; };
  106. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    107/125 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) && ...); }; }; 3.08
  107. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    108/125 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/
  108. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    109/125 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
  109. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    110/125 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
  110. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    112/125 Modern Techniques for Keeping Your Code DRY That was
  111. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    113/125 Björn Fahller Modern Techniques for Keeping Your Code DRY That was
  112. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    114/125 Björn Fahller Modern Techniques for Keeping Your Code DRY That was Remember
  113. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    115/125 Björn Fahller Modern Techniques for Keeping Your Code DRY That was Remember • Fold expressions are awesome
  114. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    116/125 Björn Fahller Modern Techniques for Keeping Your Code DRY Remember • Fold expressions are awesome • std::tuple<> and std::apply() is awsome
  115. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    117/125 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
  116. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    118/125 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
  117. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    119/125 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!
  118. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    120/125 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
  119. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    121/125 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
  120. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    122/125 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
  121. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    123/125 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
  122. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

    124/125 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++>
  123. Modern DRY C++ – NDC{TechTown} 2019 © Björn Fahller @bjorn_fahller

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