Skip to content

Latest commit

 

History

History
248 lines (202 loc) · 5.6 KB

210.md

File metadata and controls

248 lines (202 loc) · 5.6 KB
Info

Example

template<class T>
constexpr auto foo(T t) {
  if constexpr(requires{ t.foo; }) {
    return t.foo;
  } else {
    return 0;
  }
}

constexpr struct { int foo{42}; } f;
static_assert(42 == foo(f));

constexpr struct { int bar{42}; } b;
static_assert(0 == foo(b));

https://godbolt.org/z/PKhE9x

Puzzle

  • Can you implement a simple system sys which leverages Policy By Design to ensure that the process call returns { true: if the condition is satisfied by the trade; false: otherwise }?

    • Double points for explaining the advantages of Design By Introspection
struct sys;/*TODO*/

int main() {
  using namespace boost::ut;

  "should not trade since there is no condition set"_test = [] {
    sys sys{};
    struct { int price = 42; } trade;
    expect(not sys.process(trade));
  };

  "should not trade since condition doesn't match"_test = [] {
    sys sys{};
    sys.process([](auto price) { return price > 100; });
    struct { int price = 42; } trade;
    expect(not sys.process(trade));
  };

  "should trade since condition matches"_test = [] {
    sys sys{};
    sys.process([](auto price) { return price > 100; });
    struct { int price = 142; } trade;
    expect(sys.process(trade));
  };

  "should only trade on the second condition"_test = [] {
    sys sys{};
    struct { int price = 42; } trade;

    sys.process([](auto price) { return price == 100; });
    expect(not sys.process(trade));

    sys.process([](auto price) { return price == 42; });
    expect(sys.process(trade));
  };
}

https://godbolt.org/z/oTjhv4

Solutions

struct sys {
  bool process(auto input) {
    if constexpr (requires { input.price; }) {
      return condition_(input.price);
    } else {
      condition_ = input;
      return false;
    }
  }
 private:
  using condition_t = std::function<bool(int)>;
  condition_t condition_ = [](int) { return false; };
};

https://godbolt.org/z/4bbszK

struct sys {
    std::optional<std::function<bool(int)>> condition{};

    constexpr auto process(const auto& x) {
        if constexpr (requires { condition = x; }) {
            condition = x;
        } else if constexpr (requires { x.price; }) {
            if (condition.has_value()) {
                return (*condition)(x.price);
            }
        }
        return false;
    }
};

// Design by Introspection
//  - Enables lambdas which typically aren't directly overloadable to behave differently based on type
//  - Helps replace run-time branches with compile-time branches
//  - Can result in deeper nesting of if statements within a function
//  - Causes a surprising number of silent bugs when almost-correct objects are passed but no action is taken

https://godbolt.org/z/zozxjn

struct sys {
  constexpr auto process(const auto& t) {
    if constexpr (requires{ condition = t; }) {
      condition = t;
    } else if constexpr (requires{ t.price;}) {
      return condition and condition(t.price);
    } else {
      static_assert(not sizeof(t), "type t not supported!");
      return false;
    }
  }

 private:
  bool (*condition)(int){};
};

https://godbolt.org/z/zYe7ao

struct sys {
private:
  using condition_t = auto(*) (int) -> bool;
  condition_t condition = nullptr;

  template <typename T>
  constexpr static inline bool always_false = false;

public:
  template <typename T>
  constexpr auto process(const T& t) {
    if constexpr (requires { condition = t; }) {
      condition = t;
    } else if constexpr (requires { condition(t.price); }) {
      return condition and condition(t.price);
    } else {
      static_assert(always_false<T>, "Incorrect argument to process");
    }
  }
};

https://godbolt.org/z/oEvh6T

namespace detail {
template <typename T>
concept priced_object = requires
{
    T {}.price;
};
}

struct sys {
    using condition_func_t = std::function<bool(int)>;

    auto process(const auto& input)
    {
        if constexpr (requires { condition = input; }) {
            condition = input;
        } else if constexpr (detail::priced_object<decltype(input)>) {
            if (not condition.has_value()) {
                return false;
            }
            return (*condition)(input.price);
        } else {
            return false;
        }
    }

    std::optional<condition_func_t> condition {};
};

https://godbolt.org/z/aoeszf

struct sys {
  auto process(const auto& event) -> decltype(auto) {
    if constexpr (requires { condition = event; }) {
      condition = event;
    } else if constexpr (requires { condition(event.price); }) {
      return condition and condition(event.price);
    }
  }

private:
  bool (*condition)(int) = nullptr;
};

https://godbolt.org/z/dnT1h7

struct sys{
    template<class T >
    auto process( T arg )
    {
        if constexpr ( requires{ f=arg ; } )
            f = arg;
        else if( requires{ f(arg.price);} && f)
            return f(arg.price);
        return false;
    }
    std::function<bool(int)> f;
};

https://godbolt.org/z/nKso7E

struct sys {
    template<typename T>
    bool constexpr process(T t) {
        if constexpr( requires(int a){ t.operator()(a); }) {
            procF = t;
        } else if (procF) {
            return procF(t.price);
        }
        return false;
    }
    std::function<bool(int)> procF;
};

https://godbolt.org/z/ozhM8s