mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-11 02:00:04 -04:00
14f79910ee
# Description of Changes
- Migrated the C++ module-definition assembly path to V10-first
internals:
- Added v10_builder and module_type_registration systems.
- Switched Module::__describe_module__ to serialize RawModuleDef with
V10 payload.
- Updated macro registration pipeline to register through V10
- Added explicit naming support across macro surface (*_NAMED variants
for reducer/procedure/
view and field/index macros).
- Reworked multi-column index macros (FIELD_MultiColumnIndex,
FIELD_MultiColumnIndex_NAMED) with
migration alias.
- Added SPACETIMEDB_SETTING_CASE_CONVERSION(...) to support case
conversion policy
- Error-path hardening by adding explicit constraint-registration error
tracking and preinit validation
- Codegen updates:
- Updated C++ moduledef regen to V10 builder types.
- Adjusted C++ codegen duplicate-variant wrapper generation to emit
proper product-type
wrappers.
- Test/harness updates:
- type-isolation-test runner now defaults to focused V10 regression
checks; --v9 runs broader
legacy/full suite.
- Added focused modules for positive/negative V10 checks:
- test_multicolumn_index_valid
- error_multicolumn_missing_field
- error_default_missing_field
- Re-enabled C++ paths in sdks/rust/tests/test.rs procedure/view/test
suites.
# API and ABI breaking changes
- Refactor of the underlying module definition
- New *_NAMED variant macros for explicit canonical naming
- FIELD_NamedMultiColumnIndex renamed to FIELD_MultiColumnIndex
# Expected complexity level and risk
3 - Large set of changes moving over to V10 with underlying changes to
make future updates a little easier
# Testing
- [x] Ran the type isolation test and expanded it
- [x] Ran the spacetimedb-sdk test framework to confirm no more drift
between C++ and other module languages
- [x] Ran Unreal test suite though not really applicable
- [x] New app creation with `spacetime init --template basic-cpp`
- [x] Ran describe module tests against Rust + C# matching with C++ on
the /modules/sdk-test* modules to find any possible mis-alignment
# Review
- [x] Another look at the new features with C++
- [x] Thoughts on *_NAMED macros, I couldn't come up with a better
solution with C++20
213 lines
7.3 KiB
C++
213 lines
7.3 KiB
C++
#ifndef SPACETIMEDB_MODULE_H
|
|
#define SPACETIMEDB_MODULE_H
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <optional>
|
|
#include "../bsatn/types.h"
|
|
#include "../reducer_macros.h"
|
|
#include "../reducer_context.h"
|
|
#include "../abi/opaque_types.h"
|
|
#include <spacetimedb/abi/FFI.h> // For StatusCode
|
|
#include "autogen/TableAccess.g.h" // For TableAccess enum
|
|
#include "autogen/Lifecycle.g.h" // For Lifecycle enum
|
|
#include "autogen/CaseConversionPolicy.g.h" // For CaseConversionPolicy
|
|
|
|
namespace SpacetimeDB {
|
|
namespace bsatn {
|
|
class Writer; // Forward declaration
|
|
}
|
|
|
|
namespace Internal {
|
|
|
|
// Forward declarations are handled by includes
|
|
|
|
// Module class - singleton pattern similar to C# static class
|
|
class Module {
|
|
private:
|
|
Module() = default;
|
|
Module(const Module&) = delete;
|
|
Module& operator=(const Module&) = delete;
|
|
|
|
public:
|
|
static Module& Instance() {
|
|
static Module instance;
|
|
return instance;
|
|
}
|
|
|
|
// Initialize module (called once)
|
|
|
|
// Module description for FFI (matching existing signature)
|
|
static void __describe_module__(BytesSink sink);
|
|
static std::vector<uint8_t> SerializeModuleDef();
|
|
|
|
// Reducer invocation for FFI (matching existing signature)
|
|
static Status __call_reducer__(
|
|
uint32_t id,
|
|
uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,
|
|
uint64_t conn_id_0, uint64_t conn_id_1,
|
|
Timestamp timestamp,
|
|
BytesSource args_source,
|
|
BytesSink error_sink
|
|
);
|
|
|
|
// View invocation for FFI (with sender)
|
|
static int16_t __call_view__(
|
|
uint32_t id,
|
|
uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,
|
|
BytesSource args_source,
|
|
BytesSink result_sink
|
|
);
|
|
|
|
// View invocation for FFI (anonymous - no sender)
|
|
static int16_t __call_view_anon__(
|
|
uint32_t id,
|
|
BytesSource args_source,
|
|
BytesSink result_sink
|
|
);
|
|
|
|
// Procedure invocation for FFI
|
|
static int16_t __call_procedure__(
|
|
uint32_t id,
|
|
uint64_t sender_0, uint64_t sender_1, uint64_t sender_2, uint64_t sender_3,
|
|
uint64_t conn_id_0, uint64_t conn_id_1,
|
|
uint64_t timestamp_microseconds,
|
|
BytesSource args_source,
|
|
BytesSink result_sink
|
|
);
|
|
|
|
// Internal registration methods (inline to avoid linking issues)
|
|
template<typename T>
|
|
static void RegisterTableInternal(const char* name, bool is_public, bool is_event = false) {
|
|
// Forward declaration - implementation will be included at end of file
|
|
RegisterTableInternalImpl<T>(name, is_public, is_event);
|
|
}
|
|
|
|
template<typename Func>
|
|
static void RegisterReducerInternal(const std::string& name, Func func) {
|
|
// Forward declaration - implementation will be included at end of file
|
|
RegisterReducerInternalImpl<Func>(name, func);
|
|
}
|
|
|
|
// Implementation methods (will be defined after including Module_impl.h)
|
|
template<typename T>
|
|
static void RegisterTableInternalImpl(const char* name, bool is_public, bool is_event = false);
|
|
|
|
public:
|
|
// These need to be public for macro access
|
|
template<typename Func>
|
|
static void RegisterReducerInternalImpl(const std::string& name, Func func);
|
|
|
|
template<typename Func>
|
|
static void RegisterReducerInternalWithNames(const std::string& name, Func func, const std::vector<std::string>& param_names);
|
|
|
|
private:
|
|
|
|
public:
|
|
// Registration support routed through the V10 module-definition builder.
|
|
static void RegisterClientVisibilityFilter(const char* sql);
|
|
static void SetCaseConversionPolicy(CaseConversionPolicy policy);
|
|
static void RegisterExplicitTableName(const std::string& source_name, const std::string& canonical_name);
|
|
static void RegisterExplicitFunctionName(const std::string& source_name, const std::string& canonical_name);
|
|
static void RegisterExplicitIndexName(const std::string& source_name, const std::string& canonical_name);
|
|
};
|
|
|
|
// Helper functions for module description
|
|
std::vector<uint8_t> ConsumeBytes(BytesSource source);
|
|
void WriteBytes(BytesSink sink, const std::vector<uint8_t>& bytes);
|
|
|
|
void SetTableIsEventFlag(const std::string& table_name, bool is_event);
|
|
bool GetTableIsEventFlag(const std::string& table_name);
|
|
|
|
} // namespace Internal
|
|
|
|
// Public alias to mirror C# API shape (`SpacetimeDB.CaseConversionPolicy`).
|
|
using CaseConversionPolicy = Internal::CaseConversionPolicy;
|
|
|
|
// Public API similar to C# Module class
|
|
class Module {
|
|
public:
|
|
// Table registration
|
|
template<typename T>
|
|
static void RegisterTable(const char* name, bool is_public = true, bool is_event = false) {
|
|
Internal::Module::RegisterTableInternal<T>(name, is_public, is_event);
|
|
}
|
|
|
|
// Reducer registration
|
|
template<typename Func>
|
|
static void RegisterReducer(const char* name, Func func) {
|
|
Internal::Module::RegisterReducerInternal(name, func);
|
|
}
|
|
|
|
// Client visibility filter (similar to C# / Rust)
|
|
static void RegisterClientVisibilityFilter(const char* sql) {
|
|
Internal::Module::RegisterClientVisibilityFilter(sql);
|
|
}
|
|
|
|
// Module metadata (future extension)
|
|
static void SetMetadata([[maybe_unused]] const char* name, [[maybe_unused]] const char* version) {
|
|
// TODO: Implement module metadata
|
|
}
|
|
|
|
static void SetCaseConversionPolicy(CaseConversionPolicy policy) {
|
|
Internal::Module::SetCaseConversionPolicy(policy);
|
|
}
|
|
|
|
static void RegisterExplicitTableName(const char* source_name, const char* canonical_name) {
|
|
Internal::Module::RegisterExplicitTableName(source_name, canonical_name);
|
|
}
|
|
|
|
static void RegisterExplicitFunctionName(const char* source_name, const char* canonical_name) {
|
|
Internal::Module::RegisterExplicitFunctionName(source_name, canonical_name);
|
|
}
|
|
|
|
static void RegisterExplicitIndexName(const char* source_name, const char* canonical_name) {
|
|
Internal::Module::RegisterExplicitIndexName(source_name, canonical_name);
|
|
}
|
|
};
|
|
|
|
// Global registration functions for X-Macro support
|
|
template<typename T>
|
|
void register_table_impl(const char* name, bool is_public) {
|
|
Internal::Module::RegisterTableInternal<T>(name, is_public);
|
|
}
|
|
|
|
template<typename Func>
|
|
void register_reducer_impl(const std::string& name, Func func) {
|
|
Internal::Module::RegisterReducerInternal(name, func);
|
|
}
|
|
|
|
// Initialize module - no-op; preinit functions handle registration.
|
|
inline void initialize_module() {
|
|
// No-op.
|
|
}
|
|
|
|
// Write module definition (for FFI)
|
|
inline void spacetimedb_write_module_def(uint32_t sink) {
|
|
BytesSink bs{sink};
|
|
Internal::Module::__describe_module__(bs);
|
|
}
|
|
|
|
// Call reducer (for FFI)
|
|
inline int16_t spacetimedb_call_reducer(uint32_t id, uint32_t args,
|
|
uint64_t sender_0, uint64_t sender_1,
|
|
uint64_t sender_2, uint64_t sender_3) {
|
|
// Create a simple timestamp
|
|
Timestamp ts(0);
|
|
BytesSource args_source{args};
|
|
BytesSink error_sink{0}; // Null sink for now
|
|
|
|
auto status = Internal::Module::__call_reducer__(
|
|
id, sender_0, sender_1, sender_2, sender_3,
|
|
0, 0, ts, args_source, error_sink);
|
|
|
|
return is_ok(status) ? 0 : -1;
|
|
}
|
|
|
|
} // namespace SpacetimeDB
|
|
|
|
// Include the template implementations
|
|
#include "Module_impl.h"
|
|
|
|
#endif // SPACETIMEDB_MODULE_H
|