{"id":15047518,"url":"https://github.com/attwoodn/cpp-expression-tree","last_synced_at":"2026-02-13T19:31:55.812Z","repository":{"id":163693651,"uuid":"634647435","full_name":"attwoodn/cpp-expression-tree","owner":"attwoodn","description":"A header-only, C++14 library for creating logical expression trees and using them to evaluate instances of user-defined data types. Inspired by m-peko/booleval.","archived":false,"fork":false,"pushed_at":"2024-05-17T12:53:52.000Z","size":129,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-23T02:15:54.356Z","etag":null,"topics":["boolean-expression","boolean-operators","cpp","cpp14","expression","expression-evaluator","expression-parser","expression-tree","logical-expression","logical-operators"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/attwoodn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-30T19:52:40.000Z","updated_at":"2023-11-27T14:46:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"09f2eb5c-937e-4611-8a5e-731f812dd84e","html_url":"https://github.com/attwoodn/cpp-expression-tree","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/attwoodn/cpp-expression-tree","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attwoodn%2Fcpp-expression-tree","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attwoodn%2Fcpp-expression-tree/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attwoodn%2Fcpp-expression-tree/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attwoodn%2Fcpp-expression-tree/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/attwoodn","download_url":"https://codeload.github.com/attwoodn/cpp-expression-tree/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attwoodn%2Fcpp-expression-tree/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29415573,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-13T06:24:03.484Z","status":"ssl_error","status_checked_at":"2026-02-13T06:23:12.830Z","response_time":78,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["boolean-expression","boolean-operators","cpp","cpp14","expression","expression-evaluator","expression-parser","expression-tree","logical-expression","logical-operators"],"created_at":"2024-09-24T20:59:32.512Z","updated_at":"2026-02-13T19:31:55.791Z","avatar_url":"https://github.com/attwoodn.png","language":"C++","readme":"# Cpp Expression Tree\n\n[![Standard](https://img.shields.io/badge/C%2B%2B-14-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B14)\n[![license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/attwoodn/cpp-expression-tree/blob/main/LICENSE)\n\nCpp Expression Tree is a header-only, C++14 library for creating logical expression trees and using them to evaluate instances of user-defined data types. \n\nInspired by m-peko/booleval.\n\nThis project is under development and is subject to change. Project contributions and issue reports are welcome. The more the merrier! \n( ... well, maybe not so much for bug reports)\n\n\n## Table of Contents\n\n* [A Quick Example](#a-quick-example)\n* [Creating Expression Trees](#creating-expression-trees)\n* [Types of Expression Tree Nodes](#types-of-expression-tree-nodes)\n    * [Expression Tree Leaf Nodes](#expression-tree-leaf-nodes)\n    * [Expression Tree Op Nodes](#expression-tree-op-nodes)\n* [Logical Operators](#logical-operators)\n* [Boolean Operators](#boolean-operators)\n* [Using this Library](#using-this-library)\n* [Compiling](#compiling)\n    * [Running the Unit Tests](#running-the-unit-tests)\n\n\n## A Quick Example\n\n```cpp\n#include \u003cattwoodn/expression_tree.hpp\u003e\n\nusing namespace attwoodn::expression_tree;\n\n// imagine there is some user-defined type, like so\nstruct my_type {\n    int my_int;\n    bool my_bool;\n\n    int get_my_int() const {\n        return my_int;      \n    }\n};\n\n...\n\n// the Cpp Expression Tree library can be used to create an expression tree to \n// evaluate instances of my_type\n\n// create an expression tree: my_bool == true OR (get_my_int() \u003e 0 AND my_int \u003c 10)\nexpression_tree\u003cmy_type\u003e expr {\n    make_expr(\u0026my_type::my_bool, op::equals, true)\n    -\u003eOR((make_expr(\u0026my_type::get_my_int, op::greater_than, 0)\n        -\u003eAND(make_expr(\u0026my_type::my_int, op::less_than, 10))\n        )\n    )\n};\n\n\n// create an instance of my_type that satisfies the above expression\nmy_type obj;\nobj.my_bool = true;\nobj.my_int = 4;\nassert(expr.evaluate(obj));   // returns true - obj matches the expression\n\n\n// update obj so that my_int is outside the range 1..9\nobj.my_bool = true;\nobj.my_int = 12;\nassert(expr.evaluate(obj));   // returns true - obj matches the expression\n\n\n// update obj so that my_bool is false and my_int is outside the range 1..9\nobj.my_bool = false;\nobj.my_int = 0;\nassert(!expr.evaluate(obj));  // returns false - obj does not match the expression\n```\n\nBelow is a diagram showing the content of the `expression_tree\u003cmy_type\u003e` created in the example code above:\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"docs/a_quick_example_expression_tree.png\"/\u003e\n\u003c/p\u003e\n\nAs you can imagine, this example code can be expanded to fit a variety of use cases and user-defined types. 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.\n\n\n## Creating Expression Trees\n\nThe `expression_tree` class is a templated, RAII container class that takes ownership of user-defined expressions. Instances of `expression_tree` can be moved and/or copied to different contexts while maintaining consistency and memory safety. The template parameter of `expression_tree` defines the type of object that the `expression_tree` will evaluate. Assuming there is a user-defined struct named `my_type`, the templated `expression_tree` type would look like this: `expression_tree\u003cmy_type\u003e`. The template argument of `expression_tree` cannot be a primitive type, like `int`, `char`, or `double`.\n\nAn `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 deleted by the calling code. \n\nHere are some examples of how a user may safely handle the return value from one of the `make_expr` helper functions:\n```cpp\n#include \u003cattwoodn/expression_tree.hpp\u003e\n\nusing namespace attwoodn::expression_tree;\n\n// let's bring back the same implementation of my_type as shown above\nstruct my_type {\n    int my_int;\n    bool my_bool;\n\n    int get_my_int() const {\n        return my_int;      \n    }\n};\n\n\n...\n\n\n// The heap-allocated expression node pointer returned by make_expr becomes owned by the expression_tree\nexpression_tree\u003cmy_type\u003e expr_tree_raw {\n    make_expr(\u0026my_type::my_bool, op::equals, true)\n};\n \n\n...\n\n\n// The heap-allocated expression node pointer returned by make_expr becomes owned by the unique_ptr\nstd::unique_ptr\u003cnode::expression_tree_node\u003cmy_type\u003e\u003e smart_expr {\n    make_expr(\u0026my_type::my_bool, op::equals, true)\n};\n\n// the expression_tree takes ownership of the unique_ptr\nexpression_tree\u003cmy_type\u003e expr_tree_smart(std::move(smart_expr));\n\n\n...\n\n\n// The heap-allocated expression node pointer returned by make_expr must be explicitly deleted\nauto* expr_raw = make_expr(\u0026my_type::my_bool, op::equals, true);\ndelete expr_raw;\n```\n\nThe `make_expr` helper function is templated and overloaded to allow for maximum compatibility for use within expressions. There are three definitions of `make_expr`:\n * One that accepts a reference to a value-type class member variable, an operator function, and a comparison value (whose type matches the given class member variable);\n * One that accepts a reference to a pointer-type class member variable, an operator function, and a pointer to a comparison value (whose type matches the given class member variable); and\n * One that accepts a reference to a const class member function, an operator function, and a comparison value (whose type matches the return type of the given const class member function)\n \n\nPlease see the section below for more information about expression tree nodes.\n\n\n## Types of Expression Tree Nodes\n\nThere are two types of expression tree nodes: leaf nodes and op nodes. \n\n### Expression Tree Leaf Nodes\n\nExpression tree leaf nodes contain individual, actionable expressions against which a class/struct instance is evaluated. Expression tree leaf nodes are only ever found at the extremities of the expression tree.\n\n### Expression Tree Op Nodes\n\nExpression tree op nodes contain a boolean operation (AND/OR) and have references to a left child node and a right child node. The child nodes may be expression tree leaf nodes, expression tree op nodes, or a permutation of the two. Expression tree op nodes are only ever found in the inner part of the tree. An expression tree op node is always a parent node and it always has two child nodes.\n\n\n## Logical Operators\n\nThere are several logical operator functions defined in the namespace `attwoodn::expression_tree::op` that can be used to create expression tree leaf nodes. The included operators are:\n * equals\n * not_equals\n * less_than\n * greater_than\n\nEach of the above logical operator functions are templated, and are overloaded to permit passing arguments of either a `const T` type, or a `T*` type. In this manner, value types and pointers are permissible for comparison. \n\nNote that there is a known limitation to comparing `T*` types, such as `char*`, using the above operator functions. With `T*` types, no iteration is performed, so comparison is performed only on the data located at the beginning of the pointer address. For example:\n\n```cpp\nassert(op::equals(\"test\", \"testing 123\"));                            // returns true\n\nassert(!op::equals(std::string(\"test\"), std::string(\"testing 123\"))); // returns false\n```\n\nShould users wish to compare an iterable collection of elements using the provided operator functions, they should compare container types, such as `std::vector\u003cT\u003e`, instead of pointer types like `T*`.\n\nUsers of the library can also easily define their own logical operators and use them when creating expressions. Here is an example of how a user might create their own operator functions and use them in an expression:\n\n```cpp\n#include \u003cattwoodn/expression_tree.hpp\u003e\n\nusing namespace attwoodn::expression_tree;\n\n// imagine there are two user-defined types, like so:\nstruct packet_payload {\n    uint16_t error_code; \n    std::string data;\n    bool checksum_ok;\n\n    uint64_t payload_size() const {\n        return data.size();\n    }\n};\n\n// data packet contains an instance of packet_payload, which needs to be evaluated\nclass data_packet {\n    public:\n        std::string sender_name;\n        packet_payload payload;\n};\n\n\n...\n\n\n// user creates their own logical operator for evaluating incoming packet_payload objects.\n// only the first function argument is used. The other is an \"empty\", ignored instance\nauto is_small_packet_payload = [](const packet_payload\u0026 incoming, const packet_payload\u0026) -\u003e bool {\n    if(incoming.error_code == 0 \u0026\u0026 incoming.checksum_ok \u0026\u0026 incoming.payload_size() \u003c= 10) {\n        return true;\n    }\n    return false;\n};\n\n// User creates an expression that only accepts small, non-errored data packets from Jim. \n// The expression evaluates the packet_payload using the user-defined lambda operator created above\nexpression_tree\u003cdata_packet\u003e expr {\n    make_expr(\u0026data_packet::sender_name, op::equals, std::string(\"Jim\"))\n    -\u003eAND(make_expr(\u0026data_packet::payload, is_small_packet_payload, packet_payload()))\n};\n\ndata_packet incoming_packet;\n\n// Jim sends a small, non-errored data packet\nincoming_packet.sender_name = \"Jim\";\nincoming_packet.payload.checksum_ok = true;\nincoming_packet.payload.data = \"hello!\";\nincoming_packet.payload.error_code = 0;\nassert(expr.evaluate(incoming_packet));    // passes evaluation\n\n// Pam sends the same packet payload\nincoming_packet.sender_name = \"Pam\";\nassert(!expr.evaluate(incoming_packet));   // fails evaluation. No packets from Pam are accepted (sorry)\n\n// Jim sends a packet with a bad checksum\nincoming_packet.sender_name = \"Jim\";\nincoming_packet.payload.checksum_ok = false;\nassert(!expr.evaluate(incoming_packet));   // fails evaluation. Packet was from Jim, but checksum failed\n\n// Jim sends a packet whose payload is too big\nincoming_packet.payload.checksum_ok = true;\nincoming_packet.payload.data = \"Boy do I have a long story for you - so I was talking to Pam ...\";\nassert(!expr.evaluate(incoming_packet));   // fails evaluation. Payload was too big. Give me the TLDR, Jim\n\n// Jim sends a small, rude packet\nincoming_packet.payload.data = \"Dwight sux\";\nassert(expr.evaluate(incoming_packet));    // passes evaluation. The payload was the right size this time\n\n// Jim sends a packet that has an error code\nincoming_packet.payload.error_code = 404;\nassert(!expr.evaluate(incoming_packet));   // fails evaluation. The payload had an error code\n```\n\n## Boolean Operators\n\nBoolean operators are used to chain individual expression tree nodes together. There are two boolean operators that can be used: `AND` and `OR`. These boolean operators are accessible via function calls on the expression nodes. Calling these functions generates a new expression tree op node which becomes the parent of the leaf nodes on either side of the boolean operator\n\nA complex expression tree can be created by calling these functions to chain multiple expression tree nodes together.\n\n\n## Using this Library\n\nTo include this library in your project, simply copy the content of the `include` directory into the `include` directory of your project. That's it! Now, where did I put that Staples \"Easy\" button...?\n\n\n## Compiling\n\nThis project uses the CMake build system. The minimum CMake version is set to 3.10.\n\nFirst, clone the git repository and navigate into the local copy. Once you're there, run the following commands:\n\n```\nmkdir build \u0026\u0026 cd build\ncmake ..\nmake\n```\n\n### Running the Unit Tests\n\nAfter cloning and compiling the project, navigate to the build directory that was created. Enable the `BUILD_TESTING` CMake flag if it is not already enabled. My preferred tool for setting CMake flags via the command line is `ccmake`. Simply run `ccmake ..` in the build directory to get a command line UI for modifying CMake project flags. There, you can enable or disable the `BUILD_TESTING` flag, or set the build type from Release to Debug.\n\nWith the `BUILD_TESTING` flag enabled, run the following command in the build directory:\n\n```\nctest .\n```\n\nCTest will execute the unit tests and provide a pass/fail indication for each one. \n\nThe address sanitizer is enabled on every unit test executable. A test will fail should memory leak during test execution.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fattwoodn%2Fcpp-expression-tree","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fattwoodn%2Fcpp-expression-tree","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fattwoodn%2Fcpp-expression-tree/lists"}