mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-07-25 11:32:30 -04:00
52b6c66fa1
# 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>
142 lines
3.5 KiB
C++
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
|