C API: split initialization and add explicit-length strings - #3545
Conversation
Expose C-linkage static module registration, split runtime initialization around user module registration, and add size_t-based string and serialization entry points while retaining the legacy API. Add C/StbImage integration coverage, cross-translation-unit module pull coverage, and update the embedding documentation.
There was a problem hiding this comment.
Pull request overview
This PR updates daslang’s embedding surface (C and C++) to better support statically linked modules and non-null-terminated string slices. It splits C API initialization into a register-then-finalize sequence, exposes/consumes unmangled register_Module_* entry points (C linkage), and adds size_t-based explicit-length (*_n) variants for text and serialization APIs while preserving legacy entry points with checked narrowing.
Changes:
- Split C initialization into
das_initialize_modules()+das_initialize_finalize()(withdas_initialize()preserved as the one-call path). - Switch static module pulls to a declare/pull pair (
external_declare.inc+external_pull.inc) and makeregister_Module_*entry points C-linkage. - Add explicit-length C APIs for strings and byte ranges (
*_n) and extend docs/tests/tutorials to cover them (including a static StbImage C integration tutorial).
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| utils/daslang-live/main.cpp | Adds static-module declarations include and switches to external_pull.inc for static pulls. |
| utils/dasFormatter/main.cpp | Adds static-module declarations include and switches to external_pull.inc for static pulls. |
| utils/daScript/main.cpp | Adds static-module declarations include and switches to external_pull.inc for static pulls. |
| tutorials/integration/cpp/22_namespace_integration.cpp | Updates tutorial code/comments to reflect C-linkage registration entry points and modern macro usage. |
| tutorials/integration/c/CMakeLists.txt | Adds tutorial 15 target for static StbImage module integration (conditional on module availability). |
| tutorials/integration/c/CMakeLists.standalone.cmake | Adds standalone-SDK build wiring for tutorial 15 when StbImage is present. |
| tutorials/integration/c/15_static_stbimage.c | New C tutorial demonstrating split init + explicit-length C APIs with a static StbImage example. |
| tests-cpp/small/test_capi_explicit_length.cpp | New doctest covering explicit-length C APIs with non-null-terminated inputs. |
| tests-cpp/big/capi_split_init/test_cpp_module_pull.cpp | New C++ smoke test for namespaced/static module pulls via global C-linkage entry points. |
| tests-cpp/big/capi_split_init/test_capi_split_init.c | New pure-C smoke test for split init + static module registration. |
| tests-cpp/big/capi_split_init/CMakeLists.txt | Adds the new big tests to the build/ctest registry. |
| src/misc/daScriptC.cpp | Implements split-init and new *_n APIs with range validation and checked narrowing in legacy wrappers. |
| include/daScript/daScriptModule.h | Declares built-in register_Module_* entry points with C linkage and adjusts module pull macros accordingly. |
| include/daScript/daScriptC.h | Adds size_t-based explicit-length (*_n) C API entry points and splits initialization API. |
| include/daScript/ast/ast.h | Makes REGISTER_MODULE*-generated registration functions use C linkage for unmangled symbols. |
| examples/pathTracer/path_tracer.cpp | Switches static module inclusion to external_declare.inc + external_pull.inc. |
| doc/source/reference/tutorials/integration_cpp_22_namespace_integration.rst | Updates docs to match the new C-linkage registration and module pull guidance. |
| doc/source/reference/tutorials/integration_c_15_static_stbimage.rst | New tutorial doc for static StbImage C integration + explicit-length APIs. |
| doc/source/reference/tutorials/integration_c_14_passing_arrays.rst | Links to the new tutorial 15 as the next tutorial. |
| doc/source/reference/tutorials/building_from_sdk.rst | Updates SDK tutorial count and notes conditional tutorial 15 availability. |
| doc/source/reference/tutorials.rst | Adds tutorial 15 to the C integration tutorial index and clarifies tutorial source layout. |
| doc/source/reference/embedding/external_modules.rst | Updates embedding docs for external_declare.inc/external_pull.inc generation and usage. |
| doc/source/reference/embedding/cpp_api.rst | Updates C++ embedding guidance for DECLARE_MODULE and the generated .inc files. |
| doc/source/reference/embedding/c_api.rst | Documents split init and explicit-length (*_n) C APIs across the embedding surface. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (3)
src/misc/daScriptC.cpp:348
- das_fileaccess_introduce_file_from_disk_n() builds std::string ranges but then passes .c_str() into FsFileAccess::introduceFileFromDisk (which takes const std::string&). This adds an extra strlen and can truncate embedded \0, undermining the explicit-length API.
auto name_string = string_from_range(name, name_length, "name");
auto disk_path_string = string_from_range(disk_path, disk_path_length, "disk_path");
((FsFileAccess *) access)->introduceFileFromDisk(name_string.c_str(), disk_path_string.c_str());
src/misc/daScriptC.cpp:366
- das_fileaccess_introduce_native_module_n() converts req_string back to a C string even though FsFileAccess::introduceNativeModule takes const std::string&. This reintroduces strlen and can truncate explicit-length inputs containing embedded \0.
int das_fileaccess_introduce_native_module_n ( das_file_access * access, const char * req, size_t req_length ) {
auto req_string = string_from_range(req, req_length, "req");
return ((FsFileAccess *) access)->introduceNativeModule(req_string.c_str()) ? 1 : 0;
}
src/misc/daScriptC.cpp:651
- das_module_find_n() converts the explicit-length name range back into a null-terminated C string before calling Module::require(const std::string&). This adds an extra strlen and can truncate embedded \0, which defeats the explicit-length contract.
das_module * das_module_find_n ( const char * name, size_t name_length ) {
auto name_string = string_from_range(name, name_length, "name");
return (das_module *) Module::require(name_string.c_str());
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
src/misc/daScriptC.cpp:966
das_context_find_variable_nhas the same embedded-\0truncation issue asdas_context_find_function_n: it accepts an explicit-length range but then callsContext::findVariable(name_string.c_str()), so any NUL byte before the end of the range changes the lookup key silently.
To keep the _n contract predictable, reject embedded NULs (optionally permitting a trailing terminator) before calling into the C-string-only runtime API.
int das_context_find_variable_n ( das_context * context, const char * name, size_t name_length ) {
auto name_string = string_from_range(name, name_length, "name");
return ((Context *)context)->findVariable(name_string.c_str());
}
examples/pathTracer/path_tracer.cpp:67
modules/external_pull.incis included unconditionally, but in configurations that rely on DLL/dynamic includes, static-module pull files may not be available or appropriate. Other updated hosts in this PR gate static pulls with!DAS_ENABLE_DLL || !DAS_ENABLE_DYN_INCLUDES.
Guard this include similarly so the example doesn't fail to compile when built with dynamic module configuration.
// request all da-script built in modules
NEED_ALL_DEFAULT_MODULES;
// request external modules
#include "modules/external_pull.inc"
// add job-que
Summary
register_Module_*entry points and make C++ module pulls use those real symbolssize_t-based explicit-length (_n) string and serialization APIs while preserving legacy entry points with checked narrowingTesting
daslang utils/preflight/main.das -- --full— 12 passed, 0 failed (full interpreter and JIT sweeps, C++ tests, docs, formatting, generated-code checks)cmake --build build --config Release --target test_aot dasModuleGlfw dasModuleLiveHost dasModuleHV dasModuleAudio dasModulePUGIXML dasModuleStbImagectest --test-dir build -C Release -L small --output-on-failure— 79/79 passedctest --test-dir build -C Release -R ^(cpp_module_pull|capi_split_init)$ --output-on-failure— 2/2 passedintegration_c_15— static StbImage integration succeeded (114)