rule_engine is a c++ rule engine library
-
Define the structs to be manipulated by rule_engine.
struct Target { int age; std::string name; }; struct Killer { bool has_bullet; Target target; void kill() { std::cout << "killing target: " << target.name << std::endl; } };
-
Register the defined structs so that rule_engine can access them.
RTTR_REGISTRATION { rttr::registration::class_<Killer>("Killer") .property("has_bullet", &Killer::has_bullet) .property("target", &Killer::target) .method("kill", &Killer::kill) ; rttr::registration::class_<Target>("Target") .property("age", &Target::age) .property("name", &Target::name) ; }
-
in your main() function:
// define your rule const char* rule = R"( rule kill "this is a description" { if Assassin.target.age > 18 && Assassin.has_bullet == true then Assassin.kill(); Assassin.has_bullet = false; } )"; Killer killer; killer.target.age = 19; killer.target.name = "chuck norris"; killer.has_bullet = true; rule_engine::Engine e; // create an engine e.load_rules(rule); // load your rule so that engine can execute it rule_engine::DataContext dctx; // register the object to data context so that rule engine can access // and manipulate it. dctx.add("Assassin", killer); e.execute(&dctx); assert(!killer.has_bullet);
-
for more complicated use case, please see here
Then Engine
itself is stateless, it's safe to call execute
concurrently. DataContext
on the other hand not only contains objects to be accessed and manipulated by rules, but also caches intermediate data that accelerates expression evaluation. So it's users' responsibility to guard it when accessed by multiple threads. The best practice is to instantiate one DataContext
for each thread, which eliminates locks and maximize performance.