5.3.3. C++ Integration: Binding Functions

This tutorial shows how to expose C++ functions to daslang scripts by creating a custom module. Topics covered:

  • addExtern with DAS_BIND_FUN — binding free C++ functions

  • SideEffects flags — telling the optimizer what a function does

  • addConstant — exposing compile-time constants

  • Context-aware functions — receiving Context * automatically

5.3.3.1. Prerequisites

  • Tutorial 02 completed (tutorial_integration_cpp_calling_functions).

  • Understanding of cast<T> and the daslang calling convention.

5.3.3.2. Creating a module

A module is a class derived from Module. Its constructor registers types, functions, and constants that scripts can use via require:

class Module_Tutorial03 : public Module {
public:
    Module_Tutorial03() : Module("tutorial_03_cpp") {
        ModuleLibrary lib(this);
        lib.addBuiltInModule();
        // ... register functions and constants here ...
    }
};

REGISTER_MODULE(Module_Tutorial03);

REGISTER_MODULE makes the module available via NEED_MODULE in the host program.

5.3.3.3. Binding constants

addConstant exposes a C++ value as a compile-time constant:

addConstant(*this, "PI",    3.14159265358979323846f);
addConstant(*this, "SQRT2", sqrtf(2.0f));

In the script:

print("PI = {PI}\n")       // 3.1415927
print("SQRT2 = {SQRT2}\n") // 1.4142135

5.3.3.4. Binding functions with addExtern

addExtern is the primary way to expose a C++ function to daslang. The DAS_BIND_FUN macro generates the template machinery needed for automatic argument marshalling:

float xmadd(float a, float b, float c, float d) {
    return a * b + c * d;
}

addExtern<DAS_BIND_FUN(xmadd)>(*this, lib, "xmadd",
    SideEffects::none, "xmadd");

Parameters:

  1. DAS_BIND_FUN(xmadd) — the C++ function to bind

  2. *this — the module being populated

  3. lib — the module library (for type resolution)

  4. "xmadd" — the name visible in daslang

  5. SideEffects::none — optimizer hint (see below)

  6. "xmadd" — C++ function name for AOT (used in generated code)

5.3.3.5. SideEffects flags

The SideEffects enum tells the optimizer what observable effects a function has. This controls whether calls can be eliminated, reordered, or folded:

Flag

Meaning

none

Pure function — no side effects. Safe to eliminate if result is unused.

modifyExternal

Modifies external state (stdout, files, hardware, etc.)

modifyArgument

Modifies one or more of its arguments (passed by reference).

accessGlobal

Reads global/shared mutable state.

invoke

Calls a daslang function or lambda.

worstDefault

Combines modifyArgument | modifyExternal. Use when unsure.

Example — a function that prints to stdout needs modifyExternal:

void greet(const char * name) {
    printf("Hello from C++, %s!\n", name);
}

addExtern<DAS_BIND_FUN(greet)>(*this, lib, "greet",
    SideEffects::modifyExternal, "greet");

5.3.3.6. Functions that modify arguments

If a function takes a reference and modifies it, use SideEffects::modifyArgument:

void double_it(int32_t & value) {
    value *= 2;
}

addExtern<DAS_BIND_FUN(double_it)>(*this, lib, "double_it",
    SideEffects::modifyArgument, "double_it");

In the script:

var val = 21
double_it(val)
print("{val}\n")    // 42

5.3.3.7. Context-aware functions

If a C++ function takes Context * as its first parameter (or LineInfoArg * as its last), daslang injects them automatically — the script caller does not see these parameters:

void print_stack_info(Context * ctx) {
    printf("Stack size: %d bytes\n", ctx->stack.size());
}

addExtern<DAS_BIND_FUN(print_stack_info)>(*this, lib, "print_stack_info",
    SideEffects::modifyExternal, "print_stack_info");

In the script the function takes zero arguments:

print_stack_info()   // prints "Stack size: 16384 bytes"

5.3.3.8. Activating the module in the host

The host program must request the module before Module::Initialize():

int main(int, char * []) {
    NEED_ALL_DEFAULT_MODULES;
    NEED_MODULE(Module_Tutorial03);
    Module::Initialize();
    // ... compile and run scripts ...
    Module::Shutdown();
    return 0;
}

The script uses require to access the module:

require tutorial_03_cpp

5.3.3.9. Building and running

cmake --build build --config Release --target integration_cpp_03
bin\Release\integration_cpp_03.exe

Expected output:

PI    = 3.1415927
SQRT2 = 1.4142135
xmadd(SQRT2, SQRT2, 1.0, 1.0) = 3
factorial(10) = 3628800
Hello from C++, daslang!
double_it(21) = 42
Context stack size: 16384 bytes

See also

Full source: 03_binding_functions.cpp, 03_binding_functions.das

Previous tutorial: tutorial_integration_cpp_calling_functions

Next tutorial: tutorial_integration_cpp_binding_types

C API equivalent: tutorial_integration_c_binding_types (the C tutorials combine type and function binding in a single tutorial)