Skip to content

Commit 4a90580

Browse files
committed
adds clone function to circumvent object slicing
1 parent 27b8535 commit 4a90580

File tree

3 files changed

+92
-8
lines changed

3 files changed

+92
-8
lines changed

include/attwoodn/expression_tree.hpp

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <memory>
34
#include <stdexcept>
45

56
namespace 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

@@ -84,6 +85,16 @@ namespace attwoodn::expression_tree {
8485
* nodes under this node in the expression tree.
8586
*/
8687
virtual bool evaluate(const Obj& obj) = 0;
88+
89+
/**
90+
* @brief Performs a deep clone of pointers to this base class to avoid object slicing.
91+
*/
92+
auto clone() {
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
/**
@@ -209,6 +220,11 @@ namespace attwoodn::expression_tree {
209220
boolean_op bool_op_;
210221
LeftChild* left_ { nullptr };
211222
RightChild* right_ { nullptr };
223+
224+
protected:
225+
virtual expression_tree_op_node<Obj, LeftChild, RightChild>* clone_impl() const override {
226+
return new expression_tree_op_node<Obj, LeftChild, RightChild>(*this);
227+
}
212228
};
213229

214230
/**
@@ -332,6 +348,11 @@ namespace attwoodn::expression_tree {
332348
const CompValue Obj::* member_var_ = nullptr;
333349
Op logical_op_;
334350
CompValue comp_value_;
351+
352+
protected:
353+
virtual expression_tree_leaf_node<Obj, Op, CompValue>* clone_impl() const override {
354+
return new expression_tree_leaf_node<Obj, Op, CompValue>(*this);
355+
}
335356
};
336357

337358
}
@@ -362,4 +383,67 @@ namespace attwoodn::expression_tree {
362383
node::expression_tree_leaf_node<Obj, Op, CompValue>* make_expr( CompValue (Obj::* member_func)() const, Op op, CompValue comp_value ) {
363384
return new node::expression_tree_leaf_node<Obj, Op, CompValue>( member_func, op, comp_value );
364385
}
386+
387+
template<typename Obj>
388+
class expression_tree {
389+
public:
390+
expression_tree() = delete;
391+
392+
expression_tree(node::expression_tree_node<Obj>* expr) {
393+
if(!expr) {
394+
throw std::runtime_error("Attempted to construct an expression_tree with a null root expression node");
395+
}
396+
397+
expr_ = expr->clone().release();
398+
delete expr;
399+
}
400+
401+
expression_tree(const expression_tree& other)
402+
: expr_(other.expr_->clone().release()) {}
403+
404+
expression_tree(expression_tree&& other)
405+
: expr_(other.expr_) {
406+
other.expr_ = nullptr;
407+
}
408+
409+
expression_tree& operator=(const expression_tree& other) {
410+
delete expr_;
411+
expr_ = other.expr_->clone().release();
412+
return *this;
413+
}
414+
415+
expression_tree& operator=(const expression_tree&& other) {
416+
if(this != &other) {
417+
delete expr_;
418+
expr_ = other.expr_;
419+
other.expr_ = nullptr;
420+
}
421+
return *this;
422+
}
423+
424+
~expression_tree() {
425+
delete expr_;
426+
}
427+
428+
/**
429+
* @brief Evaluates the given object to determine if it satisfies the expressions defined in this expression tree.
430+
*
431+
* @returns True if the given object satisfied the expression tree conditions;
432+
* False if the given object did not satisfy the expression tree conditions.
433+
*/
434+
bool evaluate(const Obj& obj) {
435+
if(!expr_) {
436+
throw std::runtime_error("expression_tree has a null root expression node");
437+
}
438+
439+
try {
440+
return expr_->evaluate(obj);
441+
} catch(std::exception& e) {
442+
return false;
443+
}
444+
}
445+
446+
private:
447+
node::expression_tree_node<Obj>* expr_ = nullptr;
448+
};
365449
}

tests/expression_tree_op_node.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ void test_AND_op_node_evaluation() {
2121
auto child_expr1 = make_expr(&test_fixture::some_string, op::equals, std::string("hello, world!"));
2222
auto child_expr2 = make_expr(&test_fixture::some_uint, op::less_than, (uint16_t) 500);
2323

24-
auto expr = make_op_node<test_fixture>(child_expr1, boolean_op::AND, child_expr2);
24+
auto expr = make_op_node<test_fixture>(child_expr1, node::boolean_op::AND, child_expr2);
2525

2626
test_fixture fixture;
2727
fixture.some_string = "hello, world!";
@@ -72,7 +72,7 @@ void test_OR_op_node_evaluation() {
7272
auto child_expr1 = make_expr(&test_fixture::some_string, op::equals, std::string("hello, world!"));
7373
auto child_expr2 = make_expr(&test_fixture::some_uint, op::less_than, (uint16_t) 500);
7474

75-
auto expr = make_op_node<test_fixture>(child_expr1, boolean_op::OR, child_expr2);
75+
auto expr = make_op_node<test_fixture>(child_expr1, node::boolean_op::OR, child_expr2);
7676

7777
test_fixture fixture;
7878
fixture.some_string = "hello, world!";

tests/test_utils.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct test_fixture {
2121
* Helper function for creating inner expression tree nodes. I'm undecided if this should be included in the public API.
2222
*/
2323
template<typename Obj, typename LeftChild, typename RightChild>
24-
std::unique_ptr<et::node::expression_tree_node<Obj>> make_op_node(LeftChild* left, et::boolean_op op, RightChild* right) {
24+
std::unique_ptr<et::node::expression_tree_node<Obj>> make_op_node(LeftChild* left, et::node::boolean_op op, RightChild* right) {
2525
auto node = new et::node::expression_tree_op_node<Obj, LeftChild, RightChild>(op);
2626
node->set_left(left);
2727
node->set_right(right);

0 commit comments

Comments
 (0)