C++ example #
The example tree driving a simulated board from C++14, through orchard/orchard.hpp:
member functions as leaves, a Tree<64> that owns its pool, and the example tree either
loaded from its JSON file or compiled into the program.
Build and run #
From the repository root, with a C++ compiler installed:
cmake -B build && cmake --build build
./build/orchard_cpp_demo examples/posix/tree.json # the tree from its file
./build/orchard_cpp_demo --code # the same tree, built by the compiler
ctest --test-dir build
CMake builds this example only when it finds a C++ compiler, so a C-only toolchain still
builds the library and the C examples. ctest runs it both ways as tests. Both print the
same timeline.
The program does not read the keyboard. It plays a fixed timeline of button presses on a simulated clock, so a run takes no time and always prints the same output:
1200 ms button 0 pressed
1200 ms leds **...
1300 ms leds *....
...
2200 ms button 1 pressed
2200 ms leds *.**.
2600 ms button 2 pressed
2600 ms leds *....
Each branch of the tree gets its turn: button 0 blinks LED 1, button 1 turns on LEDs 2 and 3 until button 2 blocks it, and LED 0 blinks as a heartbeat the rest of the time. At the end it prints the tree with the status of the last tick.
How it works #
main.cpp is about 180 lines.
Leaves are member functions. Board holds the simulated buttons and LEDs, and its
methods button(), led_on(), led_off() and led_toggle() are the leaves.
orchard::member_leaf turns each into the plain function orchard calls, and finds the
Board through the tree's user pointer:
constexpr orchard_leaf_def leaves[] = {
{"button", orchard::member_leaf<Board, &Board::button>},
{"led_on", orchard::member_leaf<Board, &Board::led_on>},
...
};
Board board;
orchard::Tree<64> tree(leaves, &board, sim_clock);
Exceptions stop at the leaf. The leaves use std::array::at(), which throws for an
index out of range: a tree file with "arg": 9 for an LED throws in that leaf.
member_leaf catches it, so it never travels through orchard's C code, and the leaf
fails while the rest of the tree goes on. The example prints the message through
orchard::set_exception_handler().
The tree, compiled. With --code, the tree is not read from a file: it is written in
C++, nested like the JSON, and orchard::compile() turns it into a plan at compile time.
A misspelled leaf name or a negative wait would be a build error, and tree.load()
cannot fail:
constexpr auto example_tree = compile(leaves,
reactive_fallback(
reactive_sequence(condition("button", 0),
sequence(action("led_on", 1), wait(100), action("led_off", 1), wait(100))),
...));
A custom clock. The tree takes any orchard_time_t (*)(void) as the clock for wait
nodes. The example passes a function that reads the simulated time, so a 500 ms wait
takes 50 iterations of the loop and no real time. On a real target, leave the default,
orchard_port_now_ms.
No heap. The pool is inside the Tree, and the Tree, the Board and the file buffer
are globals. Tree's constructor is constexpr, so they are initialized by the compiler,
with no code at startup.
See Using from C++ for the whole interface.