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

142 lines
3.5 KiB
C++

#ifndef SPACETIMEDB_OUTCOME_H
#define SPACETIMEDB_OUTCOME_H
#include <optional>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
namespace SpacetimeDB {
// Internal distinct error type to avoid std::variant<T, std::string> when T == std::string.
struct OutcomeError {
std::string msg;
};
// Forward declaration
template <typename T>
class Outcome;
// ==================== Outcome<T> ====================
template <typename T>
class [[nodiscard]] Outcome {
private:
// index 0 = Ok(T), index 1 = Err(OutcomeError)
std::variant<T, OutcomeError> value_;
// Private constructors
explicit Outcome(T value)
: value_(std::in_place_index<0>, std::move(value)) {}
explicit Outcome(OutcomeError error)
: value_(std::in_place_index<1>, std::move(error)) {}
public:
using value_type = T;
// ---- Factories ----
static Outcome Ok(T value) {
return Outcome(std::move(value));
}
static Outcome Err(std::string error) {
return Outcome(OutcomeError{std::move(error)});
}
static Outcome Err(const char* error) {
return Outcome(OutcomeError{std::string(error)});
}
// ---- State ----
bool is_ok() const { return value_.index() == 0; }
bool is_err() const { return value_.index() == 1; }
// ---- Accessors ----
// Precondition: is_ok()
T& value() & { return std::get<0>(value_); }
const T& value() const & { return std::get<0>(value_); }
T&& value() && { return std::get<0>(std::move(value_)); }
// Precondition: is_err()
const std::string& error() const { return std::get<1>(value_).msg; }
// Optional convenience: like Rust's unwrap_or
template <typename U>
T value_or(U&& fallback) const & {
return is_ok() ? std::get<0>(value_) : T(std::forward<U>(fallback));
}
};
// ==================== Outcome<void> specialization ====================
template <>
class [[nodiscard]] Outcome<void> {
private:
std::optional<OutcomeError> error_;
explicit Outcome(std::nullopt_t) : error_(std::nullopt) {}
explicit Outcome(OutcomeError error) : error_(std::move(error)) {}
public:
using value_type = void;
// ---- Factories ----
static Outcome Ok() {
return Outcome(std::nullopt);
}
static Outcome Err(std::string error) {
return Outcome(OutcomeError{std::move(error)});
}
static Outcome Err(const char* error) {
return Outcome(OutcomeError{std::string(error)});
}
// ---- State ----
bool is_ok() const { return !error_.has_value(); }
bool is_err() const { return error_.has_value(); }
// Precondition: is_err()
const std::string& error() const { return error_->msg; }
};
// ==================== Free helper functions ====================
// Ok() for void
inline Outcome<void> Ok() {
return Outcome<void>::Ok();
}
// Err() for void
inline Outcome<void> Err(std::string msg) {
return Outcome<void>::Err(std::move(msg));
}
inline Outcome<void> Err(const char* msg) {
return Outcome<void>::Err(msg);
}
// Ok(value) for T
template <typename T>
inline Outcome<std::decay_t<T>> Ok(T&& value) {
return Outcome<std::decay_t<T>>::Ok(std::forward<T>(value));
}
// Err<T>(msg) for T
template <typename T>
inline Outcome<T> Err(std::string msg) {
return Outcome<T>::Err(std::move(msg));
}
template <typename T>
inline Outcome<T> Err(const char* msg) {
return Outcome<T>::Err(msg);
}
} // namespace SpacetimeDB
#endif // SPACETIMEDB_OUTCOME_H