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

Code::Dive - Modern Techniques for Keeping Your Code DRY

Code::Dive - 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, Björn will show techniques from modern C++ that help 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 help getting rid of the patterns that keep repeating in your code.

5d138ccf47c8d79aa8f0d29900f9e07b?s=128

Björn Fahller

November 20, 2019
Tweet

Transcript

  1. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    1/127 Modern Techniques for Keeping Your Code DRY <C++> Björn Fahller
  2. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    2/127 assert(state == IDLE =| state == DISCONNECTING =| state == DISCONNECTED);
  3. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    3/127 assert(state == IDLE =| state == DISCONNECTING =| state == DISCONNECTED);
  4. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    4/127 Modern Techniques for Keeping Your Code DRY <C++> Björn Fahller
  5. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    5/127 Modern Techniques for Keeping Your Code DRY <C++> Björn Fahller
  6. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

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

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

    8/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    9/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    10/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    11/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    12/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

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

    14/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    15/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    16/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    17/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    18/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

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

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

    21/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    22/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    23/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    24/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

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

    26/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    27/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    28/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    29/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    30/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

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

    32/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    33/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

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

    35/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    36/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    37/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    38/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    39/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    40/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    41/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    42/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    43/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    44/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    45/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    46/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    47/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    48/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    49/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    50/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    51/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    52/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    53/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    54/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    55/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    56/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    57/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    58/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    59/127 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 You can from C++20
  60. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    60/127 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/Ay1TA_
  61. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    61/127 Can we try something else?
  62. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    62/127 Can we try something else? I think lambdas are cool!
  63. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    63/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

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

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

    66/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    67/127 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;}));
  68. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    68/127 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;}));
  69. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    69/127 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;}));
  70. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    70/127 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;}));
  71. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    71/127 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)));
  72. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    72/127 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)));
  73. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    73/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    74/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    75/127 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++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    76/127 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);
  77. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    77/127 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);
  78. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    78/127 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.
  79. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    79/127 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);
  80. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    80/127 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
  81. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    81/127 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:
  82. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    82/127 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
  83. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    83/127 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
  84. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    84/127 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
  85. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    85/127 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
  86. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    86/127 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
  87. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    87/127 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
  88. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    88/127 constexpr auto and_elements = [](auto func) { return [=](auto ==. elements) { return (func(elements) =& ==.); }; };
  89. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

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

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

    91/127 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.
  92. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    92/127 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) =& ==.); }; };
  93. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    93/127 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!
  94. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    94/127 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.
  95. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    95/127 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) =& ==.); }; };
  96. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    96/127 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)==.)); };
  97. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    97/127 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
  98. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    98/127 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
  99. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    99/127 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) =& ==.); }; };
  100. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    100/127 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
  101. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    101/127 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!
  102. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    102/127 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)); } ==. };
  103. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    103/127 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)); } ==. };
  104. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    104/127 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!
  105. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    105/127 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
  106. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    106/127 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
  107. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    107/127 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) =& ==.); }; };
  108. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    108/127 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
  109. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    109/127 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) =& ==.); }; }; "Ellie, Colorkey, cateye" by Isaril is licensed under CC BY-NC-SA 2.0 https://flic.kr/p/Jp53FM
  110. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    110/127 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
  111. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    111/127 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
  112. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    112/127 That was
  113. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    113/127 Modern Techniques for Keeping Your Code DRY That was
  114. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    114/127 Björn Fahller Modern Techniques for Keeping Your Code DRY That was
  115. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    115/127 Björn Fahller Modern Techniques for Keeping Your Code DRY That was Remember
  116. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    116/127 Björn Fahller Modern Techniques for Keeping Your Code DRY That was Remember • Fold expressions are awesome
  117. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    117/127 Björn Fahller Modern Techniques for Keeping Your Code DRY Remember • Fold expressions are awesome • std::tuple<> and std::apply() is awsome
  118. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    118/127 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
  119. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    119/127 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
  120. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    120/127 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!
  121. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    121/127 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
  122. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    122/127 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 Matt Godbolt is awesome!
  123. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    123/127 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
  124. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    124/127 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
  125. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    125/127 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
  126. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    126/127 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++>
  127. Modern DRY C++ –– code::dive 2019 © Björn Fahller @bjorn_fahller

    127/127 bjorn@fahller.se @bjorn_fahller @rollbear Björn Fahller Modern Techniques for Keeping Your Code DRY