Files
Jason Larabie 346e2b2514 Add C++ query builder (#4664)
# Description of Changes
- Added a query builder for C++ module bindings
  - Added query-builder table/filter/join types
- Added semijoin support with compile-time checks for lookup-table and
indexed-field usage
  - Added support for returning query-builder queries from C++ views
- Hooked query-builder metadata into the C++ table/view macros and V10
module-def path
- Added test coverage for the new C++ query-builder behavior
  - Compile tests for pass/fail cases
  - SQL tests for generated query output
  - Added a C++ test module for view primary key coverage
- **Update:** Switched the core to pass the columns and index-columns
metadata with the table source for better client-side codegen to have
some shared code between server + client.
# API and ABI breaking changes
- No intended API or ABI breaking changes
- Adds a new public query-builder API to the C++ bindings
- C++ views can now return query-builder query types in addition to
materialized row results

# Expected complexity level and risk

3 - Mostly contained to C++ bindings, but it touches macros, view
registration/serialization, and module-def generation, so there are a
few places where the pieces need to stay in sync.

# Testing

I've done end to end testing of I think every type as well as built some
tests to confirm the SQL output.

- [x] Run the C++ query-builder SQL tests
[crates/bindings-cpp/tests/query-builder-compile/run_query_builder_compile_tests.sh]
- [x] Smoke test a generated C++ module using query-builder views

---------

Signed-off-by: Jason Larabie <jason@clockworklabs.io>
Co-authored-by: Ryan <r.ekhoff@clockworklabs.io>
Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
2026-06-12 13:02:36 +00:00

921 lines
53 KiB
C++

#ifndef SPACETIMEDB_MACROS_H
#define SPACETIMEDB_MACROS_H
#include "spacetimedb/bsatn/bsatn.h"
#include "spacetimedb/reducer_context.h"
#include "spacetimedb/query_builder.h"
#include "spacetimedb/table.h"
#include "spacetimedb/bsatn/traits.h"
#include <string>
#include <vector>
#include <sstream>
#include <utility>
namespace SpacetimeDB {
namespace Internal {
/**
* @brief Helper function to parse parameter names from stringified parameter list
*
* This is used internally by SPACETIMEDB_REDUCER and SPACETIMEDB_PROCEDURE macros to
* extract parameter names from the stringified __VA_ARGS__ (which excludes the context).
*
* The macros separate ctx_param from __VA_ARGS__, so this function receives only
* the non-context parameters.
*
* @param param_list The stringified parameter list (e.g., "uint32_t id, std::string name")
* @return Vector of parameter names
*/
inline std::vector<std::string> parseParameterNames(const std::string& param_list) {
std::vector<std::string> param_names;
// Handle empty parameter list
if (param_list.empty()) {
return param_names;
}
// Split by comma
std::istringstream stream(param_list);
std::string param;
while (std::getline(stream, param, ',')) {
// Trim whitespace
param.erase(0, param.find_first_not_of(" \t\n\r"));
param.erase(param.find_last_not_of(" \t\n\r") + 1);
// Skip empty parameters
if (param.empty()) {
continue;
}
// Extract the parameter name (last word before end or default value)
// Handle cases like:
// - "int x"
// - "const std::string& name"
// - "std::vector<int> values"
// - "MyType* ptr"
// - "int x = 5"
// Remove default value if present
size_t eq_pos = param.find('=');
std::string decl = (eq_pos != std::string::npos) ? param.substr(0, eq_pos) : param;
// Trim trailing whitespace
decl.erase(decl.find_last_not_of(" \t\n\r") + 1);
// Find the last word (parameter name)
size_t last_space = decl.find_last_of(" \t&*");
if (last_space != std::string::npos && last_space + 1 < decl.length()) {
std::string name = decl.substr(last_space + 1);
if (!name.empty()) {
param_names.push_back(name);
}
}
}
return param_names;
}
} // namespace Internal
} // namespace SpacetimeDB
// =============================================================================
// UNIFIED HELPER MACROS FOR SPACETIMEDB TYPE REGISTRATION
// =============================================================================
/**
* @brief Common CONCAT macros - unified from multiple files
*
* These eliminate duplication of CONCAT macro definitions across files.
*/
#ifndef SPACETIMEDB_CONCAT_IMPL
#define SPACETIMEDB_CONCAT_IMPL(a, b) a##b
#define SPACETIMEDB_CONCAT(a, b) SPACETIMEDB_CONCAT_IMPL(a, b)
#endif
/**
* @brief Helper for stringifying values (used in export names)
*/
#ifndef SPACETIMEDB_STRINGIFY_IMPL
#define SPACETIMEDB_STRINGIFY_IMPL(x) #x
#define SPACETIMEDB_STRINGIFY(x) SPACETIMEDB_STRINGIFY_IMPL(x)
#endif
/**
* @brief Helper for stringifying each argument in a variadic list
*
* Converts each identifier to a string literal. Used by multi-column index macros.
* Example: SPACETIMEDB_STRINGIFY_EACH(x, y, z) -> "x", "y", "z"
*/
#define SPACETIMEDB_STRINGIFY_ARG(x) #x
#define SPACETIMEDB_STRINGIFY_EACH(...) SPACETIMEDB_FOR_EACH_STRINGIFY(__VA_ARGS__)
/**
* @brief FOR_EACH macro implementation for stringifying (up to 6 arguments)
*
* Supports up to 6 arguments (matching multi-column index limit).
* Uses a different name than the existing SPACETIMEDB_FOR_EACH_ARG to avoid conflicts.
*/
#define SPACETIMEDB_GET_STRINGIFY_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME
#define SPACETIMEDB_FOR_EACH_STRINGIFY_1(a) SPACETIMEDB_STRINGIFY_ARG(a)
#define SPACETIMEDB_FOR_EACH_STRINGIFY_2(a, b) SPACETIMEDB_STRINGIFY_ARG(a), SPACETIMEDB_STRINGIFY_ARG(b)
#define SPACETIMEDB_FOR_EACH_STRINGIFY_3(a, b, c) SPACETIMEDB_STRINGIFY_ARG(a), SPACETIMEDB_STRINGIFY_ARG(b), SPACETIMEDB_STRINGIFY_ARG(c)
#define SPACETIMEDB_FOR_EACH_STRINGIFY_4(a, b, c, d) SPACETIMEDB_STRINGIFY_ARG(a), SPACETIMEDB_STRINGIFY_ARG(b), SPACETIMEDB_STRINGIFY_ARG(c), SPACETIMEDB_STRINGIFY_ARG(d)
#define SPACETIMEDB_FOR_EACH_STRINGIFY_5(a, b, c, d, e) SPACETIMEDB_STRINGIFY_ARG(a), SPACETIMEDB_STRINGIFY_ARG(b), SPACETIMEDB_STRINGIFY_ARG(c), SPACETIMEDB_STRINGIFY_ARG(d), SPACETIMEDB_STRINGIFY_ARG(e)
#define SPACETIMEDB_FOR_EACH_STRINGIFY_6(a, b, c, d, e, f) SPACETIMEDB_STRINGIFY_ARG(a), SPACETIMEDB_STRINGIFY_ARG(b), SPACETIMEDB_STRINGIFY_ARG(c), SPACETIMEDB_STRINGIFY_ARG(d), SPACETIMEDB_STRINGIFY_ARG(e), SPACETIMEDB_STRINGIFY_ARG(f)
#define SPACETIMEDB_FOR_EACH_STRINGIFY(...) \
SPACETIMEDB_GET_STRINGIFY_MACRO(__VA_ARGS__, \
SPACETIMEDB_FOR_EACH_STRINGIFY_6, \
SPACETIMEDB_FOR_EACH_STRINGIFY_5, \
SPACETIMEDB_FOR_EACH_STRINGIFY_4, \
SPACETIMEDB_FOR_EACH_STRINGIFY_3, \
SPACETIMEDB_FOR_EACH_STRINGIFY_2, \
SPACETIMEDB_FOR_EACH_STRINGIFY_1)(__VA_ARGS__)
/**
* @brief Compatibility aliases for files that use different macro names
*
* This provides backward compatibility while centralizing macro definitions.
* Files can gradually migrate to the SPACETIMEDB_* prefixed versions.
*/
// Non-prefixed compatibility aliases
#ifndef CONCAT_IMPL
#define CONCAT_IMPL(a, b) SPACETIMEDB_CONCAT_IMPL(a, b)
#define CONCAT(a, b) SPACETIMEDB_CONCAT(a, b)
#endif
#ifndef STRINGIFY_IMPL
#define STRINGIFY_IMPL(x) SPACETIMEDB_STRINGIFY_IMPL(x)
#define STRINGIFY(x) SPACETIMEDB_STRINGIFY(x)
#endif
// SPACETIMEDB_PASTE compatibility alias (some files use PASTE instead of CONCAT)
#ifndef SPACETIMEDB_PASTE_IMPL
#define SPACETIMEDB_PASTE_IMPL(a, b) SPACETIMEDB_CONCAT_IMPL(a, b)
#define SPACETIMEDB_PASTE(a, b) SPACETIMEDB_CONCAT(a, b)
#endif
// TypeRegistry system removed - use ModuleTypeRegistration instead
/*
// Register a C++ type name in the global TypeRegistry for reducer parameter lookup
// This replaces the old TypeNameRegistry approach with a unified one.
// Used by all macros that need reducer parameter support.
#define SPACETIMEDB_REGISTER_CPP_TYPE_NAME(Type) \
namespace { \
struct Type##_cpp_name_registrar { \
Type##_cpp_name_registrar() { \
::SpacetimeDB::TypeRegistry::global_instance().register_cpp_type_name<Type>(#Type); \
} \
}; \
static Type##_cpp_name_registrar Type##_cpp_name_registrar_instance; \
}
// Register a type in the active TypeRegistry with proper naming
// This is the single registration point that eliminates triple registration.
// Only registers if a registry is available and not already registered.
#define SPACETIMEDB_REGISTER_TYPE_IN_REGISTRY(Type, algebraic_type) \
do { \
static bool Type##_v9_registered = false; \
if (!Type##_v9_registered) { \
Type##_v9_registered = true; \
std::string type_name = #Type; \
::SpacetimeDB::Internal::getModuleTypeRegistration().registerTypeByName(type_name, algebraic_type, &typeid(Type)); \
} \
} while(0)
*/
/**
* @brief Updated registration using ModuleTypeRegistration system
*
* This is the new registration point using the ModuleTypeRegistration system.
*/
#define SPACETIMEDB_REGISTER_TYPE_IN_V9(Type, algebraic_type) \
do { \
static bool Type##_v9_registered = false; \
if (!Type##_v9_registered) { \
Type##_v9_registered = true; \
std::string type_name = #Type; \
/* Register immediately with name and structure */ \
::SpacetimeDB::Internal::getModuleTypeRegistration().registerTypeByName(type_name, algebraic_type, &typeid(Type)); \
} \
} while(0)
/**
* @brief Complete type registration (registry + global name registration)
*
* This combines both registry registration and global name registration
* in a single, atomic operation. Use this in algebraic_type() methods.
*/
#define SPACETIMEDB_REGISTER_TYPE_COMPLETE(Type, algebraic_type) \
SPACETIMEDB_REGISTER_TYPE_IN_V9(Type, algebraic_type)
/**
* @brief Generate a default field_registrar specialization that does nothing
*
* Most types don't need field registration (only table types do).
* This provides a clean default implementation.
*/
#define SPACETIMEDB_GENERATE_EMPTY_FIELD_REGISTRAR(Type) \
template<> \
struct ::SpacetimeDB::field_registrar<Type> { \
static void register_fields() { \
/* Default: no field registration needed */ \
} \
};
/**
* @brief Generate a field_registrar that actually registers field descriptors
*
* This version is used by SPACETIMEDB_STRUCT to ensure fields
* are properly registered for table types.
*/
#define SPACETIMEDB_GENERATE_FIELD_REGISTRAR_WITH_FIELDS(Type, ...) \
template<> \
struct ::SpacetimeDB::field_registrar<Type> { \
static void register_fields() { \
static bool registered = false; \
if (registered) return; \
registered = true; \
SPACETIMEDB_REGISTER_FIELD_DESCRIPTORS(Type, __VA_ARGS__) \
} \
};
/**
* @brief Generate the complete registration bundle for a type
*
* This combines all the registration pieces that most macros need:
* - BSATN algebraic_type_of specialization
* - Empty field_registrar specialization
*
* Use this for simple types that don't need custom field registration.
* Note: Type name registration is now handled automatically by ModuleTypeRegistration.
*/
#define SPACETIMEDB_GENERATE_TYPE_REGISTRATION_BUNDLE(Type) \
template<> \
struct ::SpacetimeDB::bsatn::algebraic_type_of<Type> { \
static ::SpacetimeDB::bsatn::AlgebraicType get() { \
return ::SpacetimeDB::bsatn::bsatn_traits<Type>::algebraic_type(); \
} \
}; \
SPACETIMEDB_GENERATE_EMPTY_FIELD_REGISTRAR(Type)
/**
* @brief Generate the complete registration bundle with field registration
*
* This version includes actual field registration for table types.
* Note: Type name registration is now handled automatically by ModuleTypeRegistration.
*/
#define SPACETIMEDB_GENERATE_TYPE_REGISTRATION_BUNDLE_WITH_FIELDS(Type, ...) \
template<> \
struct ::SpacetimeDB::bsatn::algebraic_type_of<Type> { \
static ::SpacetimeDB::bsatn::AlgebraicType get() { \
return ::SpacetimeDB::bsatn::bsatn_traits<Type>::algebraic_type(); \
} \
}; \
SPACETIMEDB_GENERATE_FIELD_REGISTRAR_WITH_FIELDS(Type, __VA_ARGS__)
/**
* @brief Helper to check if a type is already registered (for optimization)
*
* This can be used to avoid expensive operations if a type is already registered.
*/
#define SPACETIMEDB_IS_TYPE_REGISTERED(Type) \
([]() { \
static bool Type##_checked = false; \
return Type##_checked; \
}())
/**
* @brief Mark a type as registered (for optimization)
*/
#define SPACETIMEDB_MARK_TYPE_REGISTERED(Type) \
do { \
static bool Type##_checked = false; \
Type##_checked = true; \
} while(0)
/**
* @brief Unified preinit function generator
*
* This consolidates the pattern used by all table/reducer registration macros:
* - Generates unique export name with priority, category, name, and line number
* - Creates the preinit function with proper C linkage
*
* @param priority Registration order (20 for tables, 30 for reducers)
* @param category Type of registration (register_table, reducer, etc.)
* @param name Specific name of the item being registered
* @param registration_body The actual registration code
*/
#define SPACETIMEDB_GENERATE_PREINIT_FUNCTION(priority, category, name, registration_body) \
__attribute__((export_name("__preinit__" #priority "_" #category "_" #name "_line_" SPACETIMEDB_STRINGIFY(__LINE__)))) \
extern "C" void SPACETIMEDB_CONCAT(__preinit__, SPACETIMEDB_CONCAT(priority, SPACETIMEDB_CONCAT(_, SPACETIMEDB_CONCAT(category, SPACETIMEDB_CONCAT(_, SPACETIMEDB_CONCAT(name, SPACETIMEDB_CONCAT(_line_, __LINE__)))))))() { \
registration_body \
}
/**
* @brief Unified table registration pattern
*
* Consolidates the common table registration logic used by SPACETIMEDB_TABLE macro.
*
* @param type The table struct type
* @param name_str Table name string
* @param access_enum Public/Private access
* @param constraint_list Vector initializer for field constraints
*/
// SPACETIMEDB_REGISTER_TABLE_TYPE macro removed - replaced by V9Builder system
/**
* @brief Unified reducer registration pattern
*
* Consolidates the common reducer registration logic used by SPACETIMEDB_REDUCER macro.
*
* @param function_name Name of the reducer function
* @param function_signature Full parameter list for the function
*/
#define SPACETIMEDB_REGISTER_REDUCER_FUNCTION(function_name, function_signature) \
void function_name function_signature; \
SPACETIMEDB_GENERATE_PREINIT_FUNCTION(30, reducer, function_name, \
SpacetimeDB::Internal::register_reducer_func_with_params(std::string(#function_name), function_name, #function_signature); \
) \
void function_name function_signature
/**
* @brief Unified lifecycle reducer registration
*
* Consolidates the pattern used by SPACETIMEDB_INIT, SPACETIMEDB_CLIENT_CONNECTED, etc.
*
* @param lifecycle_type Type of lifecycle (init, client_connected, client_disconnected)
* @param function_name Name of the reducer function
* @param function_signature Full parameter list for the function
* @param register_call The specific registration call for this lifecycle type
*/
#define SPACETIMEDB_REGISTER_LIFECYCLE_REDUCER(lifecycle_type, function_name, function_signature, register_call) \
void function_name function_signature; \
SPACETIMEDB_GENERATE_PREINIT_FUNCTION(20, reducer, lifecycle_type, register_call) \
void function_name function_signature
// =============================================================================
// VARIADIC ARGUMENT PROCESSING UTILITIES
// =============================================================================
// Consolidated variadic macro system (supports up to 50 arguments)
// Used by SPACETIMEDB_STRUCT and SPACETIMEDB_VARIANT_ENUM
#define SPACETIMEDB_GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,_33,_34,_35,_36,_37,_38,_39,_40,_41,_42,_43,_44,_45,_46,_47,_48,_49,_50,NAME,...) NAME
#define SPACETIMEDB_FOR_EACH_ARG(MACRO, obj, extra, ...) \
SPACETIMEDB_GET_MACRO(__VA_ARGS__, \
SPACETIMEDB_FE_50, SPACETIMEDB_FE_49, SPACETIMEDB_FE_48, SPACETIMEDB_FE_47, SPACETIMEDB_FE_46, \
SPACETIMEDB_FE_45, SPACETIMEDB_FE_44, SPACETIMEDB_FE_43, SPACETIMEDB_FE_42, SPACETIMEDB_FE_41, \
SPACETIMEDB_FE_40, SPACETIMEDB_FE_39, SPACETIMEDB_FE_38, SPACETIMEDB_FE_37, SPACETIMEDB_FE_36, \
SPACETIMEDB_FE_35, SPACETIMEDB_FE_34, SPACETIMEDB_FE_33, SPACETIMEDB_FE_32, SPACETIMEDB_FE_31, \
SPACETIMEDB_FE_30, SPACETIMEDB_FE_29, SPACETIMEDB_FE_28, SPACETIMEDB_FE_27, SPACETIMEDB_FE_26, \
SPACETIMEDB_FE_25, SPACETIMEDB_FE_24, SPACETIMEDB_FE_23, SPACETIMEDB_FE_22, SPACETIMEDB_FE_21, \
SPACETIMEDB_FE_20, SPACETIMEDB_FE_19, SPACETIMEDB_FE_18, SPACETIMEDB_FE_17, SPACETIMEDB_FE_16, \
SPACETIMEDB_FE_15, SPACETIMEDB_FE_14, SPACETIMEDB_FE_13, SPACETIMEDB_FE_12, SPACETIMEDB_FE_11, \
SPACETIMEDB_FE_10, SPACETIMEDB_FE_9, SPACETIMEDB_FE_8, SPACETIMEDB_FE_7, SPACETIMEDB_FE_6, \
SPACETIMEDB_FE_5, SPACETIMEDB_FE_4, SPACETIMEDB_FE_3, SPACETIMEDB_FE_2, SPACETIMEDB_FE_1) \
(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_1(MACRO, obj, extra, X) MACRO(obj, extra, X)
#define SPACETIMEDB_FE_2(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_1(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_3(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_2(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_4(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_3(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_5(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_4(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_6(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_5(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_7(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_6(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_8(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_7(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_9(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_8(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_10(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_9(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_11(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_10(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_12(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_11(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_13(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_12(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_14(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_13(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_15(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_14(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_16(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_15(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_17(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_16(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_18(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_17(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_19(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_18(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_20(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_19(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_21(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_20(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_22(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_21(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_23(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_22(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_24(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_23(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_25(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_24(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_26(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_25(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_27(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_26(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_28(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_27(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_29(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_28(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_30(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_29(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_31(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_30(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_32(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_31(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_33(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_32(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_34(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_33(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_35(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_34(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_36(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_35(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_37(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_36(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_38(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_37(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_39(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_38(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_40(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_39(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_41(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_40(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_42(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_41(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_43(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_42(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_44(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_43(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_45(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_44(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_46(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_45(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_47(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_46(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_48(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_47(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_49(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_48(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_50(MACRO, obj, extra, X, ...) MACRO(obj, extra, X) SPACETIMEDB_FE_49(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_ARG_COMMA(MACRO, obj, extra, ...) \
SPACETIMEDB_GET_MACRO(__VA_ARGS__, \
SPACETIMEDB_FE_COMMA_50, SPACETIMEDB_FE_COMMA_49, SPACETIMEDB_FE_COMMA_48, SPACETIMEDB_FE_COMMA_47, SPACETIMEDB_FE_COMMA_46, \
SPACETIMEDB_FE_COMMA_45, SPACETIMEDB_FE_COMMA_44, SPACETIMEDB_FE_COMMA_43, SPACETIMEDB_FE_COMMA_42, SPACETIMEDB_FE_COMMA_41, \
SPACETIMEDB_FE_COMMA_40, SPACETIMEDB_FE_COMMA_39, SPACETIMEDB_FE_COMMA_38, SPACETIMEDB_FE_COMMA_37, SPACETIMEDB_FE_COMMA_36, \
SPACETIMEDB_FE_COMMA_35, SPACETIMEDB_FE_COMMA_34, SPACETIMEDB_FE_COMMA_33, SPACETIMEDB_FE_COMMA_32, SPACETIMEDB_FE_COMMA_31, \
SPACETIMEDB_FE_COMMA_30, SPACETIMEDB_FE_COMMA_29, SPACETIMEDB_FE_COMMA_28, SPACETIMEDB_FE_COMMA_27, SPACETIMEDB_FE_COMMA_26, \
SPACETIMEDB_FE_COMMA_25, SPACETIMEDB_FE_COMMA_24, SPACETIMEDB_FE_COMMA_23, SPACETIMEDB_FE_COMMA_22, SPACETIMEDB_FE_COMMA_21, \
SPACETIMEDB_FE_COMMA_20, SPACETIMEDB_FE_COMMA_19, SPACETIMEDB_FE_COMMA_18, SPACETIMEDB_FE_COMMA_17, SPACETIMEDB_FE_COMMA_16, \
SPACETIMEDB_FE_COMMA_15, SPACETIMEDB_FE_COMMA_14, SPACETIMEDB_FE_COMMA_13, SPACETIMEDB_FE_COMMA_12, SPACETIMEDB_FE_COMMA_11, \
SPACETIMEDB_FE_COMMA_10, SPACETIMEDB_FE_COMMA_9, SPACETIMEDB_FE_COMMA_8, SPACETIMEDB_FE_COMMA_7, SPACETIMEDB_FE_COMMA_6, \
SPACETIMEDB_FE_COMMA_5, SPACETIMEDB_FE_COMMA_4, SPACETIMEDB_FE_COMMA_3, SPACETIMEDB_FE_COMMA_2, SPACETIMEDB_FE_COMMA_1) \
(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_1(MACRO, obj, extra, X) MACRO(obj, extra, X)
#define SPACETIMEDB_FE_COMMA_2(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_1(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_3(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_2(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_4(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_3(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_5(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_4(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_6(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_5(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_7(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_6(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_8(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_7(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_9(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_8(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_10(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_9(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_11(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_10(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_12(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_11(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_13(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_12(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_14(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_13(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_15(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_14(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_16(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_15(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_17(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_16(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_18(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_17(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_19(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_18(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_20(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_19(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_21(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_20(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_22(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_21(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_23(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_22(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_24(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_23(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_25(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_24(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_26(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_25(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_27(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_26(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_28(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_27(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_29(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_28(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_30(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_29(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_31(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_30(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_32(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_31(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_33(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_32(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_34(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_33(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_35(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_34(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_36(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_35(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_37(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_36(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_38(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_37(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_39(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_38(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_40(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_39(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_41(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_40(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_42(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_41(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_43(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_42(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_44(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_43(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_45(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_44(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_46(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_45(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_47(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_46(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_48(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_47(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_49(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_48(MACRO, obj, extra, __VA_ARGS__)
#define SPACETIMEDB_FE_COMMA_50(MACRO, obj, extra, X, ...) MACRO(obj, extra, X), SPACETIMEDB_FE_COMMA_49(MACRO, obj, extra, __VA_ARGS__)
// =============================================================================
// SPECIALIZED FOR_EACH MACROS FOR DIFFERENT USE CASES
// =============================================================================
/**
* @brief FOR_EACH_VARIANT - specialized macro for enum variant processing
*
* This adapts the unified SPACETIMEDB_FOR_EACH_ARG to use separator-based syntax
* instead of obj-extra syntax. Used by enum_macro.h for variant processing.
*/
#define SPACETIMEDB_FOR_EACH_VARIANT(macro, sep, ...) \
SPACETIMEDB_FOR_EACH_VARIANT_IMPL(macro, sep, ##__VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_IMPL(macro, sep, ...) \
SPACETIMEDB_GET_MACRO(__VA_ARGS__, \
SPACETIMEDB_FOR_EACH_VARIANT_FE_50, SPACETIMEDB_FOR_EACH_VARIANT_FE_49, SPACETIMEDB_FOR_EACH_VARIANT_FE_48, SPACETIMEDB_FOR_EACH_VARIANT_FE_47, SPACETIMEDB_FOR_EACH_VARIANT_FE_46, \
SPACETIMEDB_FOR_EACH_VARIANT_FE_45, SPACETIMEDB_FOR_EACH_VARIANT_FE_44, SPACETIMEDB_FOR_EACH_VARIANT_FE_43, SPACETIMEDB_FOR_EACH_VARIANT_FE_42, SPACETIMEDB_FOR_EACH_VARIANT_FE_41, \
SPACETIMEDB_FOR_EACH_VARIANT_FE_40, SPACETIMEDB_FOR_EACH_VARIANT_FE_39, SPACETIMEDB_FOR_EACH_VARIANT_FE_38, SPACETIMEDB_FOR_EACH_VARIANT_FE_37, SPACETIMEDB_FOR_EACH_VARIANT_FE_36, \
SPACETIMEDB_FOR_EACH_VARIANT_FE_35, SPACETIMEDB_FOR_EACH_VARIANT_FE_34, SPACETIMEDB_FOR_EACH_VARIANT_FE_33, SPACETIMEDB_FOR_EACH_VARIANT_FE_32, SPACETIMEDB_FOR_EACH_VARIANT_FE_31, \
SPACETIMEDB_FOR_EACH_VARIANT_FE_30, SPACETIMEDB_FOR_EACH_VARIANT_FE_29, SPACETIMEDB_FOR_EACH_VARIANT_FE_28, SPACETIMEDB_FOR_EACH_VARIANT_FE_27, SPACETIMEDB_FOR_EACH_VARIANT_FE_26, \
SPACETIMEDB_FOR_EACH_VARIANT_FE_25, SPACETIMEDB_FOR_EACH_VARIANT_FE_24, SPACETIMEDB_FOR_EACH_VARIANT_FE_23, SPACETIMEDB_FOR_EACH_VARIANT_FE_22, SPACETIMEDB_FOR_EACH_VARIANT_FE_21, \
SPACETIMEDB_FOR_EACH_VARIANT_FE_20, SPACETIMEDB_FOR_EACH_VARIANT_FE_19, SPACETIMEDB_FOR_EACH_VARIANT_FE_18, SPACETIMEDB_FOR_EACH_VARIANT_FE_17, SPACETIMEDB_FOR_EACH_VARIANT_FE_16, \
SPACETIMEDB_FOR_EACH_VARIANT_FE_15, SPACETIMEDB_FOR_EACH_VARIANT_FE_14, SPACETIMEDB_FOR_EACH_VARIANT_FE_13, SPACETIMEDB_FOR_EACH_VARIANT_FE_12, SPACETIMEDB_FOR_EACH_VARIANT_FE_11, \
SPACETIMEDB_FOR_EACH_VARIANT_FE_10, SPACETIMEDB_FOR_EACH_VARIANT_FE_9, SPACETIMEDB_FOR_EACH_VARIANT_FE_8, SPACETIMEDB_FOR_EACH_VARIANT_FE_7, SPACETIMEDB_FOR_EACH_VARIANT_FE_6, \
SPACETIMEDB_FOR_EACH_VARIANT_FE_5, SPACETIMEDB_FOR_EACH_VARIANT_FE_4, SPACETIMEDB_FOR_EACH_VARIANT_FE_3, SPACETIMEDB_FOR_EACH_VARIANT_FE_2, SPACETIMEDB_FOR_EACH_VARIANT_FE_1) \
(macro, sep, ##__VA_ARGS__)
// Adapter macros that apply macro + separator pattern (instead of macro-obj-extra pattern)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_1(macro, sep, X) macro(X)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_2(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_1(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_3(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_2(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_4(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_3(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_5(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_4(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_6(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_5(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_7(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_6(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_8(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_7(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_9(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_8(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_10(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_9(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_11(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_10(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_12(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_11(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_13(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_12(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_14(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_13(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_15(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_14(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_16(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_15(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_17(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_16(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_18(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_17(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_19(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_18(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_20(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_19(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_21(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_20(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_22(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_21(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_23(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_22(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_24(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_23(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_25(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_24(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_26(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_25(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_27(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_26(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_28(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_27(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_29(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_28(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_30(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_29(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_31(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_30(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_32(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_31(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_33(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_32(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_34(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_33(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_35(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_34(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_36(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_35(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_37(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_36(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_38(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_37(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_39(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_38(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_40(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_39(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_41(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_40(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_42(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_41(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_43(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_42(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_44(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_43(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_45(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_44(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_46(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_45(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_47(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_46(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_48(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_47(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_49(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_48(macro, sep, __VA_ARGS__)
#define SPACETIMEDB_FOR_EACH_VARIANT_FE_50(macro, sep, X, ...) macro(X) sep() SPACETIMEDB_FOR_EACH_VARIANT_FE_49(macro, sep, __VA_ARGS__)
// Compatibility aliases for files that expect non-prefixed names
#define FOR_EACH_VARIANT SPACETIMEDB_FOR_EACH_VARIANT
// =============================================================================
// TABLE MACROS
// =============================================================================
// Enhanced table macro with optional scheduling support
// Usage: SPACETIMEDB_TABLE(MyStruct, "table_name", Public)
// or: SPACETIMEDB_TABLE(MyStruct, "table_name", Private)
// REMOVED: SPACETIMEDB_TABLE is now defined in field_metadata.h
// #define SPACETIMEDB_TABLE(...) \
// SPACETIMEDB_PASTE(SPACETIMEDB_TABLE_, SPACETIMEDB_NARGS(__VA_ARGS__))(__VA_ARGS__)
// All table macros have been moved to field_metadata.h to avoid duplicates.
// The SPACETIMEDB_TABLE macro in field_metadata.h supports:
// - SPACETIMEDB_TABLE(Type, table_name, Public)
// - SPACETIMEDB_TABLE(Type, table_name, Private)
// - Multiple tables using the same struct type
// All reducer macros have been moved to reducer_macros.h
// This includes SPACETIMEDB_REDUCER, SPACETIMEDB_INIT,
// SPACETIMEDB_CLIENT_CONNECTED, and SPACETIMEDB_CLIENT_DISCONNECTED
// =============================================================================
// CONSTRAINT AND FIELD ATTRIBUTES
// =============================================================================
// Use SPACETIMEDB_TABLE with constraint macros for field attributes:
// SPACETIMEDB_TABLE(MyTable, "my_table", Public,
// PrimaryKey(id), Unique(email), Index(name)
// )
// =============================================================================
// VISIBILITY FILTER MACRO
// =============================================================================
/**
* @brief Set module case conversion policy using a fixed preinit registration symbol.
*
* Defining this macro more than once in a module intentionally causes a duplicate symbol error.
*/
#define SPACETIMEDB_SETTING_CASE_CONVERSION(policy_enum_value) \
extern "C" __attribute__((export_name("__preinit__18_module_settings_case_conversion_policy"))) \
void __spacetimedb_preinit_case_conversion_policy() { \
SpacetimeDB::Module::SetCaseConversionPolicy(policy_enum_value); \
}
/**
* @brief Register a client visibility filter with the SpacetimeDB module system
*
* This macro registers either a raw SQL string or a query-builder value as a
* client visibility filter.
*
* @param filter_name The unique name for this filter
* @param sql_query The SQL query string or query-builder value for the filter
*
* Usage:
* SPACETIMEDB_CLIENT_VISIBILITY_FILTER(user_owns_data, "SELECT * FROM user_data WHERE owner_id = current_user_identity()")
*
* // Typed query-builder form (preferred when possible):
* SPACETIMEDB_CLIENT_VISIBILITY_FILTER(
* online_users_only,
* ::SpacetimeDB::QueryBuilder{}[user].where([](const auto& users) {
* return users.online;
* }))
*
* Note:
* This macro runs during preinit registration, not inside a view callback.
* `ctx.from` is not available here; use `QueryBuilder{}` directly.
*/
#define SPACETIMEDB_CLIENT_VISIBILITY_FILTER(filter_name, sql_query) \
__attribute__((export_name("__preinit__25_register_row_level_security_" #filter_name))) \
extern "C" void __register_client_visibility_filter_##filter_name() { \
const auto _spacetimedb_filter = ::SpacetimeDB::Filter::Sql(sql_query); \
::SpacetimeDB::Module::RegisterClientVisibilityFilter(_spacetimedb_filter.sql_text()); \
}
// =============================================================================
// STRUCT TYPE MACROS
// =============================================================================
/**
* @brief Enable BSATN serialization for unit struct types
*
* Unit structs are structs with no fields, similar to std::monostate.
* They serialize/deserialize as empty (0 bytes).
*
* Usage:
* struct UnitType {};
* SPACETIMEDB_UNIT_STRUCT(UnitType)
*/
#define SPACETIMEDB_UNIT_STRUCT(Type) \
struct Type { \
static constexpr bool __is_unit_type__ = true; \
Type() = default; \
Type(std::monostate) {} \
operator std::monostate() const { return std::monostate{}; } \
}; \
template<> \
struct SpacetimeDB::bsatn::bsatn_traits<Type> { \
static void serialize(SpacetimeDB::bsatn::Writer& w, const Type& v) { \
/* Unit struct serializes as empty */ \
} \
static Type deserialize(SpacetimeDB::bsatn::Reader& r) { \
return Type{}; \
} \
static SpacetimeDB::bsatn::AlgebraicType algebraic_type() { \
return SpacetimeDB::Internal::LazyTypeRegistrar<Type>::getOrRegister( \
[]() -> SpacetimeDB::bsatn::AlgebraicType { \
return SpacetimeDB::bsatn::AlgebraicType::make_product( \
std::make_unique<SpacetimeDB::bsatn::ProductType>(std::vector<SpacetimeDB::bsatn::ProductTypeElement>{}) \
); \
}, \
#Type \
); \
} \
}; \
SPACETIMEDB_GENERATE_TYPE_REGISTRATION_BUNDLE(Type)
/**
* @brief Enable BSATN serialization for struct types with fields
*
* Generates complete serialization support for structs by serializing
* each field in the order specified. This macro must be called after
* struct definition and before SPACETIMEDB_TABLE.
*
* @param Type The struct type name
* @param ... List of field names (not types) in the order they appear in the struct
*
* @example Basic struct with table registration:
* @code
* struct Player {
* uint32_t id;
* std::string name;
* uint8_t level;
* };
* SPACETIMEDB_STRUCT(Player, id, name, level)
* SPACETIMEDB_TABLE(Player, players, Public)
* FIELD_PrimaryKey(players, id)
* @endcode
*
* @example Struct with enum field:
* @code
* SPACETIMEDB_ENUM(GameState, Lobby, Playing, Ended)
*
* struct Match {
* uint32_t match_id;
* GameState state;
* Timestamp created_at;
* };
* SPACETIMEDB_STRUCT(Match, match_id, state, created_at)
* SPACETIMEDB_TABLE(Match, matches, Public)
* @endcode
*
* @example Struct used in reducer (no table):
* @code
* struct Vector2 {
* float x;
* float y;
* };
* SPACETIMEDB_STRUCT(Vector2, x, y)
*
* SPACETIMEDB_REDUCER(move_player, ReducerContext ctx, Vector2 delta) {
* // Use Vector2 as a parameter type
* return Ok();
* }
* @endcode
*
* @note Field order must match struct definition order
* @note All fields must support BSATN serialization
* @see SPACETIMEDB_TABLE for registering structs as database tables
* @see SPACETIMEDB_UNIT_STRUCT for zero-field structs
*/
#define SPACETIMEDB_STRUCT(Type, ...) \
template<> \
struct SpacetimeDB::bsatn::bsatn_traits<Type> { \
static void serialize(SpacetimeDB::bsatn::Writer& w, const Type& v) { \
SPACETIMEDB_SERIALIZE_FIELDS(v, w, __VA_ARGS__) \
} \
static Type deserialize(SpacetimeDB::bsatn::Reader& r) { \
Type v; \
SPACETIMEDB_DESERIALIZE_FIELDS(v, r, __VA_ARGS__) \
return v; \
} \
static SpacetimeDB::bsatn::AlgebraicType algebraic_type() { \
return SpacetimeDB::Internal::LazyTypeRegistrar<Type>::getOrRegister( \
[]() -> SpacetimeDB::bsatn::AlgebraicType { \
SpacetimeDB::bsatn::ProductTypeBuilder builder; \
SPACETIMEDB_REGISTER_FIELDS(Type, builder, __VA_ARGS__) \
return SpacetimeDB::bsatn::AlgebraicType::make_product(builder.build()); \
}, \
#Type \
); \
} \
}; \
SPACETIMEDB_QUERY_COLS_DECL(Type, __VA_ARGS__) \
SPACETIMEDB_GENERATE_TYPE_REGISTRATION_BUNDLE_WITH_FIELDS(Type, __VA_ARGS__)
// Field processing helper macros (used by SPACETIMEDB_STRUCT)
#define SPACETIMEDB_SERIALIZE_FIELD(obj, writer, field) \
SpacetimeDB::bsatn::serialize(writer, obj.field);
#define SPACETIMEDB_DESERIALIZE_FIELD(obj, reader, field) \
obj.field = SpacetimeDB::bsatn::deserialize<decltype(obj.field)>(reader);
#define SPACETIMEDB_REGISTER_FIELD(Type, builder, field) \
builder.with_field<decltype(Type::field)>(#field);
#define SPACETIMEDB_SERIALIZE_FIELDS(obj, writer, ...) \
SPACETIMEDB_FOR_EACH_ARG(SPACETIMEDB_SERIALIZE_FIELD, obj, writer, __VA_ARGS__)
#define SPACETIMEDB_DESERIALIZE_FIELDS(obj, reader, ...) \
SPACETIMEDB_FOR_EACH_ARG(SPACETIMEDB_DESERIALIZE_FIELD, obj, reader, __VA_ARGS__)
#define SPACETIMEDB_REGISTER_FIELDS(Type, builder, ...) \
SPACETIMEDB_FOR_EACH_ARG(SPACETIMEDB_REGISTER_FIELD, Type, builder, __VA_ARGS__)
#define SPACETIMEDB_QUERY_COL_DECL(Type, dummy, field) \
::SpacetimeDB::query_builder::Col<Type, decltype(std::declval<Type>().field)> field;
#define SPACETIMEDB_QUERY_COL_ASSIGN(Type, table_name, field) \
this->field = ::SpacetimeDB::query_builder::Col<Type, decltype(std::declval<Type>().field)>(table_name, #field);
#define SPACETIMEDB_QUERY_IX_COL_BASE_DECL(Type, dummy, field) \
template<typename Dummy, bool Indexed = ::SpacetimeDB::query_builder::detail::delayed_is_indexed_member_v<Dummy, Type, &Type::field>> \
struct SPACETIMEDB_PASTE(Type, SPACETIMEDB_PASTE(_query_builder_ix_base_, field)); \
template<typename Dummy> \
struct SPACETIMEDB_PASTE(Type, SPACETIMEDB_PASTE(_query_builder_ix_base_, field))<Dummy, true> { \
::SpacetimeDB::query_builder::IxCol<Type, decltype(std::declval<Type>().field)> field; \
explicit SPACETIMEDB_PASTE(Type, SPACETIMEDB_PASTE(_query_builder_ix_base_, field))(const char* table_name) \
: field(table_name, #field) {} \
}; \
template<typename Dummy> \
struct SPACETIMEDB_PASTE(Type, SPACETIMEDB_PASTE(_query_builder_ix_base_, field))<Dummy, false> { \
explicit SPACETIMEDB_PASTE(Type, SPACETIMEDB_PASTE(_query_builder_ix_base_, field))(const char*) {} \
};
#define SPACETIMEDB_QUERY_IX_COL_BASE_NAME(Type, dummy, field) \
SPACETIMEDB_PASTE(Type, SPACETIMEDB_PASTE(_query_builder_ix_base_, field))<Dummy>
#define SPACETIMEDB_QUERY_IX_COL_BASE_INIT(Type, table_name, field) \
SPACETIMEDB_PASTE(Type, SPACETIMEDB_PASTE(_query_builder_ix_base_, field))<Dummy>(table_name)
#define SPACETIMEDB_QUERY_COLS_DECL(Type, ...) \
struct SPACETIMEDB_PASTE(Type, _query_builder_cols) { \
SPACETIMEDB_FOR_EACH_ARG(SPACETIMEDB_QUERY_COL_DECL, Type, dummy, __VA_ARGS__) \
explicit SPACETIMEDB_PASTE(Type, _query_builder_cols)(const char* table_name) { \
SPACETIMEDB_FOR_EACH_ARG(SPACETIMEDB_QUERY_COL_ASSIGN, Type, table_name, __VA_ARGS__) \
} \
}; \
SPACETIMEDB_FOR_EACH_ARG(SPACETIMEDB_QUERY_IX_COL_BASE_DECL, Type, dummy, __VA_ARGS__) \
template<typename Dummy = void> \
struct SPACETIMEDB_PASTE(Type, _query_builder_ix_cols) \
: SPACETIMEDB_FOR_EACH_ARG_COMMA(SPACETIMEDB_QUERY_IX_COL_BASE_NAME, Type, dummy, __VA_ARGS__) { \
explicit SPACETIMEDB_PASTE(Type, _query_builder_ix_cols)(const char* table_name) \
: SPACETIMEDB_FOR_EACH_ARG_COMMA(SPACETIMEDB_QUERY_IX_COL_BASE_INIT, Type, table_name, __VA_ARGS__) {} \
}; \
template<> \
struct ::SpacetimeDB::query_builder::HasCols<Type> { \
static auto get(const char* table_name) { \
return SPACETIMEDB_PASTE(Type, _query_builder_cols)(table_name); \
} \
}; \
template<> \
struct ::SpacetimeDB::query_builder::HasIxCols<Type> { \
template<typename Dummy = void> \
static auto get(const char* table_name) { \
return SPACETIMEDB_PASTE(Type, _query_builder_ix_cols)<Dummy>(table_name); \
} \
};
// Field descriptor registration for runtime reflection
#define SPACETIMEDB_REGISTER_FIELD_DESCRIPTOR(Type, dummy, field) \
{ \
::SpacetimeDB::FieldDescriptor desc; \
desc.name = #field; \
desc.offset = offsetof(Type, field); \
desc.size = sizeof(decltype(Type::field)); \
desc.write_type = [](std::vector<uint8_t>& buf) { \
::SpacetimeDB::write_field_type<decltype(Type::field)>(buf); \
}; \
desc.get_algebraic_type = []() { \
return ::SpacetimeDB::bsatn::bsatn_traits<decltype(Type::field)>::algebraic_type(); \
}; \
desc.serialize = [](std::vector<uint8_t>& buf, const void* obj) { \
const Type* typed_obj = static_cast<const Type*>(obj); \
::SpacetimeDB::serialize_value(buf, typed_obj->field); \
}; \
desc.get_type_name = []() -> std::string { \
return demangle_cpp_type_name(typeid(decltype(Type::field)).name()); \
}; \
::SpacetimeDB::get_table_descriptors()[&typeid(Type)].fields.push_back(desc); \
}
#define SPACETIMEDB_REGISTER_FIELD_DESCRIPTORS(Type, ...) \
SPACETIMEDB_FOR_EACH_ARG(SPACETIMEDB_REGISTER_FIELD_DESCRIPTOR, Type, dummy, __VA_ARGS__)
/**
* @brief Define a unit type (empty struct) with BSATN serialization support
*
* Creates an empty struct and generates the necessary BSATN traits for it.
* Useful for creating unique wrapper types for unit variants in enums,
* since std::variant requires all types to be unique.
*
* This is the C++ equivalent of C#'s `[Type] public partial record UnitType { }`
*
* @param TypeName The name of the unit type to create
*
* @example Creating unit types for enum variants:
* @code
* // Define unique types for unit variants
* SPACETIMEDB_UNIT_TYPE(FooVariant)
* SPACETIMEDB_UNIT_TYPE(BarVariant)
*
* // Use in variant enum
* SPACETIMEDB_ENUM(MyEnum,
* (Foo, FooVariant),
* (Bar, BarVariant),
* (Baz, std::string)
* )
* @endcode
*/
#define SPACETIMEDB_UNIT_TYPE(TypeName) \
struct TypeName {}; \
\
namespace SpacetimeDB::bsatn { \
template<> \
struct bsatn_traits<TypeName> { \
static void serialize(Writer& writer, const TypeName&) { \
/* Unit type: serialize nothing */ \
} \
static TypeName deserialize(Reader& reader) { \
/* Unit type: deserialize nothing, return default */ \
return TypeName{}; \
} \
static AlgebraicType algebraic_type() { \
/* Unit type is a product type with no fields */ \
return AlgebraicType::Unit(); \
} \
}; \
}
#endif // SPACETIMEDB_MACROS_H