Files
Jason Larabie 52b6c66fa1 Add C++ Bindings (#3544)
# Description of Changes

This adds C++ server bindings (/crate/bindings-cpp) to allow writing C++
20 modules.

- Emscripten WASM build system integration with CMake
- Macro-based code generation (SPACETIMEDB_TABLE, SPACETIMEDB_REDUCER,
etc)
- All SpacetimeDB types supported (primitives, Timestamp, Identity,
Uuid, etc)
- Product types via SPACETIMEDB_STRUCT
- Sum types via SPACETIMEDB_ENUM
- Constraints marked with FIELD* macros

# API and ABI breaking changes

None

# Expected complexity level and risk

2 - Doesn't heavily impact any other areas but is complex macro C++
structure to support a similar developer experience, did have a small
impact on init command

# Testing

- [x] modules/module-test-cpp - heavily tested every reducer
- [x] modules/benchmarks-cpp - tested through the standalone (~6x faster
than C#, ~6x slower than Rust)
- [x] modules/sdk-test-cpp
- [x] modules/sdk-test-procedure-cpp
- [x] modules/sdk-test-view-cpp  
- [x] Wrote several test modules myself
- [x] Quickstart smoketest [Currently in progress]
- [ ] Write Blackholio C++ server module

---------

Signed-off-by: Jason Larabie <jason@clockworklabs.io>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: Ryan <r.ekhoff@clockworklabs.io>
Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com>
2026-02-07 04:26:45 +00:00

164 lines
4.6 KiB
C++

#ifndef SPACETIMEDB_REDUCER_ERROR_H
#define SPACETIMEDB_REDUCER_ERROR_H
#include "spacetimedb/outcome.h"
#include <string>
#include <optional>
/**
* @file reducer_error.h
* @brief Graceful error handling for reducers using Outcome<T> pattern
*
* This module provides reducer-specific error handling utilities using the Outcome type.
* For the general Outcome<T> type definition, see outcome.h.
*
* Modern usage (recommended):
* @code
* SPACETIMEDB_REDUCER(my_reducer, ReducerContext ctx, uint32_t id) {
* if (id == 0) {
* return Err("ID must be non-zero");
* }
* // ... rest of logic
* return Ok();
* }
* @endcode
*
* Legacy usage (still supported):
* @code
* SPACETIMEDB_REDUCER(my_reducer, ReducerContext ctx, uint32_t id) {
* if (id == 0) {
* fail_reducer("ID must be non-zero");
* return Ok(); // Or just return;
* }
* // ... rest of logic
* return Ok();
* }
* @endcode
*
* When a reducer returns Err():
* - The transaction is rolled back (not committed to the log)
* - The error message is captured and returned to the caller
* - No database changes are persisted
* - No WASM crash or panic occurs
*
* @ingroup sdk_runtime
*/
namespace SpacetimeDB {
/**
* @brief Type alias for reducer return type, matching Rust's ReducerResult
*
* Reducers use Outcome<void> for their return type. The Ok() and Err() helper
* functions from outcome.h can be used directly:
*
* @code
* SPACETIMEDB_REDUCER(my_reducer, ReducerContext ctx, uint32_t id) {
* if (id == 0) {
* return Err("ID must be non-zero");
* }
* // ... rest of logic
* return Ok();
* }
* @endcode
*/
using ReducerResult = Outcome<void>;
namespace Internal {
/**
* Thread-local error state for the current reducer invocation.
* This is cleared at the start of each reducer call and checked at the end.
*/
extern thread_local std::optional<std::string> g_reducer_error_message;
/**
* Clear the error state. Called automatically at the start of each reducer.
* @internal
*/
inline void clear_reducer_error() {
g_reducer_error_message = std::nullopt;
}
/**
* Check if the current reducer has failed.
* @internal
*/
inline bool has_reducer_error() {
return g_reducer_error_message.has_value();
}
/**
* Get the error message if one exists.
* @internal
*/
inline std::string get_reducer_error() {
return g_reducer_error_message.value_or(std::string());
}
}
/**
* @brief Fail the current reducer with an error message.
*
* This function marks the current reducer invocation as failed.
* The transaction will be rolled back and the error message will be
* returned to the caller. Failed transactions are NOT committed to the
* log and will not appear in temporal queries or transaction history.
*
* After calling this function, the reducer should return immediately
* to avoid executing additional logic on inconsistent state.
*
* @param message A descriptive error message explaining why the reducer failed
*
* @code
* if (amount <= 0) {
* SpacetimeDB::fail_reducer("Amount must be positive");
* return Ok(); // Or just return;
* }
*
* // Alternative using Outcome pattern:
* if (amount <= 0) {
* return Err("Amount must be positive");
* }
* @endcode
*
* @note This is thread-safe and works in WASM single-threaded environments.
* @note This does NOT throw exceptions or cause panics.
*/
inline void fail_reducer(std::string message) {
Internal::g_reducer_error_message = std::move(message);
}
/**
* @brief Fail the current reducer with a formatted error message.
*
* Convenience function for creating formatted error messages.
*
* @tparam Args Variadic template arguments for formatting
* @param format Printf-style format string
* @param args Arguments to format into the message
*
* @code
* fail_reducer_fmt("Tracker %u not found", tracker_id);
* fail_reducer_fmt("Invalid coordinates (%.2f, %.2f)", lat, lon);
* @endcode
*/
template<typename... Args>
inline void fail_reducer_fmt(const char* format, Args... args) {
// Get required size
int size = std::snprintf(nullptr, 0, format, args...);
if (size <= 0) {
fail_reducer("Error formatting failure message");
return;
}
// Format string
std::string result(size + 1, '\0');
std::snprintf(result.data(), result.size(), format, args...);
result.resize(size); // Remove null terminator
fail_reducer(std::move(result));
}
} // namespace SpacetimeDB
#endif // SPACETIMEDB_REDUCER_ERROR_H