Tabooli is a header-only C++ library providing data structures and algorithms for declarative, lazy, expression abstractions.
Suppose you would like to compose some data into an expression.
auto bugs = Toon{};
auto daffy = Toon{};
auto sylvester = Toon{};
bool value = bugs && (daffy || !sylvester);
This value would then be fixed. If the values of the data were to change then the expression would need to be re-evaluated. Additionally, if we wanted to store the structure of the expression, perhaps to be interpreted in a different context, then we are stuck since all the structure is lost as soon as the expression is evaluated.
Tabooli is designed to solve these problems.
auto bugs = tabooli::boolean<Toon>();
auto daffy = tabooli::boolean<Toon>();
auto sylvester = tabooli::boolean<Toon>();
auto expression = bugs && (daffy || !sylvester);
// store the expression or pass it around
// or do stuff to change the data values
bool value = expression.evaluate();
Tabooli's boolean
type allows users to wrap arbitrary data
into an expression formed of logical operators.
struct Widget {};
auto w1 = tabooli::boolean<Widget>{};
auto w2 = tabooli::boolean<Widget>{};
auto w3 = tabooli::boolean<Widget>{};
auto expression = w1 || !(w2 && w3);
Expressions can also be created inline, from r-value data. We use the pipe operator to help us, because providing logical operators for arbitrary type would break C++,
using mybool = ;
auto expression = tabooli::boolean<Widget>{} |
Widget{} || !(
tabooli::boolean<Widget>{} | Widget{} && Widget{}i
);
If Widget
is implicitly convertible to bool,
then we can evaluate using the no-args evaluate
method.
bool value = expression.evaluate();
Otherwise we can pass a functor describing how to convert the type to bool.
bool value = expression.evaluate([](const Widget&){ return false; });
The boolean
type is derived from the tabooli::detail::expression
.
This is a standard compliant container with methods to
compose with other trees and also a method called visit
which traverses the tree while calling in on the visitor object.
struct visitor
{
void with_data(const Widget&) {...}
void push_operator(operator_code) {...}
void pop_operator(operator_code) {...}
};
expression.visit(visitor{});
Under the hood, the expression
type is represented as a
binary expression tree
by the tabooli::detail::binary_tree
.
Traversing this tree is O(n)
complexity where n
is the
number of nodes in the tree.
The binary tree type is a standard compliant container.
The easiest way to install the library is to simply add it as a CMake subdirectory of your project and link your targets to the library.
add_subdirectory(tabooli)
add_executable(my_app ...)
target_link_libraries(my_app PRIVATE tabooli)
To build the unit tests set TABOOLI_TESTS=ON
.
The tests require the
Catch2 and
Boost
libraries to be installed.
Tabooli as in to-bool-i. It sounds cute and I like the dish :-)
Contribution and feedback is more than welcome!