我在为我的一个项目编写一个简单的行为模型引擎。现在我卡在试图使一个特定的函数成为constexpr上。
问题在底部,其余部分是背景信息
框架工作原理的简短解释
行为节点可以返回三种状态之一:
enum class State { Fail, Success, Running };
默认的叶节点总是返回这三种状态之一:
constexpr inline auto success() { return []{ return State::Success; }; } constexpr inline auto fail() { return []{ return State::Fail; }; } constexpr inline auto running() { return []{ return State::Running; }; }
可以执行不同的操作,例如否定:
constexpr inline auto operator~(auto rule) { return [rule]{ const auto result = rule(); return result == State::Fail ? State::Success : result == State::Success ? State::Fail : State::Running; }; }
…以及其他逻辑操作:
constexpr inline auto operator&&(auto left, auto right) { return [left, right]{ const auto lresult = left(); const auto rresult = right(); return (lresult == State::Fail || rresult == State::Fail) ? State::Fail : (lresult == State::Success && rresult == State::Success) ? State::Success : State::Running; };}
最后是非常简单的评估函数:
inline State execute(auto rule) { return rule();}
示例
CHECK( State::Success == execute(success() && success()) );CHECK( State::Fail == execute(success() && fail()) );
示例
编写这个示例时我没有访问C++17编译器,因此无法实际编译和测试它。
这里我们使用了一组可以在编译时和运行时评估的规则。一个家伙走到一扇锁着的门前并试图打开它,他是否有钥匙在编译时决定。
auto walking = false;auto door_open = false;auto is_at_door = false;constexpr auto dude_has_key = false;constexpr auto unlock_door = []{return []{ return dude_has_key ? State::Success : State::Fail;};};const auto walk_to_door = [&]{return []{ if(walking) { // 在门前停下 walking = false; is_at_door = true; return State::Success; } if(!is_at_door) { walking = true; return State::Running; } return State::Fail; // 无法穿过门};};const auto open_door = [&]{return []{ // 我们希望在编译时检查门是否锁着 // 因此我们在这里从不检查该条件。 if(!door_open) { door_open = true; } return State::Success};};const auto rules = sequence({walk_to_door, unlock_door && open_door});CHECK( State::Running == execute(rules) ); // 正在走向门CHECK( State::Fail == execute(rules) ); // 无法解锁门
示例
编写这个示例时我没有访问C++17编译器,因此无法实际编译和测试它。
struct MyThing { static constexpr bool PropertyA = true; static constexpr int PowerLevel = 42;};struct YourThing { static constexpr bool PropertyA = false; static constexpr int PowerLevel = 43;};constexpr auto has_property_a(auto thing) { return []{ return decltype(thing)::PropertyA ? State::Success :: State::Fail; };}constexpr auto has_minimum_power_level(auto thing, auto min_pl) { return []{ return decltype(thing)::PowerLevel > min_pl ? Satet::Success ::State::Fail; };} template<typename ThingA, typename ThingB>constexpr auto rule = sequence({ has_property_a(ThingA{}), has_minimum_power_level(ThingB{}, 9000)});execute(sequence({rule<MyThing,YourThing>, __some_runtime_rule});
当前问题
…是排序函数,它应该通过以下测试
CHECK( State::Success == execute(sequence({success(), success(), success()})) );CHECK( State::Fail == execute(sequence({success(), success(), fail()})) );CHECK( State::Running == execute(sequence({success(), success(), running()})) );// 由于第二个是运行中,最后一个(失败)从未被评估CHECK( State::Running == execute(sequence({success(), running(), fail()})) );
换句话说,当序列中的所有节点都返回State::Success
时,它返回State::Success
。如果遇到State::Running
,它会“等待”直到State::Success
或State::Fail
。一个State::Fail
会使整个序列失败。
当前使测试通过的这个函数的实现如下
using Rule = std::function<State()>;inline auto sequence(std::initializer_list<Rule> rules) { return [rules]{ auto result = State::Success; for(auto next : rules) { const auto next_result = next(); result = (result == State::Success && next_result == State::Success) ? State::Success : (result == State::Success && next_result == State::Running) ? State::Running : State::Fail; if(result == State::Fail || result == State::Running) { return result; } } return State::Success; };}
现在我如何使这个函数成为constexpr?
回答:
如果你的序列只是重复应用&&,那么你可以用折叠来指定它
允许空的sequence()
(它返回相当于success
)
template <typename ... Rules>constexpr auto sequence(Rules ... rules){ return [rules...](){ return State::Success && ... && rules(); };}
或者要求至少有一个规则
template <typename First, typename ... Rules>constexpr auto sequence(First first, Rules ... rules){ return [first, rules...](){ return first() && ... && rules(); };}