Skip to content

Commit 752a4b0

Browse files
committed
updates README, adds new expression tree drawio diagram, expands unit tests
1 parent 74d9704 commit 752a4b0

File tree

6 files changed

+149
-14
lines changed

6 files changed

+149
-14
lines changed

README.md

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,67 @@ This project is under development and is subject to change. Project contribution
2424
## A Quick Example
2525

2626
```cpp
27-
// some code here
28-
// imagine we have some class struct whatever ...
29-
// we can create an expression_tree for evaluating the contents of instances of this struct class whatever
27+
#include <attwoodn/expression_tree.hpp>
28+
29+
using namespace attwoodn::expression_tree;
30+
31+
// imagine there is some user-defined type, like so
32+
struct my_type {
33+
int my_int;
34+
bool my_bool;
35+
36+
int get_my_int() const {
37+
return my_int;
38+
}
39+
};
40+
41+
...
42+
43+
// the Cpp Expression Tree library can be used to create an expression tree to evaluate instances of my_type
44+
45+
// create an expression tree: my_bool == true OR (get_my_int() > 0 AND my_int < 10)
46+
expression_tree<my_type> expr {
47+
make_expr(&my_type::my_bool, op::equals, true)
48+
->OR((make_expr(&my_type::get_my_int, op::greater_than, 0)
49+
->AND(make_expr(&my_type::my_int, op::less_than, 10))
50+
)
51+
)
52+
};
53+
54+
// create an instance of my_type that satisfies the above expression
55+
my_type obj;
56+
obj.my_bool = true;
57+
obj.my_int = 4;
58+
59+
// evaluating obj against the expression_tree above returns true
60+
assert(expr.evaluate(obj));
61+
62+
// update obj so that my_int is outside the range 1..9
63+
obj.my_bool = true;
64+
obj.my_int = 12;
65+
66+
// evaluating obj against the expression_tree above returns true
67+
assert(expr.evaluate(obj));
68+
69+
// update obj so that my_bool is false and my_int is outside the range 1..9
70+
obj.my_bool = false;
71+
obj.my_int = 0;
72+
73+
// evaluating obj against the expression_tree above returns false
74+
assert(!expr.evaluate(obj));
3075
```
3176

32-
Below is a tree diagram showing the content of the expression_tree that was created in the example code above:
77+
Below is a diagram showing the content of the `expression_tree<my_type>` that was created in the example code above:
3378

3479
<p align="center">
35-
<img src="docs/expression-tree.png"/>
80+
<img src="docs/a_quick_example_expression_tree.png"/>
3681
</p>
3782

83+
As you can imagine, this example code can be expanded to fit a variety of use cases and struct/class type structures. More complex code examples are provided in the documentation below. Further, there are a number of unit tests located in the `tests` directory, which may be helpful for getting familiar with the library.
84+
3885
## Creating Expression Trees
3986

40-
The `expression_tree` class is a templated, RAII container class that takes ownership of user-defined expressions. The template parameter of `expression_tree` is the type of object that the `expression_tree` can evaluate. Assuming there is a user-defined class named `my_type`, the templated `expression_tree` type would look like this: `expression_tree<my_type>`. The template argument of `expression_tree` cannot be a primitive type, like `int`, `char`, or `double`.
87+
The `expression_tree` class is a templated, RAII container class that takes ownership of user-defined expressions. The `expression_tree` class can be moved and/or copied to different contexts while maintaining consistency and safety. The template parameter of `expression_tree` is the type of object that the `expression_tree` can evaluate. Assuming there is a user-defined class named `my_type`, the templated `expression_tree` type would look like this: `expression_tree<my_type>`. The template argument of `expression_tree` cannot be a primitive type, like `int`, `char`, or `double`.
4188

4289
An `expression_tree` cannot be default constructed - it must be initialized with an expression. Users can easily and intuitively define expressions using one of the `make_expr` helper functions found in the namespace `attwoodn::expression_tree`. `make_expr` generates heap-allocated pointers to expression tree nodes and returns them. As such, the returned expression tree node pointers should be managed carefully. If the returned pointers are not wrapped in an `expression_tree` or a smart pointer, they will need to be explicitly `delete`d by the calling code.
4390

@@ -49,20 +96,29 @@ Here are some examples of how you might handle the return value from one of the
4996

5097
using namespace attwoodn::expression_tree;
5198

99+
// let's bring back the same implementation of my_type as shown above
52100
struct my_type {
53-
int my_int = 5;
54-
bool my_bool = true;
101+
int my_int;
102+
bool my_bool;
103+
104+
int get_my_int() const {
105+
return my_int;
106+
}
55107
};
56108

109+
57110
...
58111

112+
59113
// The heap-allocated expression node pointer returned by make_expr becomes owned by the expression_tree
60114
expression_tree<my_type> expr_tree_raw {
61115
make_expr(&my_type::my_bool, op::equals, true)
62116
};
63117

118+
64119
...
65120

121+
66122
// The heap-allocated expression node pointer returned by make_expr becomes owned by the unique_ptr
67123
std::unique_ptr<node::expression_tree_node<my_type>> smart_expr {
68124
make_expr(&my_type::my_bool, op::equals, true)
@@ -71,8 +127,10 @@ std::unique_ptr<node::expression_tree_node<my_type>> smart_expr {
71127
// the expression_tree takes ownership of the unique_ptr
72128
expression_tree<my_type> expr_tree_smart(std::move(smart_expr));
73129

130+
74131
...
75132

133+
76134
// The heap-allocated expression node pointer returned by make_expr must be explicitly deleted
77135
auto* expr_raw = make_expr(&my_type::my_bool, op::equals, true);
78136
delete expr_raw;
@@ -205,4 +263,6 @@ After cloning and compiling the project, navigate to the build directory that wa
205263
ctest .
206264
```
207265

208-
CTest will execute the unit tests and provide a pass/fail indication for each one.
266+
CTest will execute the unit tests and provide a pass/fail indication for each one.
267+
268+
The address sanitizer is enabled on every unit test executable. A test will fail should memory leak during test execution.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<mxfile host="Electron" modified="2023-05-10T18:19:44.134Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.2.8 Chrome/112.0.5615.165 Electron/24.2.0 Safari/537.36" etag="A1TK9bRuSOZq5wIYVlKq" version="21.2.8" type="device">
2+
<diagram name="Page-1" id="MzqSHk1QGt0DU5yl56VU">
3+
<mxGraphModel dx="1360" dy="738" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
4+
<root>
5+
<mxCell id="0" />
6+
<mxCell id="1" parent="0" />
7+
<mxCell id="4_OO1GRJjltScIyBnV0f-2" value="&lt;font style=&quot;font-size: 15px;&quot;&gt;expression_tree&amp;lt;my_type&amp;gt;&lt;/font&gt;" style="swimlane;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;shadow=1;" vertex="1" parent="1">
8+
<mxGeometry x="80" y="95" width="550" height="435" as="geometry" />
9+
</mxCell>
10+
<mxCell id="4_OO1GRJjltScIyBnV0f-4" value="&lt;font size=&quot;1&quot; style=&quot;&quot;&gt;&lt;b style=&quot;font-size: 15px;&quot;&gt;OR&lt;/b&gt;&lt;/font&gt;" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#dae8fc;strokeColor=#6c8ebf;shadow=1;" vertex="1" parent="4_OO1GRJjltScIyBnV0f-2">
11+
<mxGeometry x="174" y="50" width="80" height="80" as="geometry" />
12+
</mxCell>
13+
<mxCell id="4_OO1GRJjltScIyBnV0f-6" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;startArrow=classic;startFill=1;endArrow=none;endFill=0;edgeStyle=orthogonalEdgeStyle;curved=1;shadow=1;" edge="1" parent="4_OO1GRJjltScIyBnV0f-2" source="4_OO1GRJjltScIyBnV0f-5" target="4_OO1GRJjltScIyBnV0f-4">
14+
<mxGeometry relative="1" as="geometry" />
15+
</mxCell>
16+
<mxCell id="4_OO1GRJjltScIyBnV0f-5" value="&lt;font style=&quot;font-size: 15px;&quot;&gt;my_bool == true&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;shadow=1;" vertex="1" parent="4_OO1GRJjltScIyBnV0f-2">
17+
<mxGeometry x="40" y="200" width="134" height="60" as="geometry" />
18+
</mxCell>
19+
<mxCell id="4_OO1GRJjltScIyBnV0f-8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;curved=1;startArrow=classic;startFill=1;endArrow=none;endFill=0;shadow=1;" edge="1" parent="4_OO1GRJjltScIyBnV0f-2" source="4_OO1GRJjltScIyBnV0f-7" target="4_OO1GRJjltScIyBnV0f-4">
20+
<mxGeometry relative="1" as="geometry" />
21+
</mxCell>
22+
<mxCell id="4_OO1GRJjltScIyBnV0f-7" value="&lt;font style=&quot;font-size: 15px;&quot;&gt;&lt;b&gt;AND&lt;/b&gt;&lt;/font&gt;" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#dae8fc;strokeColor=#6c8ebf;shadow=1;" vertex="1" parent="4_OO1GRJjltScIyBnV0f-2">
23+
<mxGeometry x="290" y="200" width="80" height="80" as="geometry" />
24+
</mxCell>
25+
<mxCell id="4_OO1GRJjltScIyBnV0f-10" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;startArrow=classic;startFill=1;endArrow=none;endFill=0;edgeStyle=orthogonalEdgeStyle;curved=1;shadow=1;" edge="1" parent="4_OO1GRJjltScIyBnV0f-2" source="4_OO1GRJjltScIyBnV0f-11" target="4_OO1GRJjltScIyBnV0f-7">
26+
<mxGeometry relative="1" as="geometry">
27+
<mxPoint x="328" y="280" as="targetPoint" />
28+
</mxGeometry>
29+
</mxCell>
30+
<mxCell id="4_OO1GRJjltScIyBnV0f-11" value="&lt;span style=&quot;font-size: 15px;&quot;&gt;get_my_int() &amp;gt; 0&lt;/span&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;shadow=1;" vertex="1" parent="4_OO1GRJjltScIyBnV0f-2">
31+
<mxGeometry x="150" y="350" width="140" height="60" as="geometry" />
32+
</mxCell>
33+
<mxCell id="4_OO1GRJjltScIyBnV0f-12" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;startArrow=classic;startFill=1;endArrow=none;endFill=0;edgeStyle=orthogonalEdgeStyle;curved=1;shadow=1;" edge="1" parent="4_OO1GRJjltScIyBnV0f-2" source="4_OO1GRJjltScIyBnV0f-13" target="4_OO1GRJjltScIyBnV0f-7">
34+
<mxGeometry relative="1" as="geometry">
35+
<mxPoint x="580" y="280" as="targetPoint" />
36+
</mxGeometry>
37+
</mxCell>
38+
<mxCell id="4_OO1GRJjltScIyBnV0f-13" value="&lt;span style=&quot;font-size: 15px;&quot;&gt;my_int &amp;lt; 10&lt;/span&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;shadow=1;" vertex="1" parent="4_OO1GRJjltScIyBnV0f-2">
39+
<mxGeometry x="370" y="350" width="140" height="60" as="geometry" />
40+
</mxCell>
41+
</root>
42+
</mxGraphModel>
43+
</diagram>
44+
</mxfile>
51.3 KB
Loading

tests/expression_tree.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using namespace attwoodn::expression_tree;
77

88
void test_expression_tree_std_string_template();
9+
void test_quick_example_expression_tree();
910
void test_simple_expression_tree();
1011
void test_complex_expression_tree();
1112
void test_moved_expression_tree();
@@ -14,6 +15,7 @@ void test_user_defined_operator();
1415

1516
int main(int argc, char** argv) {
1617
test_expression_tree_std_string_template();
18+
test_quick_example_expression_tree();
1719
test_simple_expression_tree();
1820
test_complex_expression_tree();
1921
test_moved_expression_tree();
@@ -23,6 +25,30 @@ int main(int argc, char** argv) {
2325
return EXIT_SUCCESS;
2426
}
2527

28+
void test_quick_example_expression_tree() {
29+
expression_tree<my_type> expr {
30+
make_expr(&my_type::my_bool, op::equals, true)
31+
->OR((make_expr(&my_type::get_my_int, op::greater_than, 0)
32+
->AND(make_expr(&my_type::my_int, op::less_than, 10))
33+
)
34+
)
35+
};
36+
37+
my_type obj;
38+
39+
obj.my_bool = true;
40+
obj.my_int = 4;
41+
assert(expr.evaluate(obj));
42+
43+
obj.my_bool = true;
44+
obj.my_int = 12;
45+
assert(expr.evaluate(obj));
46+
47+
obj.my_bool = false;
48+
obj.my_int = 0;
49+
assert(!expr.evaluate(obj));
50+
}
51+
2652
void test_expression_tree_std_string_template() {
2753
expression_tree<std::string> expr {
2854
make_expr(&std::string::empty, op::equals, true)

tests/make_expr_safety.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
#include <attwoodn/expression_tree.hpp>
2+
#include "test_utils.hpp"
23
#include <cassert>
34

45
using namespace attwoodn::expression_tree;
56

67
void make_expr_memory_safety_test();
78

8-
struct my_type {
9-
int my_int = 5;
10-
bool my_bool = true;
11-
};
12-
139
int main(int argc, char** argv) {
1410
make_expr_memory_safety_test();
1511

tests/test_utils.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,13 @@ class data_packet {
3131
public:
3232
std::string sender_name;
3333
packet_payload payload;
34+
};
35+
36+
struct my_type {
37+
int my_int;
38+
bool my_bool;
39+
40+
int get_my_int() const {
41+
return my_int;
42+
}
3443
};

0 commit comments

Comments
 (0)