Engineers at Genki Instruments, creators of Wave, an expressive embedded device for music and beyond.
In engineering-speak that means developing everything from hardware and firmware to algorithms and software for end-users.
We mainly use C++, but curious about other systems-level languages. We strive for clean, readable code.
Build boilerplate for a minimal (event driven) concurrent OS.
It should run on bare-metal and have a single thread of execution.
Inspired by our own attempts at building reliable firmware for Wave.
RAII
– Deterministic way to manage resources.Templates
– Generic compile-time programming.lambdas
– Dependency injected, compile-time glue.namespaces
– Group together similar code
std::variant / visit
– Type-safe dynamic dispatch.
…and many more!
gsl::byte
and gsl::span
view into raw memory.namespace events { struct data { std::array<std::byte, MaxPacketSize> packet; }; struct button { uint8_t id; bool toggle_state; }; using var_t = std::variant<const data, const button>; using queue = etl::queue<var_t, MaxEventQueueSize>; } events::queue event_queue{}; static void pin_handler(const uint8_t pin_id) { event_queue.push(events::button{pin_id, gpio_pin_read(pin_id)}); } static void data_receive_handler(const uint8_t* const bytes, const size_t size) { events::data event{}; std::copy(bytes, bytes + size, event.packet.begin()); event_queue.push(event); }
Message passing is a form of communication accomplished by queuing events.
std::variant
offers many ways to achieve dynamic dispatch, e.g., std::holds_alternative
+ std::get
(exceptional), std::get_if
, std::visit
…
#include <variant> // creates helper type for the variant visitor template<class... Ts> struct visitor : Ts... { using Ts::operator()...; }; template<class... Ts> visitor(Ts...) -> visitor<Ts...>; // struct visitor { // void operator()(const int& v) {}; // void operator()(const float& v) {}; // }; void handle_int(); void handle_float(); int main(int argc, char** argv) { std::variant<int, float> v; if (argc == 2) { v = 42; } else { v = 42.0f; } std::visit(visitor{ [&](const int&) { handle_int(); }, [&](const float&) { handle_float(); }, }, v); return 0; }
std::get_if
std::visit
cmp
, nice syntax, exhaustive.#include <tuple> template<typename... Args, typename Func, std::size_t... Idx> void for_each_impl(const std::tuple<Args...>& t, Func&& f, std::index_sequence<Idx...>) { (f(std::get<Idx>(t)), ...); } template<typename Tuple, typename F> constexpr void for_each(Tuple&& tuple, F&& f){ constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value; for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f), std::make_index_sequence<N>{}); } void handle_int(); void handle_float(); int main(int argc, char** argv) { volatile bool scheduler_running = true; volatile bool task_1_running = true; volatile bool task_2_running = true; const auto task_1 = [&] { const auto needs_work = [&] { return task_1_running; }; const auto process_work = [&]{ handle_float(); }; return std::make_pair(needs_work, process_work); }; const auto task_2 = [&] { const auto needs_work = [&] { return task_2_running; }; const auto process_work = [&]{ handle_int(); }; return std::make_pair(needs_work, process_work); }; const auto task_list = std::make_tuple(task_1, task_2); while (scheduler_running) { for_each(task_list, [](const auto& task) { const auto[needs_work, process_work] = task(); while (needs_work()) { process_work(); } }); } return 0; }
#include <https://raw.githubusercontent.com/boost-experimental/sml/master/include/boost/sml.hpp> #include <cstdint> void ui_indicate_shutdown(); void system_shutdown(); namespace events { struct button { uint8_t id; bool toggle_state; }; } struct two_button_shutdown { static constexpr uint8_t ButtonX = 0; static constexpr uint8_t ButtonY = 1; auto operator()() const { using namespace boost::sml; using namespace events; const auto is_x_pressed = [](const auto& e) { return e.id == ButtonX && e.toggle_state; }; const auto is_y_pressed = [](const auto& e) { return e.id == ButtonY && e.toggle_state; }; const auto is_x_released = [](const auto& e) { return e.id == ButtonY && !e.toggle_state; }; const auto is_y_released = [](const auto& e) { return e.id == ButtonY && !e.toggle_state; }; const auto shutdown = [] { system_shutdown(); }; const auto indicate_shutdown = [] { ui_indicate_shutdown(); }; return make_transition_table( *"none_pressed"_s + event<button>[is_x_pressed] = "x_pressed"_s, "none_pressed"_s + event<button>[is_y_pressed] = "y_pressed"_s, "x_pressed"_s + event<button>[is_x_released] = "none_pressed"_s, "y_pressed"_s + event<button>[is_y_released] = "none_pressed"_s, "x_pressed"_s + event<button>[is_y_pressed] / indicate_shutdown = "both_pressed"_s, "y_pressed"_s + event<button>[is_x_pressed] / indicate_shutdown = "both_pressed"_s, "both_pressed"_s + event<button>[is_x_released] = "terminating_y_pressed"_s, "both_pressed"_s + event<button>[is_y_released] = "terminating_x_pressed"_s, "terminating_x_pressed"_s + event<button>[is_x_released] / shutdown = X, "terminating_y_pressed"_s + event<button>[is_y_released] / shutdown = X ); } }; int main(int argc, char** argv){ boost::sml::sm<two_button_shutdown> sm; static_assert(sizeof(sm) == 11); sm.process_event(events::button{0, true}); sm.process_event(events::button{1, true}); sm.process_event(events::button{0, false}); sm.process_event(events::button{1, false}); }
namespace sml = boost::sml; struct MyLogger { template<class SM, class TEvent> void log_process_event(const TEvent&) { LOG("[%s][process_event] %s\n", sml::aux::get_type_name<SM>(), sml::aux::get_type_name<TEvent>()); } template<class SM, class TGuard, class TEvent> void log_guard(const TGuard&, const TEvent&, bool result) { LOG("[%s][guard] %s %s %s\n", sml::aux::get_type_name<SM>(), sml::aux::get_type_name<TGuard>(), sml::aux::get_type_name<TEvent>(), (result ? "[OK]" : "[Reject]")); } template<class SM, class TAction, class TEvent> void log_action(const TAction&, const TEvent&) { LOG("[%s][action] %s %s\n", sml::aux::get_type_name<SM>(), sml::aux::get_type_name<TAction>(), sml::aux::get_type_name<TEvent>()); } template<class SM, class TSrcState, class TDstState> void log_state_change(const TSrcState& src, const TDstState& dst) { LOG("[%s][transition] %s -> %s\n", sml::aux::get_type_name<SM>(), src.c_str(), dst.c_str()); } }; Logger logger{}; sml::sm<two_button_shutdown, sml::logger<MyLogger>> sm{logger};