C++ Best Practices

State Machine for Asynchronous Queries

When dealing with asynchronous D-Bus queries that can be triggered by external events (like InterfacesAdded), use a state machine to handle race conditions and defer events:

  • Use a querying boolean flag to protect the critical section during an active query.
  • Use an enum state (e.g., None, Add, Remove) to record events that arrive while a query is in flight.
  • When the query completes, check the recorded state and trigger a deferred query if needed (e.g., if an Add event arrived).
  • Centralize the setting and clearing of the querying flag inside the query function to avoid scattered state management.

Compare, Don't Subtract for Sentinel Time Points

Because of the guaranteed overflow, you should never perform arithmetic operations on time_point::min() or time_point::max(). They are strictly meant to be used as sentinel (flag) values.

If you need to know if an interval is valid, use equality operators (== or !=) to check the state of the variable before doing any math:

auto last_event = std::chrono::steady_clock::time_point::min();
auto current_time = std::chrono::steady_clock::now();

// ❌ WRONG: This will overflow and cause undefined behavior!
// auto elapsed = current_time - last_event;

// ✅ CORRECT: Check the sentinel value first
if (last_event != std::chrono::steady_clock::time_point::min()) {
    auto elapsed = current_time - last_event;
    // Process the elapsed time...
}
### Coroutine Reference Safety Across Suspension Points

When using coroutines (e.g., with `boost::asio::yield_context`), do not hold references or pointers to objects in global or shared state across suspension points (`yield`).

Because the coroutine suspends and yields control back to the event loop, other asynchronous tasks or destructors (such as test teardowns) can run and modify or delete the global state before the coroutine resumes or is unwound.

*   **❌ Unsafe Pattern:** Capturing a reference to a map element or global variable and accessing it in a deferred cleanup trap (deleter) or after a `yield` call. If the map is cleared during suspension, the reference becomes dangling.
*   **✅ Safe Pattern:** Capture the lookup key (e.g., a string ID) **by value**, and perform a fresh lookup in the global/shared container after resuming or inside the cleanup trap. If the container was cleared, the lookup will safely fail (e.g., returning `end()`) instead of dereferencing dangling memory.

## Testing & Debugging


### Verify Mock Before Questioning Infrastructure
Before questioning Object Mapper (mapperx) performance or behavior in a testing environment, use `dbus-monitor` and `busctl` to examine the mock server implementation. The mock might be static or missing dynamic behaviors expected by the test.

### Use A/B Testing for Regressions
Use A/B testing (running specific tests with and without your changes) to narrow down which change broke what test, before claiming the test was already failing before your changes.