Skip to main content
A unit is the fundamental building block of a Basis application. It owns a TransportManager, connects to the coordinator, and exposes typed publish/subscribe helpers. You subclass Unit (or SingleThreadedUnit) and implement Initialize() and, optionally, Update().
In generated code, Basis creates a concrete subclass for you. HandlerPubSub structs and the handlers map are populated automatically by the code generator.

struct UnitInitializeOptions

Passed to Unit::Initialize() to control which resources are created during startup.
create_subscribers
bool
default:"true"
When true, the unit creates real transport subscribers for each declared input. Set to false during deterministic replay when subscriptions are driven externally.
basis::UnitInitializeOptions opts;
opts.create_subscribers = false; // disable for replay
unit.Initialize(opts);

class Unit

basis::Unit — defined in cpp/unit/include/basis/unit.h The abstract base class for all Basis units. Manages the transport manager, coordinator connection, publishers, subscribers, and the handler registry used by the code generator.

Constructor

Unit(std::string_view unit_name)
unit_name
std::string_view
required
Human-readable name for this unit. Used for logging and coordinator identification. A dedicated spdlog logger is created under this name.

WaitForCoordinatorConnection

basis::core::transport::CoordinatorConnector* WaitForCoordinatorConnection()
Blocks until a TCP connection to the coordinator is established, retrying every second. Returns a non-owning pointer to the internal CoordinatorConnector; the unit retains ownership.
Call this before CreateTransportManager() when you need publisher/subscriber discovery across processes.

CreateTransportManager

basis::core::transport::TransportManager* CreateTransportManager(
    basis::RecorderInterface* recorder = nullptr)
Constructs a standard TransportManager (with inproc and TCP transports) and stores it internally. Returns a non-owning pointer.
recorder
basis::RecorderInterface*
default:"nullptr"
Optional recorder. When non-null, all publishers created through this transport manager will write serialized messages to the recorder for every topic that matches the recorder’s filter.

template <typename T_MSG, typename T_Serializer = SerializationHandler<T_MSG>::type>
[[nodiscard]] std::shared_ptr<core::transport::Publisher<T_MSG>>
Advertise(
    std::string_view topic,
    core::serialization::MessageTypeInfo message_type =
        T_Serializer::template DeduceMessageTypeInfo<T_MSG>())
Creates a typed publisher on topic and registers it with the transport manager.
T_MSG
typename
The message type to publish.
T_Serializer
typename
default:"SerializationHandler<T_MSG>::type"
Serializer to use. Defaults to the serializer registered for T_MSG.
topic
std::string_view
required
Topic name, e.g. "/camera/rgb".
message_type
core::serialization::MessageTypeInfo
Type metadata. Deduced automatically from T_MSG and T_Serializer when omitted.
Returns std::shared_ptr<core::transport::Publisher<T_MSG>>. The [[nodiscard]] attribute means you must store the returned pointer; dropping it destroys the publisher.
auto pub = unit.Advertise<sensor_msgs::Image>("/camera/rgb");
auto msg = std::make_shared<sensor_msgs::Image>();
pub->Publish(msg);

Subscribe

template <typename T_MSG, typename T_Serializer = SerializationHandler<T_MSG>::type>
[[nodiscard]] std::shared_ptr<core::transport::Subscriber<T_MSG>>
Subscribe(
    std::string_view topic,
    core::transport::SubscriberCallback<T_MSG> callback,
    basis::core::threading::ThreadPool* work_thread_pool,
    basis::core::containers::SubscriberQueueSharedPtr output_queue = nullptr,
    core::serialization::MessageTypeInfo message_type =
        T_Serializer::template DeduceMessageTypeInfo<T_MSG>())
Creates a typed subscriber and registers it with the transport manager.
T_MSG
typename
The message type to receive.
T_Serializer
typename
default:"SerializationHandler<T_MSG>::type"
Serializer used to deserialize incoming data.
topic
std::string_view
required
Topic name to subscribe to.
callback
core::transport::SubscriberCallback<T_MSG>
required
Callback invoked with std::shared_ptr<const T_MSG> for each message received.
work_thread_pool
basis::core::threading::ThreadPool*
required
Thread pool used to deserialize and dispatch messages.
output_queue
basis::core::containers::SubscriberQueueSharedPtr
default:"nullptr"
Optional queue that serializes callback delivery. Used by SingleThreadedUnit to ensure mutual exclusion.
message_type
core::serialization::MessageTypeInfo
Type metadata. Deduced automatically when omitted.

Initialize

virtual void Initialize(const UnitInitializeOptions& options = {}) = 0
Pure virtual. Override to set up publishers, subscribers, and any other one-time initialization. Called once by main() before the update loop starts.
options
const UnitInitializeOptions&
default:"{}"
Controls subscriber creation behavior.

Update

virtual void Update(
    std::atomic<bool>* stop_token = nullptr,
    const basis::core::Duration& max_execution_duration =
        basis::core::Duration::FromSecondsNanoseconds(0, 0))
Pumps transport and coordinator state. The base implementation calls StandardUpdate() which flushes the transport manager and coordinator connector. Override in subclasses to add polling logic.
stop_token
std::atomic<bool>*
default:"nullptr"
When set to true by an external thread, the update loop should terminate.
max_execution_duration
const basis::core::Duration&
default:"Duration::FromSecondsNanoseconds(0, 0)"
Maximum wall time to block waiting for events. A zero duration means non-blocking.

Name

const std::string& Name() const
Returns the unit name provided at construction.

GetHandlers

const std::map<std::string, HandlerPubSub*>& GetHandlers()
Returns the handler registry. Keys are handler names as declared in the unit schema. This map is populated by generated code and consumed by DeterministicReplayer and UnitManager.

class SingleThreadedUnit

basis::SingleThreadedUnit — inherits from Unit A unit where all handler callbacks are serialized through a single shared queue, preventing concurrent execution. Adds an overloaded Subscribe that automatically wires callbacks into this queue.
Use SingleThreadedUnit when your handler callbacks are not thread-safe. For maximum throughput where callbacks are independent, subclass Unit directly and manage your own thread pool.

Overloaded Subscribe

template <typename T_MSG, typename T_Serializer = SerializationHandler<T_MSG>::type>
[[nodiscard]] std::shared_ptr<core::transport::Subscriber<T_MSG>>
Subscribe(
    std::string_view topic,
    core::transport::SubscriberCallback<T_MSG> callback,
    size_t queue_depth = 0,
    core::serialization::MessageTypeInfo message_type =
        T_Serializer::template DeduceMessageTypeInfo<T_MSG>())
Simplified overload that uses the unit’s internal thread_pool and routes callbacks through the shared overall_queue.
topic
std::string_view
required
Topic name to subscribe to.
callback
core::transport::SubscriberCallback<T_MSG>
required
Callback invoked with std::shared_ptr<const T_MSG>.
queue_depth
size_t
default:"0"
Maximum number of pending messages to buffer per subscriber before dropping. 0 means unlimited.
message_type
core::serialization::MessageTypeInfo
Type metadata. Deduced automatically when omitted.

Update (overridden)

virtual void Update(
    std::atomic<bool>* stop_token,
    const basis::core::Duration& max_execution_duration) override
Calls the base Unit::Update(), then drains the callback queue. Blocks for up to max_execution_duration waiting for the first event, then processes all remaining buffered events.

struct HandlerPubSub

basis::HandlerPubSub — defined in cpp/unit/include/basis/unit.h An abstract base populated entirely by generated code. Each declared handler in a unit’s schema produces one HandlerPubSub subclass. The Unit::handlers map holds non-owning pointers to these objects, keyed by handler name.
struct HandlerPubSub {
  using TopicMap = std::map<std::string, std::shared_ptr<const void>>;
  using HandlerExecutingCallback = std::function<TopicMap()>;
  using TypeErasedCallback =
      std::function<void(const std::shared_ptr<const void>,
                         HandlerExecutingCallback*, std::string_view)>;

  virtual void OnRateSubscriberTypeErased(
      basis::core::MonotonicTime now,
      HandlerExecutingCallback* callback) = 0;

  std::map<std::string, TypeErasedCallback> type_erased_callbacks;
  std::vector<std::string> outputs;
  std::optional<basis::core::Duration> rate_duration;
};
type_erased_callbacks
std::map<std::string, TypeErasedCallback>
Maps each subscribed topic’s runtime name to a callback that accepts a type-erased message pointer. Used by DeterministicReplayer to feed recorded messages into handlers without knowing their concrete type at the call site.
outputs
std::vector<std::string>
List of topic names this handler publishes to. Populated by generated code.
rate_duration
std::optional<basis::core::Duration>
When set, the handler fires on a timer at this period rather than on message receipt.

Free functions

CreateStandardTransportManager

std::unique_ptr<basis::core::transport::TransportManager>
CreateStandardTransportManager(basis::RecorderInterface* recorder = nullptr)
Creates a TransportManager with the standard set of transports (inproc + TCP). Called internally by Unit::CreateTransportManager().

StandardUpdate

void StandardUpdate(
    basis::core::transport::TransportManager* transport_manager,
    basis::core::transport::CoordinatorConnector* coordinator_connector)
Pumps the transport manager and coordinator connector. Called by Unit::Update().

Build docs developers (and LLMs) love