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));
Puzzle
-
Can you implement a simple system
sys
which leveragesPolicy 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
- Double points for explaining the advantages of
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));
};
}
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; };
};
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
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){};
};
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");
}
}
};
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 {};
};
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;
};
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;
};
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;
};