11#pragma once
22
3+ #include < memory>
34#include < stdexcept>
45
56namespace attwoodn ::expression_tree {
@@ -53,13 +54,13 @@ namespace attwoodn::expression_tree {
5354 }
5455 }
5556
56- enum class boolean_op {
57- AND,
58- OR
59- };
60-
6157 namespace node {
6258
59+ enum class boolean_op {
60+ AND,
61+ OR
62+ };
63+
6364 template <typename Obj, typename LeftChild, typename RightChild>
6465 class expression_tree_op_node ;
6566
@@ -83,7 +84,17 @@ namespace attwoodn::expression_tree {
8384 * False if the given object did not satisfy the expression in this node and the expressions of all
8485 * nodes under this node in the expression tree.
8586 */
86- virtual bool evaluate (const Obj& obj) = 0;
87+ virtual bool evaluate (const Obj& obj) const = 0;
88+
89+ /* *
90+ * @brief Performs a deep clone of pointers to this base class to avoid object slicing.
91+ */
92+ auto clone () const {
93+ return std::unique_ptr<expression_tree_node<Obj>>(clone_impl ());
94+ }
95+
96+ protected:
97+ virtual expression_tree_node<Obj>* clone_impl () const = 0;
8798 };
8899
89100 /* *
@@ -95,6 +106,11 @@ namespace attwoodn::expression_tree {
95106 public:
96107 using this_type = expression_tree_op_node<Obj, LeftChild, RightChild>;
97108
109+ expression_tree_op_node () = delete ;
110+ expression_tree_op_node (expression_tree_op_node&& other) = delete ;
111+ expression_tree_op_node& operator =(const expression_tree_op_node& other) = delete ;
112+ expression_tree_op_node& operator =(expression_tree_op_node&& other) = delete ;
113+
98114 expression_tree_op_node (boolean_op bool_op)
99115 : bool_op_(bool_op) {}
100116
@@ -123,7 +139,7 @@ namespace attwoodn::expression_tree {
123139 delete l;
124140 }
125141
126- bool evaluate (const Obj& obj) override {
142+ bool evaluate (const Obj& obj) const override {
127143 if (!left_ || !right_) {
128144 throw std::runtime_error (" expression_tree_op_node has a missing child node" );
129145 }
@@ -151,10 +167,10 @@ namespace attwoodn::expression_tree {
151167 * and the other node that was AND'ed with this node. This node becomes the left child. The other node becomes
152168 * the right child.
153169 */
154- template <typename OtherOp, typename OtherCompValue, typename OtherLeafNode,
155- std::enable_if<std::is_same<OtherLeafNode, expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>>::value>* = nullptr >
156- expression_tree_op_node<Obj, this_type, OtherLeafNode>* AND (OtherLeafNode* other) {
157- auto * op_node = new expression_tree_op_node<Obj, this_type, OtherLeafNode> (boolean_op::AND);
170+ template <typename OtherOp, typename OtherCompValue>
171+ auto * AND ( expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>* other) {
172+ using ret = expression_tree_op_node<Obj, this_type, expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>>;
173+ ret * op_node = new ret (boolean_op::AND);
158174 op_node->set_left (this );
159175 op_node->set_right (other);
160176 return op_node;
@@ -166,10 +182,10 @@ namespace attwoodn::expression_tree {
166182 * and the other node that was OR'ed with this node. This node becomes the left child. The other node becomes
167183 * the right child.
168184 */
169- template <typename OtherOp, typename OtherCompValue, typename OtherLeafNode,
170- std::enable_if<std::is_same<OtherLeafNode, expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>>::value>* = nullptr >
171- expression_tree_op_node<Obj, this_type, OtherLeafNode>* OR (OtherLeafNode* other) {
172- auto * op_node = new expression_tree_op_node<Obj, this_type, OtherLeafNode> (boolean_op::OR);
185+ template <typename OtherOp, typename OtherCompValue>
186+ auto * OR ( expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>* other) {
187+ using ret = expression_tree_op_node<Obj, this_type, expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>>;
188+ ret * op_node = new ret (boolean_op::OR);
173189 op_node->set_left (this );
174190 op_node->set_right (other);
175191 return op_node;
@@ -181,10 +197,10 @@ namespace attwoodn::expression_tree {
181197 * and the other node that was AND'ed with this node. This node becomes the left child. The other node becomes
182198 * the right child.
183199 */
184- template <typename OtherLeftChild, typename OtherRightChild, typename OtherOpNode,
185- std::enable_if<std::is_same<OtherOpNode, expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>>::value>* = nullptr >
186- expression_tree_op_node<Obj, this_type, OtherOpNode>* AND (OtherOpNode* other) {
187- auto * op_node = new expression_tree_op_node<Obj, this_type, OtherOpNode> (boolean_op::AND);
200+ template <typename OtherLeftChild, typename OtherRightChild>
201+ auto * AND ( expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>* other) {
202+ using ret = expression_tree_op_node<Obj, this_type, expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>>;
203+ ret * op_node = new ret (boolean_op::AND);
188204 op_node->set_left (this );
189205 op_node->set_right (other);
190206 return op_node;
@@ -196,10 +212,10 @@ namespace attwoodn::expression_tree {
196212 * and the other node that was OR'ed with this node. This node becomes the left child. The other node becomes
197213 * the right child.
198214 */
199- template <typename OtherLeftChild, typename OtherRightChild, typename OtherOpNode,
200- std::enable_if<std::is_same<OtherOpNode, expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>>::value>* = nullptr >
201- expression_tree_op_node<Obj, this_type, OtherOpNode>* OR (OtherOpNode* other) {
202- auto * op_node = new expression_tree_op_node<Obj, this_type, OtherOpNode> (boolean_op::OR);
215+ template <typename OtherLeftChild, typename OtherRightChild>
216+ auto * OR ( expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>* other) {
217+ using ret = expression_tree_op_node<Obj, this_type, expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>>;
218+ ret * op_node = new ret (boolean_op::OR);
203219 op_node->set_left (this );
204220 op_node->set_right (other);
205221 return op_node;
@@ -209,6 +225,11 @@ namespace attwoodn::expression_tree {
209225 boolean_op bool_op_;
210226 LeftChild* left_ { nullptr };
211227 RightChild* right_ { nullptr };
228+
229+ protected:
230+ virtual expression_tree_op_node<Obj, LeftChild, RightChild>* clone_impl () const override {
231+ return new expression_tree_op_node<Obj, LeftChild, RightChild>(*this );
232+ }
212233 };
213234
214235 /* *
@@ -221,6 +242,13 @@ namespace attwoodn::expression_tree {
221242 public:
222243 using this_type = expression_tree_leaf_node<Obj, Op, CompValue>;
223244
245+ expression_tree_leaf_node () = delete ;
246+
247+ expression_tree_leaf_node (const expression_tree_leaf_node& other) = default ;
248+ expression_tree_leaf_node (expression_tree_leaf_node&& other) = default ;
249+ expression_tree_leaf_node& operator =(const expression_tree_leaf_node& other) = default ;
250+ expression_tree_leaf_node& operator =(expression_tree_leaf_node&& other) = default ;
251+
224252 /* *
225253 * @brief Constructor that accepts a reference to a member variable of Obj
226254 */
@@ -239,7 +267,7 @@ namespace attwoodn::expression_tree {
239267
240268 ~expression_tree_leaf_node () override {};
241269
242- bool evaluate (const Obj& obj) override {
270+ bool evaluate (const Obj& obj) const override {
243271 if (member_func_ && member_var_) {
244272 throw std::runtime_error (" expression_tree_leaf_node has both a member function reference " +
245273 std::string (" and member variable reference. Only one is permitted" ));
@@ -273,10 +301,10 @@ namespace attwoodn::expression_tree {
273301 * and the other node that was AND'ed with this node. This node becomes the left child. The other node becomes
274302 * the right child.
275303 */
276- template <typename OtherOp, typename OtherCompValue, typename OtherLeafNode,
277- std::enable_if<std::is_same<OtherLeafNode, expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>>::value>* = nullptr >
278- expression_tree_op_node<Obj, this_type, OtherLeafNode>* AND (OtherLeafNode* other) {
279- auto * op_node = new expression_tree_op_node<Obj, this_type, OtherLeafNode> (boolean_op::AND);
304+ template <typename OtherOp, typename OtherCompValue>
305+ auto * AND ( expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>* other) {
306+ using ret = expression_tree_op_node<Obj, this_type, expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>>;
307+ ret * op_node = new ret (boolean_op::AND);
280308 op_node->set_left (this );
281309 op_node->set_right (other);
282310 return op_node;
@@ -288,10 +316,10 @@ namespace attwoodn::expression_tree {
288316 * and the other node that was OR'ed with this node. This node becomes the left child. The other node becomes
289317 * the right child.
290318 */
291- template <typename OtherOp, typename OtherCompValue, typename OtherLeafNode,
292- std::enable_if<std::is_same<OtherLeafNode, expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>>::value>* = nullptr >
293- expression_tree_op_node<Obj, this_type, OtherLeafNode>* OR (OtherLeafNode* other) {
294- auto * op_node = new expression_tree_op_node<Obj, this_type, OtherLeafNode> (boolean_op::OR);
319+ template <typename OtherOp, typename OtherCompValue>
320+ auto * OR ( expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>* other) {
321+ using ret = expression_tree_op_node<Obj, this_type, expression_tree_leaf_node<Obj, OtherOp, OtherCompValue>>;
322+ ret * op_node = new ret (boolean_op::OR);
295323 op_node->set_left (this );
296324 op_node->set_right (other);
297325 return op_node;
@@ -303,10 +331,10 @@ namespace attwoodn::expression_tree {
303331 * and the other node that was AND'ed with this node. This node becomes the left child. The other node becomes
304332 * the right child.
305333 */
306- template <typename OtherLeftChild, typename OtherRightChild, typename OtherOpNode,
307- std::enable_if<std::is_same<OtherOpNode, expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>>::value>* = nullptr >
308- expression_tree_op_node<Obj, this_type, OtherOpNode>* AND (OtherOpNode* other) {
309- auto * op_node = new expression_tree_op_node<Obj, this_type, OtherOpNode> (boolean_op::AND);
334+ template <typename OtherLeftChild, typename OtherRightChild>
335+ auto * AND ( expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>* other) {
336+ using ret = expression_tree_op_node<Obj, this_type, expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>>;
337+ ret * op_node = new ret (boolean_op::AND);
310338 op_node->set_left (this );
311339 op_node->set_right (other);
312340 return op_node;
@@ -318,10 +346,10 @@ namespace attwoodn::expression_tree {
318346 * and the other node that was OR'ed with this node. This node becomes the left child. The other node becomes
319347 * the right child.
320348 */
321- template <typename OtherLeftChild, typename OtherRightChild, typename OtherOpNode,
322- std::enable_if<std::is_same<OtherOpNode, expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>>::value>* = nullptr >
323- expression_tree_op_node<Obj, this_type, OtherOpNode>* OR (OtherOpNode* other) {
324- auto * op_node = new expression_tree_op_node<Obj, this_type, OtherOpNode> (boolean_op::OR);
349+ template <typename OtherLeftChild, typename OtherRightChild>
350+ auto * OR ( expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>* other) {
351+ using ret = expression_tree_op_node<Obj, this_type, expression_tree_op_node<Obj, OtherLeftChild, OtherRightChild>>;
352+ ret * op_node = new ret (boolean_op::OR);
325353 op_node->set_left (this );
326354 op_node->set_right (other);
327355 return op_node;
@@ -332,6 +360,11 @@ namespace attwoodn::expression_tree {
332360 const CompValue Obj::* member_var_ = nullptr ;
333361 Op logical_op_;
334362 CompValue comp_value_;
363+
364+ protected:
365+ virtual expression_tree_leaf_node<Obj, Op, CompValue>* clone_impl () const override {
366+ return new expression_tree_leaf_node<Obj, Op, CompValue>(*this );
367+ }
335368 };
336369
337370 }
@@ -362,4 +395,85 @@ namespace attwoodn::expression_tree {
362395 node::expression_tree_leaf_node<Obj, Op, CompValue>* make_expr ( CompValue (Obj::* member_func)() const , Op op, CompValue comp_value ) {
363396 return new node::expression_tree_leaf_node<Obj, Op, CompValue>( member_func, op, comp_value );
364397 }
365- }
398+
399+ template <typename Obj>
400+ class expression_tree {
401+ public:
402+ expression_tree () = delete ;
403+
404+ expression_tree (node::expression_tree_node<Obj>* expr) {
405+ if (!expr) {
406+ throw std::runtime_error (" Attempted to construct an expression_tree using a null expression" );
407+ }
408+ expr_ = expr->clone ().release ();
409+ delete expr;
410+ }
411+
412+ expression_tree (std::unique_ptr<node::expression_tree_node<Obj>> expr)
413+ : expression_tree(expr.release()) {}
414+
415+ expression_tree (const expression_tree& other) {
416+ if (!other.expr_ ) {
417+ throw std::runtime_error (" Attempted to copy construct an expression_tree " +
418+ std::string (" from an expression_tree with a null expression" ));
419+ }
420+ expr_ = other.expr_ ->clone ().release ();
421+ }
422+
423+ expression_tree (expression_tree&& other) {
424+ if (!other.expr_ ) {
425+ throw std::runtime_error (" Attempted to move construct an expression_tree " +
426+ std::string (" from an expression_tree with a null expression" ));
427+ }
428+ expr_ = other.expr_ ;
429+ other.expr_ = nullptr ;
430+ }
431+
432+ expression_tree& operator =(const expression_tree& other) {
433+ if (!other.expr_ ) {
434+ throw std::runtime_error (" Attempted copy assignment from an expression_tree with a null expression" );
435+ }
436+ delete expr_;
437+ expr_ = other.expr_ ->clone ().release ();
438+ return *this ;
439+ }
440+
441+ expression_tree& operator =(expression_tree&& other) {
442+ if (!other.expr_ ) {
443+ throw std::runtime_error (" Attempted move assignment from an expression_tree with a null expression" );
444+ }
445+
446+ if (this != &other) {
447+ delete expr_;
448+ expr_ = other.expr_ ;
449+ other.expr_ = nullptr ;
450+ }
451+ return *this ;
452+ }
453+
454+ ~expression_tree () {
455+ delete expr_;
456+ }
457+
458+ /* *
459+ * @brief Evaluates the given object to determine if it satisfies the expressions defined in this expression tree.
460+ *
461+ * @returns True if the given object satisfied the expression tree conditions;
462+ * False if the given object did not satisfy the expression tree conditions.
463+ */
464+ bool evaluate (const Obj& obj) const {
465+ if (!expr_) {
466+ throw std::runtime_error (" expression_tree has a null root expression node" );
467+ }
468+
469+ try {
470+ return expr_->evaluate (obj);
471+ } catch (std::exception& e) {
472+ return false ;
473+ }
474+ }
475+
476+ private:
477+ node::expression_tree_node<Obj>* expr_ = nullptr ;
478+ };
479+ }
0 commit comments