Quick Start: C++

If you prefer Rust, see the Rust Quick Start. For Python, see the Python Quick Start.

Prerequisites

What You'll Build

A temperature monitoring system with two nodes communicating over shared memory:

  1. Sensor node — publishes temperature readings at 10 Hz
  2. Monitor node — subscribes, detects overheating, publishes alerts

Both nodes run in the same scheduler with deterministic ordering.

Step 1: Create the Project

horus new temp_monitor --lang cpp
cd temp_monitor

This creates:

temp_monitor/
├── horus.toml         # Project config (single source of truth)
├── src/
│   └── main.cpp       # Your code goes here
└── include/

Step 2: Write the Sensor Node

Replace src/main.cpp:

#include <horus/horus.hpp>
#include <cstdio>
#include <cmath>

using namespace horus::literals;

// Simulated temperature sensor
static float read_temperature() {
    static int tick = 0;
    return 22.0f + 5.0f * std::sin(tick++ * 0.1f);  // oscillates 17-27°C
}

int main() {
    // Create scheduler at 10 Hz
    horus::Scheduler sched;
    sched.tick_rate(10_hz);
    sched.name("temp_monitor");

    // Create pub/sub — both share the same SHM topic
    horus::Publisher<horus::msg::CmdVel> sensor_pub("temp.reading");
    horus::Subscriber<horus::msg::CmdVel> monitor_sub("temp.reading");

    // Sensor node: reads temperature, publishes it
    sched.add("sensor")
        .order(0)           // runs first
        .tick([&] {
            auto sample = sensor_pub.loan();     // zero-copy from SHM
            sample->linear = read_temperature();  // store temp in linear field
            sample->timestamp_ns = 0;
            sensor_pub.publish(std::move(sample));
        })
        .build();

    // Monitor node: reads temperature, checks threshold
    sched.add("monitor")
        .order(10)          // runs after sensor
        .tick([&] {
            auto msg = monitor_sub.recv();
            if (!msg) return;

            float temp = msg->get()->linear;
            if (temp > 25.0f) {
                std::printf("[ALERT] Temperature %.1f°C exceeds threshold!\n", temp);
            } else {
                std::printf("[OK]    Temperature %.1f°C\n", temp);
            }
        })
        .build();

    std::printf("Starting temperature monitor (Ctrl+C to stop)...\n");
    sched.spin();  // blocks until stopped
}

Step 3: Build and Run

horus build
horus run

Output:

Starting temperature monitor (Ctrl+C to stop)...
[OK]    Temperature 22.0°C
[OK]    Temperature 22.5°C
[OK]    Temperature 23.5°C
[OK]    Temperature 24.8°C
[ALERT] Temperature 25.9°C exceeds threshold!
[ALERT] Temperature 26.7°C exceeds threshold!
[ALERT] Temperature 26.9°C exceeds threshold!
...

Step 4: Monitor with CLI

In a second terminal:

horus topic list          # see active topics
horus topic echo temp.reading   # watch raw messages
horus node list           # see running nodes

What Just Happened?

  • Zero-copy IPCsensor_pub.loan() returns a pointer directly into shared memory. The monitor reads from the same memory. No serialization, no copying.
  • Deterministic orderingorder(0) guarantees sensor runs before order(10) monitor, every tick.
  • Single scheduler — both nodes share one tick loop. No threads, no race conditions.

Next Steps