5.3.2. C++ Integration: Calling Functions
This tutorial shows two ways to call daslang functions from C++:
Part A — Low-level: manual
cast<T>::from/to+evalWithCatch. Gives maximum control over the calling convention.Part B — High-level:
das_invoke_function/das_invoke_function_by_name. Type-safe variadic templates that handle marshalling and AOT dispatch automatically. Prefer this in production code.
Note
All argument and return value marshalling goes through vec4f, the
SIMD-aligned register type that daslang uses for its calling convention.
Part A works with vec4f directly; Part B hides it behind templates.
5.3.2.1. Prerequisites
Tutorial 01 completed (tutorial_integration_cpp_hello_world).
Familiarity with the daslang lifecycle (init → compile → simulate → eval → shutdown).
5.3.2.2. The daslang file
The script exports several functions with different signatures:
options gen2
[export]
def add(a : int; b : int) : int {
return a + b
}
[export]
def square(x : float) : float {
return x * x
}
[export]
def greet(name : string) : string {
return "Hello, {name}!"
}
struct Vec2 {
x : float
y : float
}
[export]
def make_vec2(x : float; y : float) : Vec2 {
return Vec2(x = x, y = y)
}
[export]
def will_fail() {
panic("something went wrong!")
}
5.3.2.3. Part A — Low-level calling
5.3.2.3.1. Passing arguments with cast<T>::from
Every daslang function takes arguments as an array of vec4f. Use
cast<T>::from(value) to pack a C++ value into a vec4f slot:
vec4f args[2];
args[0] = cast<int32_t>::from(17); // int
args[1] = cast<int32_t>::from(25); // int
vec4f ret = ctx.evalWithCatch(fnAdd, args);
The supported primitive types are:
C++ type |
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pointers |
|
5.3.2.3.2. Reading return values with cast<T>::to
The return value of evalWithCatch is also a vec4f. Extract it with
cast<T>::to(ret):
int32_t result = cast<int32_t>::to(ret);
For strings, the returned char * points into the context’s heap and
remains valid until the context is destroyed or garbage-collected:
const char * result = cast<char *>::to(ret);
5.3.2.3.3. Verifying function signatures
verifyCall<ReturnType, ArgTypes...> checks a SimFunction’s debug info
against the expected C++ types:
if (!verifyCall<int32_t, int32_t, int32_t>(fnAdd->debugInfo, dummyLibGroup)) {
tout << "'add' has wrong signature\n";
return;
}
This is a development-time safety net — it walks debug info and is slow, so call it once during setup, never in a hot loop.
5.3.2.3.4. Functions returning structs (cmres)
When a daslang function returns a struct, the caller supplies a result
buffer. Use the three-argument form of evalWithCatch:
// C++ struct layout must exactly match the daslang struct.
struct Vec2 { float x; float y; };
Vec2 v;
ctx.evalWithCatch(fnMakeVec2, args, &v);
The third argument (void * cmres) is where the result is written.
5.3.2.3.5. Handling exceptions
If the script calls panic(), evalWithCatch catches it. Check with
getException():
ctx.evalWithCatch(fnFail, nullptr);
if (auto ex = ctx.getException()) {
printf("Caught: %s\n", ex);
}
5.3.2.4. Part B — High-level calling
das_invoke_function (from daScript/simulate/aot.h) is a family of
variadic templates that handle argument marshalling, AOT dispatch, and
return-value extraction in a single call. This is the recommended way
to call daslang functions from C++.
Include the header:
#include "daScript/simulate/aot.h"
5.3.2.4.1. Obtaining a Func handle
The Func struct (daScript/misc/arraytype.h) is a lightweight wrapper
around SimFunction *. Convert a found SimFunction * into a Func:
auto fnAdd = ctx.findFunction("add");
Func add_func(fnAdd);
Func is small and copyable — pass it by value.
5.3.2.4.2. Calling with invoke
For functions returning scalars or strings, use
das_invoke_function<RetType>::invoke:
int32_t result = das_invoke_function<int32_t>::invoke(&ctx, nullptr,
add_func, 17, 25);
// result == 42
float sq = das_invoke_function<float>::invoke(&ctx, nullptr,
square_func, 3.5f);
// sq == 12.25
char * greeting = das_invoke_function<char *>::invoke(&ctx, nullptr,
greet_func, "World");
// greeting == "Hello, World!"
The second argument (LineInfo *) can be nullptr in most cases.
It is used for error reporting and stack traces.
5.3.2.4.3. Calling with invoke_cmres
When a daslang function returns a struct, use invoke_cmres instead:
Vec2 v = das_invoke_function<Vec2>::invoke_cmres(&ctx, nullptr,
make_vec2_func, 1.5f, 2.5f);
// v.x == 1.5, v.y == 2.5
This allocates the result buffer on the stack and passes it as the caller-managed result pointer.
5.3.2.4.4. Calling by name
das_invoke_function_by_name looks up the function by name at call time.
It validates that exactly one function matches and that it is not private:
int32_t result = das_invoke_function_by_name<int32_t>::invoke(
&ctx, nullptr, "add", 100, 200);
// result == 300
This is convenient for one-off calls but slower than using a Func
handle. For functions called repeatedly, cache the Func instead.
5.3.2.5. Part A vs Part B comparison
Aspect |
Part A (cast + evalWithCatch) |
Part B (das_invoke_function) |
|---|---|---|
Header |
|
|
Argument marshalling |
Manual |
Automatic (variadic templates) |
Return value extraction |
Manual |
Automatic (template parameter) |
Struct returns |
|
|
AOT dispatch |
No |
Yes |
Exception handling |
|
Throws C++ exception |
Best for |
Learning / edge cases |
Production code |
5.3.2.6. Building and running
cmake --build build --config Release --target integration_cpp_02
bin\Release\integration_cpp_02.exe
Expected output:
=== Part A: Low-level (cast + evalWithCatch) ===
add(17, 25) = 42
square(3.5) = 12.25
greet("World") = "Hello, World!"
int=42 float=3.14 string=test bool=true
make_vec2(1.5, 2.5) = { x=1.5, y=2.5 }
Caught expected exception: something went wrong!
=== Part B: High-level (das_invoke_function) ===
add(17, 25) = 42
square(3.5) = 12.25
greet("World") = "Hello, World!"
int=42 float=3.14 string=test bool=true
make_vec2(1.5, 2.5) = { x=1.5, y=2.5 }
add(100, 200) by name = 300
See also
Full source:
02_calling_functions.cpp,
02_calling_functions.das
Previous tutorial: tutorial_integration_cpp_hello_world
Next tutorial: tutorial_integration_cpp_binding_functions
C API equivalent: tutorial_integration_c_calling_functions