8.3.15. C Integration: Static StbImage Module

This tutorial demonstrates how a C host statically links an additional native module and registers it before daScript finalizes module dependencies. It uses StbImage and Raster as a concrete end-to-end example: an embedded script creates an RGBA image through stbimage_boost and resizes it through Raster.

The host also uses the explicit-length *_n C APIs throughout. These APIs map directly to languages such as D, whose strings are pointer/length slices and do not need to be null-terminated.

8.3.15.1. Why initialization is split

das_initialize() remains the convenient one-call API for hosts which only need the built-in modules. A host with statically linked application modules needs an insertion point between registering the built-ins and resolving module dependencies:

das_initialize_modules();

register_Module_Raster();
register_Module_StbImage();

das_initialize_finalize();

The three phases are:

  1. das_initialize_modules() registers daScript’s built-in modules.

  2. The host calls every statically linked register_Module_* entry point.

  3. das_initialize_finalize() initializes the complete dependency graph.

Do not call das_initialize() as well: it performs phases 1 and 3 itself.

8.3.15.2. C module entry points

Module registration functions have C linkage and therefore expose undecorated symbol names. A C host declares only the opaque C API return type:

extern das_module * register_Module_Raster(void);
extern das_module * register_Module_StbImage(void);

The equivalent declarations in D use extern(C):

extern(C) das_module* register_Module_Raster();
extern(C) das_module* register_Module_StbImage();

The application must link libDasModuleStbImage. That library contains the Raster, StbImage, and StbTrueType registration functions.

8.3.15.3. Native module versus its daslang wrapper

Calling register_Module_StbImage makes the native functions available to the compiler. The higher-level stbimage/stbimage_boost.das wrapper is still a source module and must be visible through the compilation’s das_file_access:

static const char BOOST_MODULE[] = "stbimage/stbimage_boost";

das_file_access * files = das_fileaccess_make_default();
if (!das_fileaccess_introduce_native_module_n(
        files, BOOST_MODULE, sizeof(BOOST_MODULE) - 1)) {
    /* The SDK's StbImage source module was not found. */
}

This distinction is important:

  • register_Module_StbImage registers the statically linked C++ module.

  • das_fileaccess_introduce_native_module_n introduces its installed daslang wrapper into this file access.

8.3.15.4. The embedded daslang script

The script creates a 2x2 four-channel image, resizes it to 1x1, and returns a value encoding the resulting dimensions and channel count:

options gen2
require stbimage/stbimage_boost

[export]
def main : int {
    var image = make_image(2, 2, 4)
    var resized <- image.resize(1, 1)
    return resized.width * 100 + resized.height * 10 + resized.channels
}

make_image exercises the StbImage wrapper. Image.resize reaches the native Raster resize implementation. The expected return value is 114.

8.3.15.5. Explicit-length source and names

The host passes every string as a pointer and a size_t length. No strlen call and no narrowing to uint32_t is required:

static const char SCRIPT_NAME[] = "stbimage_from_c.das";
static const char SCRIPT_SOURCE[] =
    "options gen2\n"
    "require stbimage/stbimage_boost\n"
    /* ... */;

das_fileaccess_introduce_file_n(
    files,
    SCRIPT_NAME, sizeof(SCRIPT_NAME) - 1,
    SCRIPT_SOURCE, sizeof(SCRIPT_SOURCE) - 1,
    0);

das_program * program = das_program_compile_n(
    SCRIPT_NAME, sizeof(SCRIPT_NAME) - 1,
    files, output, modules);

The same pattern is used for das_context_find_function_n. Existing null-terminated APIs remain available for compatibility.

8.3.15.6. Calling the result

After compilation and simulation, the host locates and invokes the exported main function through the regular C evaluation API:

static const char MAIN_FUNCTION[] = "main";

das_function * main_function = das_context_find_function_n(
    context, MAIN_FUNCTION, sizeof(MAIN_FUNCTION) - 1);
vec4f result = das_context_eval_with_catch(context, main_function, NULL);

if (!das_context_get_exception(context)) {
    int value = das_argument_int(result);  /* 114 */
}

Release the context, program, file access, module group, and output writer before calling das_shutdown().

8.3.15.7. Static CMake linkage

The in-tree tutorial links the static module before the static compiler library. CMake carries the module’s transitive runtime dependencies:

target_link_libraries(integration_c_15 PRIVATE
    libDasModuleStbImage libDaScript Threads::Threads)

An installed SDK exposes the corresponding imported targets as DAS::libDasModuleStbImage and DAS::libDaScript. The standalone tutorial build enables this target when the SDK was installed with StbImage support.

8.3.15.8. Build & run

Build:

cmake --build build --config Release --target integration_c_15

Run from the project root:

bin/Release/integration_c_15

Expected output:

static StbImage integration succeeded: 114

See also

Full source: 15_static_stbimage.c

Previous tutorial: C Integration: Passing Arrays

C API reference: C API Reference

Native and shared module details: External Modules

daScriptC.h API header: include/daScript/daScriptC.h