From d464f55ebe482d71fd164b9b0e6e9820eb2d198c Mon Sep 17 00:00:00 2001 From: Jason Larabie Date: Thu, 30 Oct 2025 10:41:50 -0700 Subject: [PATCH 01/30] Initial commit for bindings-cpp, module-test-cpp, sdk-test-cpp, and benchmarks-cpp --- .gitignore | 4 + crates/bindings-cpp/.gitignore | 1 + crates/bindings-cpp/ARCHITECTURE.md | 483 +++++ crates/bindings-cpp/CMakeLists.txt | 82 + crates/bindings-cpp/DEVELOP.md | 597 ++++++ crates/bindings-cpp/QUICKSTART.md | 293 +++ crates/bindings-cpp/README.md | 244 +++ crates/bindings-cpp/REFERENCE.md | 1167 +++++++++++ crates/bindings-cpp/include/spacetimedb.h | 203 ++ .../include/spacetimedb/abi/FFI.h | 125 ++ .../include/spacetimedb/abi/abi.h | 173 ++ .../include/spacetimedb/abi/opaque_types.h | 195 ++ .../include/spacetimedb/bsatn/DEVELOP.md | 7 + .../include/spacetimedb/bsatn/README.md | 58 + .../spacetimedb/bsatn/algebraic_type.h | 508 +++++ .../spacetimedb/bsatn/algebraic_type.h.backup | 657 +++++++ .../include/spacetimedb/bsatn/bsatn.h | 64 + .../spacetimedb/bsatn/monostate_traits.h | 46 + .../spacetimedb/bsatn/primitive_traits.h | 275 +++ .../include/spacetimedb/bsatn/reader.h | 268 +++ .../include/spacetimedb/bsatn/schedule_at.h | 166 ++ .../spacetimedb/bsatn/schedule_at_impl.h | 135 ++ .../include/spacetimedb/bsatn/serialization.h | 148 ++ .../spacetimedb/bsatn/size_calculator.h | 255 +++ .../include/spacetimedb/bsatn/sum_type.h | 181 ++ .../include/spacetimedb/bsatn/time_duration.h | 134 ++ .../include/spacetimedb/bsatn/timestamp.h | 178 ++ .../include/spacetimedb/bsatn/traits.h | 300 +++ .../spacetimedb/bsatn/type_extensions.h | 503 +++++ .../include/spacetimedb/bsatn/types.h | 894 +++++++++ .../include/spacetimedb/bsatn/types_impl.h | 112 ++ .../include/spacetimedb/bsatn/writer.h | 254 +++ .../spacetimedb/client_visibility_filter.h | 41 + .../include/spacetimedb/database.h | 263 +++ .../include/spacetimedb/enum_macro.h | 305 +++ .../include/spacetimedb/error_handling.h | 260 +++ .../spacetimedb/filterable_value_concept.h | 73 + .../include/spacetimedb/index_iterator.h | 284 +++ .../include/spacetimedb/internal/Module.h | 322 +++ .../spacetimedb/internal/Module_impl.h | 731 +++++++ .../include/spacetimedb/internal/README.md | 101 + .../internal/autogen/AlgebraicType.g.h | 96 + .../internal/autogen/Lifecycle.g.h | 23 + .../internal/autogen/ProductType.g.h | 27 + .../internal/autogen/ProductTypeElement.g.h | 29 + .../internal/autogen/RawConstraintDataV9.g.h | 20 + .../internal/autogen/RawConstraintDefV9.g.h | 29 + .../internal/autogen/RawIndexAlgorithm.g.h | 51 + .../internal/autogen/RawIndexDefV9.g.h | 31 + .../autogen/RawMiscModuleExportV9.g.h | 20 + .../internal/autogen/RawModuleDefV9.g.h | 42 + .../internal/autogen/RawReducerDefV9.g.h | 32 + .../autogen/RawRowLevelSecurityDefV9.g.h | 26 + .../internal/autogen/RawScheduleDefV9.g.h | 30 + .../internal/autogen/RawScopedTypeNameV9.g.h | 28 + .../internal/autogen/RawSequenceDefV9.g.h | 36 + .../internal/autogen/RawTableDefV9.g.h | 48 + .../internal/autogen/RawTypeDefV9.g.h | 31 + .../autogen/RawUniqueConstraintDataV9.g.h | 26 + .../spacetimedb/internal/autogen/SumType.g.h | 27 + .../internal/autogen/SumTypeVariant.g.h | 29 + .../internal/autogen/TableAccess.g.h | 22 + .../internal/autogen/TableType.g.h | 22 + .../internal/autogen/Typespace.g.h | 27 + .../spacetimedb/internal/autogen_base.h | 124 ++ .../spacetimedb/internal/bsatn_adapters.h | 126 ++ .../include/spacetimedb/internal/debug.h | 91 + .../spacetimedb/internal/field_registration.h | 400 ++++ .../internal/forward_declarations.h | 10 + .../internal/nested_type_collection.h | 685 +++++++ .../include/spacetimedb/internal/v9_builder.h | 679 +++++++ .../internal/v9_type_registration.h | 403 ++++ .../bindings-cpp/include/spacetimedb/logger.h | 294 +++ .../bindings-cpp/include/spacetimedb/macros.h | 613 ++++++ .../bindings-cpp/include/spacetimedb/random.h | 142 ++ .../include/spacetimedb/range_queries.h | 212 ++ .../include/spacetimedb/reducer_context.h | 67 + .../include/spacetimedb/reducer_error.h | 128 ++ .../include/spacetimedb/reducer_macros.h | 204 ++ crates/bindings-cpp/include/spacetimedb/rls.h | 196 ++ .../include/spacetimedb/schedule_reducer.h | 92 + .../bindings-cpp/include/spacetimedb/table.h | 568 ++++++ .../spacetimedb/table_with_constraints.h | 696 +++++++ .../bindings-cpp/src/abi/module_exports.cpp | 55 + crates/bindings-cpp/src/abi/wasi_shims.cpp | 158 ++ .../src/internal/AlgebraicType.cpp | 171 ++ crates/bindings-cpp/src/internal/Module.cpp | 370 ++++ .../bindings-cpp/src/internal/v9_builder.cpp | 356 ++++ .../src/internal/v9_type_registration.cpp | 748 +++++++ .../tests/client-comparison/README.md | 86 + .../tests/client-comparison/check_tables.py | 58 + .../run_client_comparison.sh | 181 ++ .../scripts/compare_clients.sh | 110 ++ .../scripts/compare_modules.sh | 475 +++++ .../scripts/regenerate_cpp_client.sh | 134 ++ .../scripts/regenerate_rust_client.sh | 93 + .../tests/type-isolation-test/.gitignore | 11 + .../type-isolation-test/CMakeLists.module.txt | 64 + .../tests/type-isolation-test/CMakeLists.txt | 15 + .../type-isolation-test/CMakeLists_module.txt | 64 + .../tests/type-isolation-test/README.md | 148 ++ .../run_type_isolation_test.sh | 343 ++++ .../test_modules/debug_constraint_simple.cpp | 22 + .../test_modules/debug_large_struct.cpp | 46 + .../test_modules/debug_minimal_fail.cpp | 14 + .../debug_optional_large_struct.cpp | 45 + .../test_modules/debug_simple_enum.cpp | 34 + .../test_modules/debug_special.cpp | 13 + .../debug_special_constraints.cpp | 29 + .../test_modules/debug_special_reducers.cpp | 60 + .../test_modules/debug_trace.cpp | 14 + .../test_modules/debug_vector_only.cpp | 23 + .../test_modules/debug_vector_only_simple.cpp | 19 + .../error_autoinc_non_integer.cpp | 144 ++ .../test_modules/error_circular_ref.cpp | 26 + .../test_modules/error_invalid_index.cpp | 184 ++ .../test_modules/error_multiple_pk.cpp | 98 + .../error_non_spacetimedb_type.cpp | 78 + .../test_modules/error_scheduled_id_pk.cpp | 100 + .../test_modules/module01_basic_unsigned.cpp | 90 + .../test_modules/module02_large_unsigned.cpp | 90 + .../test_modules/module03_basic_signed.cpp | 90 + .../test_modules/module04_large_signed.cpp | 90 + .../test_modules/module05_float_bool.cpp | 66 + .../test_modules/module06_string.cpp | 35 + .../test_modules/module07_special_types.cpp | 113 ++ .../test_modules/module08_enums.cpp | 84 + .../test_modules/module09_structs.cpp | 189 ++ .../test_modules/module10_vectors.cpp | 54 + .../test_modules/module11_optional.cpp | 114 ++ .../test_modules/module12_constraints.cpp | 58 + .../type-isolation-test/test_modules/test.o | Bin 0 -> 4485212 bytes .../test_complex_reducer_only.cpp | 56 + .../test_modules/test_complex_table_only.cpp | 67 + .../test_modules/test_connectionid_only.cpp | 13 + .../test_modules/test_debug_optional.cpp | 43 + .../test_modules/test_enum_table_only.cpp | 74 + .../test_enum_vector_payloads.cpp | 39 + .../test_modules/test_identity_only.cpp | 13 + .../test_modules/test_massive_reducer.cpp | 78 + .../test_modules/test_minimal_special.cpp | 16 + .../test_modules/test_mixed_types.cpp | 21 + .../test_modules/test_nested_optionals.cpp | 39 + .../test_modules/test_optional_debug.cpp | 19 + .../test_optional_reducer_only.cpp | 41 + .../test_modules/test_optional_simple.cpp | 28 + .../test_modules/test_optional_table_only.cpp | 29 + .../test_modules/test_simple_table.cpp | 16 + .../test_single_special_vector.cpp | 12 + .../test_modules/test_special_minimal.cpp | 20 + .../test_modules/test_special_vectors.cpp | 20 + .../test_modules/test_timeduration_only.cpp | 13 + .../test_modules/test_timestamp_only.cpp | 13 + .../test_modules/test_u128_only.cpp | 13 + .../test_modules/test_unified_enum.cpp | 23 + .../test_modules/test_unit_isolation.cpp | 67 + .../test_modules/test_unit_progression.cpp | 83 + .../test_modules/test_unit_simple.cpp | 23 + .../test_modules/test_unit_struct.cpp | 113 ++ .../test_modules/test_wrapped_special.cpp | 20 + .../update_table_from_log.sh | 296 +++ crates/cli/src/subcommands/init.rs | 123 ++ .../subcommands/project/cpp/CMakeLists._txt | 62 + .../src/subcommands/project/cpp/_gitignore | 21 + .../cli/src/subcommands/project/cpp/lib._cpp | 44 + crates/cli/src/tasks/cpp.rs | 100 + crates/cli/src/tasks/mod.rs | 3 + crates/cli/src/util.rs | 6 +- .../codegen/examples/regen-cpp-moduledef.rs | 52 + crates/codegen/src/cpp.rs | 525 +++++ crates/codegen/src/lib.rs | 1 + crates/testing/src/modules.rs | 14 + .../tests/standalone_integration_test.rs | 18 +- modules/benchmarks-cpp/CMakeLists.txt | 63 + modules/benchmarks-cpp/build.bat | 2 + modules/benchmarks-cpp/src/circles.cpp | 180 ++ modules/benchmarks-cpp/src/common.h | 35 + modules/benchmarks-cpp/src/ia_loop.cpp | 419 ++++ modules/benchmarks-cpp/src/lib.cpp | 14 + modules/benchmarks-cpp/src/synthetic.cpp | 437 +++++ modules/module-test-cpp/CMakeLists.txt | 60 + .../module-test-cpp/compare_module_schemas.py | 208 ++ modules/module-test-cpp/src/lib.cpp | 510 +++++ modules/sdk-test-cpp/.gitignore | 31 + modules/sdk-test-cpp/CMakeLists.txt | 65 + modules/sdk-test-cpp/README.md | 50 + modules/sdk-test-cpp/src/lib.cpp | 1727 +++++++++++++++++ 187 files changed, 29310 insertions(+), 2 deletions(-) create mode 100644 crates/bindings-cpp/.gitignore create mode 100644 crates/bindings-cpp/ARCHITECTURE.md create mode 100644 crates/bindings-cpp/CMakeLists.txt create mode 100644 crates/bindings-cpp/DEVELOP.md create mode 100644 crates/bindings-cpp/QUICKSTART.md create mode 100644 crates/bindings-cpp/README.md create mode 100644 crates/bindings-cpp/REFERENCE.md create mode 100644 crates/bindings-cpp/include/spacetimedb.h create mode 100644 crates/bindings-cpp/include/spacetimedb/abi/FFI.h create mode 100644 crates/bindings-cpp/include/spacetimedb/abi/abi.h create mode 100644 crates/bindings-cpp/include/spacetimedb/abi/opaque_types.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/DEVELOP.md create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/README.md create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/algebraic_type.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/algebraic_type.h.backup create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/bsatn.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/monostate_traits.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/primitive_traits.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/reader.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/schedule_at.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/schedule_at_impl.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/serialization.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/size_calculator.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/sum_type.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/time_duration.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/timestamp.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/traits.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/type_extensions.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/types.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/types_impl.h create mode 100644 crates/bindings-cpp/include/spacetimedb/bsatn/writer.h create mode 100644 crates/bindings-cpp/include/spacetimedb/client_visibility_filter.h create mode 100644 crates/bindings-cpp/include/spacetimedb/database.h create mode 100644 crates/bindings-cpp/include/spacetimedb/enum_macro.h create mode 100644 crates/bindings-cpp/include/spacetimedb/error_handling.h create mode 100644 crates/bindings-cpp/include/spacetimedb/filterable_value_concept.h create mode 100644 crates/bindings-cpp/include/spacetimedb/index_iterator.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/Module.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/Module_impl.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/README.md create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/AlgebraicType.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/Lifecycle.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/ProductType.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/ProductTypeElement.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawConstraintDataV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawConstraintDefV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawIndexAlgorithm.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawIndexDefV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawMiscModuleExportV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawModuleDefV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawReducerDefV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawRowLevelSecurityDefV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawScheduleDefV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawScopedTypeNameV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawSequenceDefV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawTableDefV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawTypeDefV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/RawUniqueConstraintDataV9.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/SumType.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/SumTypeVariant.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/TableAccess.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/TableType.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen/Typespace.g.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/autogen_base.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/bsatn_adapters.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/debug.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/field_registration.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/forward_declarations.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/nested_type_collection.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/v9_builder.h create mode 100644 crates/bindings-cpp/include/spacetimedb/internal/v9_type_registration.h create mode 100644 crates/bindings-cpp/include/spacetimedb/logger.h create mode 100644 crates/bindings-cpp/include/spacetimedb/macros.h create mode 100644 crates/bindings-cpp/include/spacetimedb/random.h create mode 100644 crates/bindings-cpp/include/spacetimedb/range_queries.h create mode 100644 crates/bindings-cpp/include/spacetimedb/reducer_context.h create mode 100644 crates/bindings-cpp/include/spacetimedb/reducer_error.h create mode 100644 crates/bindings-cpp/include/spacetimedb/reducer_macros.h create mode 100644 crates/bindings-cpp/include/spacetimedb/rls.h create mode 100644 crates/bindings-cpp/include/spacetimedb/schedule_reducer.h create mode 100644 crates/bindings-cpp/include/spacetimedb/table.h create mode 100644 crates/bindings-cpp/include/spacetimedb/table_with_constraints.h create mode 100644 crates/bindings-cpp/src/abi/module_exports.cpp create mode 100644 crates/bindings-cpp/src/abi/wasi_shims.cpp create mode 100644 crates/bindings-cpp/src/internal/AlgebraicType.cpp create mode 100644 crates/bindings-cpp/src/internal/Module.cpp create mode 100644 crates/bindings-cpp/src/internal/v9_builder.cpp create mode 100644 crates/bindings-cpp/src/internal/v9_type_registration.cpp create mode 100644 crates/bindings-cpp/tests/client-comparison/README.md create mode 100644 crates/bindings-cpp/tests/client-comparison/check_tables.py create mode 100644 crates/bindings-cpp/tests/client-comparison/run_client_comparison.sh create mode 100644 crates/bindings-cpp/tests/client-comparison/scripts/compare_clients.sh create mode 100644 crates/bindings-cpp/tests/client-comparison/scripts/compare_modules.sh create mode 100644 crates/bindings-cpp/tests/client-comparison/scripts/regenerate_cpp_client.sh create mode 100644 crates/bindings-cpp/tests/client-comparison/scripts/regenerate_rust_client.sh create mode 100644 crates/bindings-cpp/tests/type-isolation-test/.gitignore create mode 100644 crates/bindings-cpp/tests/type-isolation-test/CMakeLists.module.txt create mode 100644 crates/bindings-cpp/tests/type-isolation-test/CMakeLists.txt create mode 100644 crates/bindings-cpp/tests/type-isolation-test/CMakeLists_module.txt create mode 100644 crates/bindings-cpp/tests/type-isolation-test/README.md create mode 100644 crates/bindings-cpp/tests/type-isolation-test/run_type_isolation_test.sh create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_constraint_simple.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_large_struct.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_minimal_fail.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_optional_large_struct.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_simple_enum.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_special.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_special_constraints.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_special_reducers.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_trace.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_vector_only.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/debug_vector_only_simple.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/error_autoinc_non_integer.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/error_circular_ref.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/error_invalid_index.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/error_multiple_pk.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/error_non_spacetimedb_type.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/error_scheduled_id_pk.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module01_basic_unsigned.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module02_large_unsigned.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module03_basic_signed.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module04_large_signed.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module05_float_bool.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module06_string.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module07_special_types.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module08_enums.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module09_structs.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module10_vectors.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module11_optional.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/module12_constraints.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test.o create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_complex_reducer_only.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_complex_table_only.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_connectionid_only.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_debug_optional.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_enum_table_only.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_enum_vector_payloads.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_identity_only.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_massive_reducer.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_minimal_special.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_mixed_types.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_nested_optionals.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_optional_debug.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_optional_reducer_only.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_optional_simple.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_optional_table_only.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_simple_table.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_single_special_vector.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_special_minimal.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_special_vectors.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_timeduration_only.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_timestamp_only.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_u128_only.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_unified_enum.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_unit_isolation.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_unit_progression.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_unit_simple.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_unit_struct.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/test_modules/test_wrapped_special.cpp create mode 100644 crates/bindings-cpp/tests/type-isolation-test/update_table_from_log.sh create mode 100644 crates/cli/src/subcommands/project/cpp/CMakeLists._txt create mode 100644 crates/cli/src/subcommands/project/cpp/_gitignore create mode 100644 crates/cli/src/subcommands/project/cpp/lib._cpp create mode 100644 crates/cli/src/tasks/cpp.rs create mode 100644 crates/codegen/examples/regen-cpp-moduledef.rs create mode 100644 crates/codegen/src/cpp.rs create mode 100644 modules/benchmarks-cpp/CMakeLists.txt create mode 100644 modules/benchmarks-cpp/build.bat create mode 100644 modules/benchmarks-cpp/src/circles.cpp create mode 100644 modules/benchmarks-cpp/src/common.h create mode 100644 modules/benchmarks-cpp/src/ia_loop.cpp create mode 100644 modules/benchmarks-cpp/src/lib.cpp create mode 100644 modules/benchmarks-cpp/src/synthetic.cpp create mode 100644 modules/module-test-cpp/CMakeLists.txt create mode 100644 modules/module-test-cpp/compare_module_schemas.py create mode 100644 modules/module-test-cpp/src/lib.cpp create mode 100644 modules/sdk-test-cpp/.gitignore create mode 100644 modules/sdk-test-cpp/CMakeLists.txt create mode 100644 modules/sdk-test-cpp/README.md create mode 100644 modules/sdk-test-cpp/src/lib.cpp diff --git a/.gitignore b/.gitignore index daf8d98fc22..ef3bbe90300 100644 --- a/.gitignore +++ b/.gitignore @@ -224,6 +224,10 @@ new.json # Temporary templates dir for CLI's init command /crates/cli/.templates +# C++ build data +modules/benchmarks-cpp/build/ +modules/module-test-cpp/build/ + # Symlinked output from `nix build` result diff --git a/crates/bindings-cpp/.gitignore b/crates/bindings-cpp/.gitignore new file mode 100644 index 00000000000..a5309e6b906 --- /dev/null +++ b/crates/bindings-cpp/.gitignore @@ -0,0 +1 @@ +build*/ diff --git a/crates/bindings-cpp/ARCHITECTURE.md b/crates/bindings-cpp/ARCHITECTURE.md new file mode 100644 index 00000000000..bbc84d0b7c3 --- /dev/null +++ b/crates/bindings-cpp/ARCHITECTURE.md @@ -0,0 +1,483 @@ +# SpacetimeDB C++ SDK Architecture + +## Overview + +The SpacetimeDB C++ SDK provides a sophisticated compile-time/runtime hybrid system for building database modules in C++ that compile to WebAssembly (WASM) and run inside the SpacetimeDB database. This document describes the architectural components, type registration flow, and key differences from other language SDKs. + +## Core Architecture Principles + +### 1. Hybrid Compile-Time/Runtime System +- **Compile-time validation**: C++20 concepts and static assertions catch constraint violations before compilation +- **Runtime registration**: __preinit__ functions execute during WASM module load to register types and metadata +- **Nominal type system**: Types identified by their declared names, not structural analysis +- **Error detection**: Multi-layer validation system from compile-time through module publishing + +### 2. Priority-Ordered Initialization System +The SDK uses a numbered __preinit__ function system to ensure correct initialization order: + +``` +__preinit__01_ - Clear global state (first) +__preinit__10_ - Field registration +__preinit__19_ - Auto-increment integration registration +__preinit__20_ - Table and lifecycle reducer registration +__preinit__21_ - Field constraints +__preinit__25_ - Row level security filters +__preinit__30_ - User reducers +__preinit__99_ - Type validation and error detection (last) +``` + +## Detailed Type Registration Flow + +### Phase 1: Compile-Time Validation + +**Location**: Template instantiation during compilation + +**Components**: +- **C++20 Concepts** (`filterable_value_concept.h`, `table_with_constraints.h`): + ```cpp + template + concept FilterableValue = + std::integral || + std::same_as || + std::same_as || + // ... other filterable types + + template + concept AutoIncrementable = + std::same_as || + std::same_as || + // ... integer types only + ``` + +- **Static Assertions** in FIELD_ macros: + ```cpp + #define FIELD_Unique(table_name, field_name) \ + static_assert([]() constexpr { \ + using FieldType = decltype(std::declval().field_name); \ + static_assert(FilterableValue, \ + "Field cannot have Unique constraint - type is not filterable."); \ + return true; \ + }(), "Constraint validation for " #table_name "." #field_name); + ``` + +**Validation Coverage**: +- ✅ AutoIncrement constraints (only integer types) +- ✅ Index/Unique/PrimaryKey constraints (only filterable types) +- ✅ Type compatibility with BSATN serialization +- ✅ Template parameter validation + +**Error Output**: Clear compile-time error messages with specific guidance + +### Phase 2: Runtime Registration (__preinit__ functions) + +**Location**: WASM module load, before any user code executes + +#### 2.1 Global State Initialization (__preinit__01_) +```cpp +extern "C" __attribute__((export_name("__preinit__01_clear_global_state"))) +void __preinit__01_clear_global_state() { + ClearV9Module(); // Reset module definition + getV9TypeRegistration().clear(); // Reset type registry +} +``` + +#### 2.2 Component Registration (__preinit__10-30_) +Generated by macros during preprocessing: + +**Table Registration** (__preinit__20_): +```cpp +SPACETIMEDB_TABLE(User, users, Public) +// Generates: +extern "C" __attribute__((export_name("__preinit__20_register_table_User_line_42"))) +void __preinit__20_register_table_User_line_42() { + SpacetimeDb::Module::RegisterTable("users", true); +} +``` + +**Field Constraints** (__preinit__21_): +```cpp +FIELD_PrimaryKey(users, id); +// Generates: +extern "C" __attribute__((export_name("__preinit__21_field_constraint_users_id_line_43"))) +void __preinit__21_field_constraint_users_id_line_43() { + getV9Builder().AddFieldConstraint("users", "id", FieldConstraint::PrimaryKey); +} +``` + +**Auto-Increment Integration Registration** (__preinit__19_): +Auto-increment fields require special handling during `insert()` operations. When SpacetimeDB processes an auto-increment insert, it returns only the generated column values (not the full row) in BSATN format. The C++ SDK uses a registry-based integration system to properly handle these generated values and update the user's row object. + +```cpp +FIELD_PrimaryKeyAutoInc(users, id); +// Generates both constraint registration AND auto-increment integration: + +// 1. Auto-increment integration function (unique per field via __LINE__) +namespace SpacetimeDb { namespace detail { + static void autoinc_integrate_47(User& row, SpacetimeDb::bsatn::Reader& reader) { + using FieldType = decltype(std::declval().id); + FieldType generated_value = SpacetimeDb::bsatn::deserialize(reader); + row.id = generated_value; // Update field with generated ID + } +}} + +// 2. Registration function to register the integrator +extern "C" __attribute__((export_name("__preinit__19_autoinc_register_47"))) +void __preinit__19_autoinc_register_47() { + SpacetimeDb::detail::get_autoinc_integrator() = + &SpacetimeDb::detail::autoinc_integrate_47; +} +``` + +**Runtime Integration Process**: +When `insert()` is called on a table with auto-increment fields: +1. SDK serializes and sends the row to SpacetimeDB +2. SpacetimeDB processes the insert and generates the auto-increment value(s) +3. SpacetimeDB returns a buffer containing only the generated column values in BSATN format +4. SDK calls the registered integrator function to update the original row with generated values +5. `insert()` returns the updated row with the correct generated ID + +This system enables users to immediately access generated IDs: +```cpp +SPACETIMEDB_REDUCER(create_user, ReducerContext ctx, std::string name) { + User user{0, name, true}; // id=0 will be auto-generated + User inserted_user = ctx.db[users].insert(user); // Returns user with generated ID + LOG_INFO("Created user with ID: " + std::to_string(inserted_user.id)); +} +``` + +**Reducer Registration** (__preinit__30_): +```cpp +SPACETIMEDB_REDUCER(add_user, ReducerContext ctx, std::string name) +// Generates registration function that captures parameter types and creates dispatch handler +``` + +#### 2.3 Multiple Primary Key Detection +During constraint registration, track primary keys per table: +```cpp +// In V9Builder::AddFieldConstraint +if (constraint == FieldConstraint::PrimaryKey) { + if (table_has_primary_key[table_name]) { + SetMultiplePrimaryKeyError(table_name); // Set global error flag + } + table_has_primary_key[table_name] = true; +} +``` + +### Phase 3: Type System Registration + +**Component**: V9TypeRegistration system (`v9_type_registration.h`) + +**Core Principle**: Only user-defined structs and enums get registered in the typespace. Primitives, arrays, Options, and special types are always inlined. + +**Registration Flow**: +```cpp +class V9TypeRegistration { + AlgebraicType registerType(const bsatn::AlgebraicType& bsatn_type, + const std::string& explicit_name = "", + const std::type_info* cpp_type = nullptr) { + // 1. Check if primitive → return inline + if (isPrimitive(bsatn_type)) return convertPrimitive(bsatn_type); + + // 2. Check if array → return inline Array with recursive element processing + if (bsatn_type.tag() == bsatn::AlgebraicTypeTag::Array) + return convertArray(bsatn_type); + + // 3. Check if Option → return inline Sum structure + if (isOptionType(bsatn_type)) return convertOption(bsatn_type); + + // 4. Check if special type → return inline Product structure + if (isSpecialType(bsatn_type)) return convertSpecialType(bsatn_type); + + // 5. User-defined type → register in typespace, return Ref + return registerUserDefinedType(bsatn_type, explicit_name, cpp_type); + } +}; +``` + +**Circular Reference Detection**: +```cpp +// Track types currently being registered +std::unordered_set types_being_registered_; + +AlgebraicType registerUserDefinedType(...) { + if (types_being_registered_.contains(type_name)) { + setError("Circular reference detected in type: " + type_name); + return createErrorType(); + } + types_being_registered_.insert(type_name); + // ... process type ... + types_being_registered_.erase(type_name); +} +``` + +### Phase 4: Validation and Error Detection (__preinit__99_) + +**Location**: Final preinit function - runs after all registration is complete + +**Error Detection**: +```cpp +extern "C" __attribute__((export_name("__preinit__99_validate_types"))) +void __preinit__99_validate_types() { + // 1. Check for circular reference errors + if (g_circular_ref_error) { + createErrorModule("ERROR_CIRCULAR_REFERENCE_" + g_circular_ref_type_name); + return; + } + + // 2. Check for multiple primary key errors + if (g_multiple_primary_key_error) { + createErrorModule("ERROR_MULTIPLE_PRIMARY_KEYS_" + g_multiple_primary_key_table_name); + return; + } + + // 3. Check for type registration errors + if (getV9TypeRegistration().hasError()) { + createErrorModule("ERROR_TYPE_REGISTRATION_" + sanitize(error_message)); + return; + } +} +``` + +**Error Module Creation**: When errors are detected, the normal module is replaced with a special error module containing an invalid type reference. When SpacetimeDB tries to resolve the type, it fails with an error message that includes the descriptive error type name. + +### Phase 5: Module Description Export + +**Function**: `__describe_module__()` - Called by SpacetimeDB after preinit functions complete + +**Process**: +1. Serialize the completed V9 module definition +2. Include typespace (all registered types) +3. Include tables with constraints +4. Include reducers with parameter types +5. Include named type exports +6. Return binary module description + +## Namespace Qualification System + +### Overview +The C++ SDK provides a unique compile-time namespace qualification system for enum types, allowing better organization in generated client code without affecting server-side C++ usage. + +### Architecture Components + +#### 1. Compile-Time Namespace Storage +**Location**: `enum_macro.h` - namespace_info template specialization + +```cpp +namespace SpacetimeDb::detail { + // Primary template - no namespace by default + template + struct namespace_info { + static constexpr const char* value = nullptr; + }; +} + +// SPACETIMEDB_NAMESPACE macro creates specialization +#define SPACETIMEDB_NAMESPACE(EnumType, NamespacePrefix) \ + namespace SpacetimeDb::detail { \ + template<> \ + struct namespace_info { \ + static constexpr const char* value = NamespacePrefix; \ + }; \ + } +``` + +#### 2. LazyTypeRegistrar Integration +**Location**: `v9_type_registration.h` - Compile-time namespace detection + +```cpp +template +class LazyTypeRegistrar { + static bsatn::AlgebraicType getOrRegister(...) { + std::string qualified_name = type_name; + + // Compile-time check for namespace information + if constexpr (requires { SpacetimeDb::detail::namespace_info::value; }) { + constexpr const char* namespace_prefix = + SpacetimeDb::detail::namespace_info::value; + if (namespace_prefix != nullptr) { + qualified_name = std::string(namespace_prefix) + "." + type_name; + } + } + + // Register with qualified name + type_index_ = getV9TypeRegistration().registerAndGetIndex( + algebraic_type, qualified_name, &typeid(T)); + } +}; +``` + +#### 3. Type Registration with Namespaces +When an enum with namespace qualification is registered: +1. SPACETIMEDB_ENUM defines the enum and its BSATN traits +2. SPACETIMEDB_NAMESPACE adds compile-time metadata +3. LazyTypeRegistrar detects namespace at compile-time +4. Type is registered with qualified name (e.g., "Auth.UserRole") +5. Client generators recognize the namespace structure + +### Design Rationale + +**Why Separate Macros?** +- Clean separation of concerns: enum definition vs. namespace qualification +- Optional feature - enums work without namespaces +- Non-intrusive - doesn't modify the enum type itself +- Compile-time only - zero runtime overhead + +**Why Template Specialization?** +- Type-safe association between enum and namespace +- Compile-time resolution - no runtime lookups +- Works with C++20 concepts and if constexpr +- No memory overhead - constexpr strings + +### Comparison with Other Approaches + +**Alternative 1: Preinit Runtime Modification** (Rejected) +- Would require modifying types after registration +- Complex synchronization with type registry +- Runtime overhead for namespace lookup + +**Alternative 2: Embedded in SPACETIMEDB_ENUM** (Rejected) +- Would complicate the macro syntax +- Makes namespace mandatory rather than optional +- Harder to add namespaces to existing code + +**Current Approach Benefits**: +- Clean, modular design +- Zero runtime cost +- Optional and backwards-compatible +- Easy to understand and maintain + +## Key Differences from Rust and C# SDKs + +### 1. Type Registration Approach + +**Rust SDK**: +- Derive macros automatically generate type registration code +- Compile-time code generation using procedural macros +- Direct integration with Rust's type system +- Option types automatically inlined by macro system + +**C# SDK**: +- Reflection-based runtime type discovery +- Attribute-based configuration +- Dynamic type registration during module initialization +- .NET type system integration + +**C++ SDK**: +- Template-based compile-time validation with runtime registration +- Macro-generated __preinit__ functions for ordered initialization +- Manual type registration via SPACETIMEDB_STRUCT macros +- Hybrid approach combining compile-time safety with runtime flexibility + +### 2. Constraint Validation + +**Rust SDK**: +- Procedural macros generate compile-time validation +- Type system automatically enforces valid constraints +- No runtime constraint checking needed + +**C# SDK**: +- Runtime validation using reflection +- Attributes specify constraints, validated during registration +- Dynamic error reporting + +**C++ SDK**: +- **Three-layer validation system**: + 1. **Compile-time**: C++20 concepts and static assertions + 2. **Registration-time**: Multiple primary key detection + 3. **Module load**: preinit_99_ comprehensive validation +- Most sophisticated error detection of all SDKs + +### 3. Error Handling Strategy + +**Rust SDK**: +- Compile-time errors prevent building invalid modules +- Type system prevents most runtime errors +- Standard Rust error messages + +**C# SDK**: +- Runtime exceptions with detailed error messages +- Graceful error handling with exception propagation +- .NET debugging tools integration + +**C++ SDK**: +- **Error module replacement strategy**: + - Invalid modules replaced with special error modules + - Error type names embed descriptive information + - SpacetimeDB server provides clear error messages + - Comprehensive error categorization and reporting + +### 4. Type System Philosophy + +**Rust SDK**: +- "If it compiles, it works" - maximum compile-time validation +- Leverages Rust's ownership and type system +- Minimal runtime overhead + +**C# SDK**: +- "Flexibility with safety" - runtime validation with rich error messages +- Leverages .NET reflection and attributes +- Dynamic type discovery + +**C++ SDK**: +- **"Validate early, validate often"** - multi-layer validation system +- Combines C++20 compile-time features with runtime checks +- Nominal type system with explicit registration +- Optimized for catching errors at the earliest possible phase + +## Memory Management and Performance + +### Compile-Time Optimizations +- Template specialization eliminates runtime overhead +- Constexpr evaluations reduce WASM binary size +- Zero-cost abstractions for type-safe database access + +### Runtime Efficiency +- Minimal allocation during type registration +- Efficient binary serialization with BSATN +- Optimized field accessors with index caching + +### WASM Constraints +- 16MB initial memory limit (configurable) +- No dynamic memory growth during module registration +- Careful memory management in preinit functions + +## Development Workflow Integration + +### Error Detection Timeline +``` +Developer writes code + ↓ +C++ compilation → Compile-time validation (concepts, static_assert) + ↓ +Emscripten WASM build → Template instantiation validation + ↓ +Module publishing → Runtime validation (__preinit__99_) + ↓ +SpacetimeDB loading → Server-side validation and error reporting +``` + +### Debugging Support +- **Compile-time**: Clear error messages with field/constraint guidance +- **Build-time**: Template instantiation error reporting +- **Runtime**: Comprehensive logging with error categorization +- **Server-side**: Descriptive error module names for easy diagnosis + +## Future Architecture Considerations + +### Potential Improvements +1. **Unified Validation**: Move more validation to compile-time using concepts +2. **Better Error Recovery**: Partial module loading with isolated error handling +3. **Performance Optimization**: Reduce template instantiation overhead +4. **Enhanced Debugging**: Source location tracking for runtime errors + +### Scalability +- Type registration system scales linearly with module complexity +- Preinit function count grows with table/reducer count but remains manageable +- Memory usage is predictable and bounded + +## Related Documentation + +- [Type System Details](README.md#features) +- [Constraint Validation Tests](tests/type-isolation-test/) +- [API Reference](REFERENCE.md) +- [Quick Start Guide](QUICKSTART.md) \ No newline at end of file diff --git a/crates/bindings-cpp/CMakeLists.txt b/crates/bindings-cpp/CMakeLists.txt new file mode 100644 index 00000000000..782a351a525 --- /dev/null +++ b/crates/bindings-cpp/CMakeLists.txt @@ -0,0 +1,82 @@ +# SpacetimeDB C++ Module Library CMake Configuration + +cmake_minimum_required(VERSION 3.15) +project(SpacetimeDBCppModuleLibrary CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Module Library source files +set(LIBRARY_SOURCES + src/abi/module_exports.cpp + src/abi/wasi_shims.cpp + src/internal/Module.cpp + src/internal/AlgebraicType.cpp # Required for V9 autogen types + src/internal/v9_builder.cpp # V9 incremental module builder + src/internal/v9_type_registration.cpp # Unified type registration system + # src/library/index_management.cpp # Disabled due to conflicts with index_ops.h + # src/type_registry.cpp # REMOVED - TypeRegistry system eliminated +) + +# Create the Module Library +add_library(spacetimedb_cpp_library STATIC ${LIBRARY_SOURCES}) + +# Set include directories +target_include_directories(spacetimedb_cpp_library + PUBLIC + $ + $ +) + +# Create an alias target for better namespacing +add_library(spacetimedb::spacetimedb_cpp_library ALIAS spacetimedb_cpp_library) + +# Set compile options if building for WASM +if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") + target_compile_options(spacetimedb_cpp_library PRIVATE + -O2 # Optimize for performance + -fno-exceptions # Disable exceptions for WASM compatibility + -ffunction-sections # Place each function in its own section + -fdata-sections # Place each data item in its own section + -Wall -Wextra # Enable warnings + ) + # Note: -g0 should be set in the final executable's linker flags, not here + # This allows debugging the library during development if needed +endif() + +# Export compile commands for better IDE support +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Testing configuration +option(BUILD_TESTS "Build the test suite" ON) + +if(BUILD_TESTS AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") + enable_testing() + + # Add test executable + add_executable(test_bsatn tests/main.cpp tests/module_library_unit_tests.cpp) + + # Link against the module library + target_link_libraries(test_bsatn PRIVATE spacetimedb_cpp_library) + + # Set C++20 standard for tests + target_compile_features(test_bsatn PRIVATE cxx_std_20) + + # Add test to CTest + add_test(NAME bsatn_tests COMMAND test_bsatn) + + # Add verbose test variant + add_test(NAME bsatn_tests_verbose COMMAND test_bsatn -v) + + # Set test properties + set_tests_properties(bsatn_tests PROPERTIES + TIMEOUT 30 + LABELS "unit" + ) + + set_tests_properties(bsatn_tests_verbose PROPERTIES + TIMEOUT 30 + LABELS "unit;verbose" + ) +endif() \ No newline at end of file diff --git a/crates/bindings-cpp/DEVELOP.md b/crates/bindings-cpp/DEVELOP.md new file mode 100644 index 00000000000..0d4a761d00d --- /dev/null +++ b/crates/bindings-cpp/DEVELOP.md @@ -0,0 +1,597 @@ +# C++ SDK Development Roadmap: Leveraging Modern C++ Standards + +This document explores how upgrading to C++23 and C++26 could fundamentally transform the SpacetimeDB C++ SDK, moving from runtime registration to compile-time type system integration and eliminating most macro usage. + +## Current Architecture: Why So Many Macros? + +### The Fundamental Problem: No Compile-Time Reflection in C++20 + +Without reflection, the compiler cannot: +- Enumerate struct fields automatically +- Determine field types and names +- Discover which types should be tables +- Generate serialization code +- Build type relationships + +This forces us into a multi-layer macro system with runtime registration: + +### Layer 1: SPACETIMEDB_STRUCT - Manual Field Enumeration + +```cpp +struct User { + uint32_t id; + std::string name; + std::string email; +}; +SPACETIMEDB_STRUCT(User, id, name, email) +``` + +**Why we need it:** +- C++ has no way to iterate over struct fields at compile-time +- We must manually list every field for serialization +- The macro generates a `bsatn_traits` specialization that knows how to serialize/deserialize + +**What it generates:** +```cpp +template<> struct bsatn_traits { + static AlgebraicType algebraic_type() { + // Builds Product type with fields [id, name, email] + } + static void serialize(Writer& w, const User& val) { + w.write(val.id); w.write(val.name); w.write(val.email); + } +}; +``` + +**Why it can't be compile-time:** We can't discover the fields without listing them explicitly. + +### Layer 2: SPACETIMEDB_TABLE - Runtime Table Registration + +```cpp +SPACETIMEDB_TABLE(User, users, Public) +``` + +**Why we need it separately from STRUCT:** +- A struct might be used as a table, a reducer parameter, or a nested type +- Not all structs are tables - some are just data types +- Table registration needs additional metadata (name, access level) + +**What it generates:** +```cpp +extern "C" __attribute__((export_name("__preinit__20_register_table_User"))) +void __preinit__20_register_table_User() { + Module::RegisterTable("users", true); +} +// Plus a tag type for clean syntax: ctx.db[users] +``` + +**Why it must be runtime:** The module schema must be built dynamically when the WASM module loads, as we can't generate a complete module description at compile-time. + +### Layer 3: FIELD_ Macros - Constraint Registration + +```cpp +FIELD_PrimaryKey(users, id); +FIELD_Unique(users, email); +FIELD_Index(users, name); +``` + +**Why we need separate macros per constraint:** +- Each field can have multiple constraints +- Constraints must be registered AFTER the table (priority ordering) +- Some constraints need compile-time validation (C++20 concepts) +- Can't be part of SPACETIMEDB_TABLE because we need the table to exist first + +**What they generate:** +```cpp +// Compile-time validation +static_assert(FilterableValue, "Primary keys must be filterable"); + +// Runtime registration +extern "C" __attribute__((export_name("__preinit__21_field_constraint_users_id"))) +void __preinit__21_field_constraint_users_id() { + AddFieldConstraint("users", "id", FieldConstraint::PrimaryKey); +} +``` + +**Why the split:** Compile-time validation via concepts, but runtime registration for module schema. + +### Layer 4: __preinit__ Priority System - Ordered Initialization + +```cpp +__preinit__01_ - Clear global state +__preinit__10_ - Field registration +__preinit__20_ - Table registration +__preinit__21_ - Constraints (must come after tables) +__preinit__30_ - Reducers +__preinit__99_ - Validation +``` + +**Why we need priority ordering:** +- Tables must exist before constraints can be added +- Types must be registered before they're referenced +- Validation must happen after everything else +- WebAssembly's linear memory model requires deterministic initialization + +**Why it's runtime:** WASM modules are initialized linearly, and we need to build the module schema during this initialization phase. + +### Layer 5: Namespace Qualification - Compile-Time Metadata + +```cpp +SPACETIMEDB_ENUM(UserRole, Admin, Moderator, Member) +SPACETIMEDB_NAMESPACE(UserRole, "Auth") // Separate macro for namespace +``` + +**Why we need a separate macro:** +- Namespace is optional metadata, not core to the enum definition +- Must work with existing enum definitions without modification +- Needs to associate compile-time string with type + +**What it generates:** +```cpp +namespace SpacetimeDb::detail { + template<> struct namespace_info { + static constexpr const char* value = "Auth"; + }; +} +``` + +**Why it's compile-time but still needs a macro:** +- C++20 has no way to attach metadata to types without explicit specialization +- Template specialization requires knowing the type name +- LazyTypeRegistrar uses `if constexpr` to detect namespace at compile-time + +### Layer 6: __describe_module__ - Final Runtime Assembly + +```cpp +extern "C" const uint8_t* __describe_module__() { + // Serialize the complete module built by __preinit__ functions + return Module::serialize(); +} +``` + +**Why it's needed:** +- SpacetimeDB server calls this to get the module schema +- Must return a binary description of all tables, types, reducers +- Can only be built after all __preinit__ functions have run + +**The fundamental limitation:** Without compile-time reflection, we cannot know at compile-time: +- Which types are tables +- What fields each struct has +- What constraints apply to each field +- The complete type dependency graph +- What namespace qualifications are applied + +### The Cascade Effect + +This creates a cascade of limitations: + +1. **No automatic serialization** → Need SPACETIMEDB_STRUCT macro +2. **No type discovery** → Need explicit SPACETIMEDB_TABLE macro +3. **No field introspection** → Need separate FIELD_ macros +4. **No compile-time module generation** → Need runtime __preinit__ system +5. **No static validation** → Need runtime validation in __preinit__99 + +Each macro exists because C++20 lacks the reflection capabilities to do this work automatically. The runtime registration exists because we can't build a complete module description at compile-time without knowing what types exist and their relationships. + +## Current Architecture Limitations (Summary) + +The current C++20 SDK relies on: +- **Runtime registration** via `__preinit__` functions (because no compile-time type discovery) +- **Heavy macro usage** for type and table registration (because no reflection) +- **Runtime error detection** in `__preinit__99_validate_types` (because incomplete compile-time info) +- **Manual serialization** through SPACETIMEDB_STRUCT macros (because no field enumeration) +- **String-based type identification** requiring explicit registration (because no compile-time type identity) + +## C++23 Improvements + +### 1. Deducing `this` for Zero-Cost Field Accessors + +**Current approach:** +```cpp +template +class TypedFieldAccessor : public TableAccessor { + FieldType TableType::*member_ptr_; + // Complex inheritance hierarchy +}; +``` + +**C++23 approach:** +```cpp +struct TableAccessor { + template + auto filter(this Self&& self, auto&& predicate) { + // Deduce table type from self, no inheritance needed + return self.table_.filter(std::forward(predicate)); + } +}; +``` + +**Benefits:** +- Eliminate accessor class hierarchy +- Perfect forwarding throughout +- Reduced template instantiation overhead + +### 2. `if consteval` for Hybrid Compile/Runtime Validation + +**Current approach:** +```cpp +// Static assertions in macros +static_assert(FilterableValue, "Error message"); +// Plus runtime validation in __preinit__99 +``` + +**C++23 approach:** +```cpp +template +constexpr auto validate_constraint() { + if consteval { + // Compile-time path: full validation + static_assert(FilterableValue); + return compile_time_type_id(); + } else { + // Runtime fallback for dynamic types + return runtime_type_registry::get(); + } +} +``` + +**Benefits:** +- Single validation function works at compile-time or runtime +- Better error messages with source locations +- Gradual migration path from runtime to compile-time + +### 3. `std::expected` for Error Propagation + +**Current approach:** +```cpp +// Global error flags +static bool g_multiple_primary_key_error = false; +static std::string g_multiple_primary_key_table_name = ""; +``` + +**C++23 approach:** +```cpp +template +using RegistrationResult = std::expected; + +constexpr RegistrationResult register_table() { + if (/* multiple primary keys detected */) + return std::unexpected(RegistrationError::MultiplePrimaryKeys); + return TypeId{...}; +} +``` + +**Benefits:** +- Type-safe error handling +- Composable error propagation +- No global state needed + +### 4. `constexpr std::unique_ptr` for Compile-Time Type Trees + +**Current approach:** +```cpp +// Runtime type tree building +std::vector types; +types.push_back(...); +``` + +**C++23 approach:** +```cpp +constexpr auto build_type_tree() { + std::unique_ptr root = std::make_unique(); + // Build entire type hierarchy at compile time + return root; +} + +constexpr auto type_tree = build_type_tree(); +``` + +**Benefits:** +- Complete type system known at compile-time +- Zero runtime allocation +- Enables compile-time validation of entire module + +### 5. `std::ranges` for Cleaner Type Processing + +**Current approach:** +```cpp +// Manual iteration and filtering +std::vector types; +for (const auto& type : all_types) { + if (isPrimitive(type)) continue; + if (isOptionType(type)) continue; + types.push_back(processType(type)); +} +``` + +**C++23 approach:** +```cpp +// Declarative pipeline with ranges +auto process_types() { + return all_types + | std::views::filter(not_primitive) + | std::views::filter(not_option) + | std::views::transform(processType) + | std::ranges::to(); +} + +// Better: lazy evaluation for type checking +auto valid_types = registered_types + | std::views::filter([](auto& t) { return validate_type(t).has_value(); }); +``` + +**Benefits:** +- More declarative and readable type processing +- Lazy evaluation reduces memory usage +- Composable validation pipelines +- Better separation of filtering logic from processing + +### 6. `std::mdspan` for Table Data Access + +**Current approach:** +```cpp +// Custom iterators and accessors +for (const auto& row : table) { } +``` + +**C++23 approach:** +```cpp +template +using TableView = std::mdspan()>>; + +// Direct columnar access +auto names = table_view[std::full_extent, name_column]; +``` + +**Benefits:** +- Standard library support for multi-dimensional access +- Optimized for columnar operations +- Compatible with parallel algorithms + +## C++26 Transformative Features + +### 1. Static Reflection (P2996) - Complete Macro Elimination + +**Current approach:** +```cpp +SPACETIMEDB_STRUCT(User, id, name, email) +SPACETIMEDB_TABLE(User, users, Public) +FIELD_PrimaryKey(users, id); +SPACETIMEDB_ENUM(UserRole, Admin, Moderator, Member) +SPACETIMEDB_NAMESPACE(UserRole, "Auth") // Separate macro for namespace +``` + +**C++26 approach:** +```cpp +struct [[spacetimedb::table("users", public)]] User { + [[spacetimedb::primary_key]] uint32_t id; + [[spacetimedb::unique]] std::string email; + std::string name; +}; + +enum class [[spacetimedb::namespace("Auth")]] UserRole { + Admin, Moderator, Member +}; + +// Automatic registration via reflection +template requires has_spacetimedb_table_attr +consteval void register_table() { + constexpr auto members = ^T::members(); + for (constexpr auto member : members) { + if constexpr (has_attribute) { + register_primary_key(member.name(), member.type()); + } + } +} + +// Automatic namespace detection via reflection +template +consteval std::string get_qualified_name() { + if constexpr (has_attribute<^T, spacetimedb::namespace>) { + return get_attribute<^T, spacetimedb::namespace>() + "." + name_of(^T); + } + return name_of(^T); +} +``` + +**Benefits:** +- **Zero macros needed** +- Natural C++ syntax with attributes +- Complete type information at compile-time +- Automatic serialization without SPACETIMEDB_STRUCT + +### 2. Contracts for Constraint Validation + +**Current approach:** +```cpp +static_assert(FilterableValue, "Field cannot have Index constraint"); +``` + +**C++26 approach:** +```cpp +template +void add_index_constraint(T TableType::*field) + [[pre: FilterableValue]] + [[pre: !has_existing_index(field)]] +{ + // Contract violations become compile-time or runtime errors + // depending on evaluation context +} +``` + +**Benefits:** +- Declarative constraint specification +- Automatic error messages from contract violations +- Works for both compile-time and runtime validation + +### 3. Pattern Matching for Type Dispatch + +**Current approach:** +```cpp +switch(type.tag()) { + case AlgebraicTypeTag::Product: ... + case AlgebraicTypeTag::Sum: ... + // Manual casting and handling +} +``` + +**C++26 approach:** +```cpp +inspect(type) { + p => serialize_product(p), + s => serialize_sum(s), + [auto elem_type] => serialize_array(elem_type), +