5.3.7. C++ Integration: Callbacks
This tutorial shows how C++ code can receive and invoke daslang closures. Topics covered:
TBlock<Ret, Args...>— typed block parameters (stack-bound closures)das_invoke<Ret>::invoke()— invoking a block from C++Func— function pointer parametersdas_invoke_function<Ret>::invoke()— invoking a function pointerLambda overview —
TLambda<>,das_invoke_lambda<>Practical patterns: iteration, reduction
5.3.7.1. Prerequisites
Tutorial 06 completed (tutorial_integration_cpp_interop).
Familiarity with
addExternandSideEffects.
5.3.7.2. Blocks — TBlock<Ret, Args...>
Blocks are stack-bound closures — the primary callback mechanism in daslang. They are only valid during the C++ call that receives them. Never store a block for later use.
The template TBlock<ReturnType, ArgType1, ArgType2, ...> provides
type safety: daslang’s compiler checks the argument types at compile
time.
void with_values(int32_t a, int32_t b,
const TBlock<void, int32_t, int32_t> & blk,
Context * context, LineInfoArg * at) {
das_invoke<void>::invoke(context, at, blk, a, b);
}
A predicate block returning bool:
int32_t count_matching(const TArray<int32_t> & arr,
const TBlock<bool, int32_t> & pred,
Context * context, LineInfoArg * at) {
int32_t count = 0;
for (uint32_t i = 0; i < arr.size; ++i) {
if (das_invoke<bool>::invoke(context, at, pred, arr[i])) {
count++;
}
}
return count;
}
Registration uses SideEffects::invoke because the function invokes
script code:
addExtern<DAS_BIND_FUN(count_matching)>(*this, lib, "count_matching",
SideEffects::invoke, "count_matching")
->args({"arr", "pred", "context", "at"});
5.3.7.3. Function pointers — Func
Func is a reference to a daslang-side function. Unlike blocks,
function pointers can be stored and invoked multiple times (within the
same context). In daslang, @@function_name creates a Func
value.
void call_function_twice(Func fn, int32_t value,
Context * context, LineInfoArg * at) {
das_invoke_function<void>::invoke(context, at, fn, value);
das_invoke_function<void>::invoke(context, at, fn, value * 2);
}
5.3.7.4. Lambdas — Lambda / TLambda<>
Lambdas are heap-allocated closures that can capture variables.
TLambda<Ret, Args...> is the typed variant (analogous to TBlock
and TFunc). Invoke with das_invoke_lambda<Ret>::invoke().
Note
The untyped Lambda maps to lambda<> in daslang and will not
match typed lambdas like lambda<(x:int):int>. Use
TLambda<Ret, Args...> for type-safe lambda acceptance.
5.3.7.5. Practical pattern: C++ iterates, script processes
A common embedding pattern is having C++ own the iteration/generation logic while the script provides the processing callback:
void for_each_fibonacci(int32_t count,
const TBlock<void, int32_t, int32_t> & blk,
Context * context, LineInfoArg * at) {
int32_t a = 0, b = 1;
for (int32_t i = 0; i < count; ++i) {
das_invoke<void>::invoke(context, at, blk, i, a);
int32_t next = a + b;
a = b;
b = next;
}
}
And the accumulator/reduce pattern:
int32_t reduce_range(int32_t from, int32_t to, int32_t init,
const TBlock<int32_t, int32_t, int32_t> & blk,
Context * context, LineInfoArg * at) {
int32_t acc = init;
for (int32_t i = from; i < to; ++i) {
acc = das_invoke<int32_t>::invoke(context, at, blk, acc, i);
}
return acc;
}
5.3.7.6. Calling from daslang
Blocks use the <| pipe syntax with $() lambda prefix.
Function pointers use @@function_name:
options gen2
require tutorial_07_cpp
def print_value(x : int) {
print("fn({x})\n")
}
[export]
def test() {
// Block callback
with_values(10, 20) $(a, b) {
print("a + b = {a + b}\n")
}
// Function pointer
call_function_twice(@@print_value, 5)
// Fibonacci iteration
for_each_fibonacci(5) $(index, value) {
print("fib({index}) = {value}\n")
}
// Reduce — sum 1..10
let total = reduce_range(1, 11, 0) $(acc, i) {
return acc + i
}
print("sum = {total}\n")
// Reduce — factorial 10
let fact = reduce_range(1, 11, 1) $(acc, i) {
return acc * i
}
print("10! = {fact}\n")
5.3.7.7. Building and running
cmake --build build --config Release --target integration_cpp_07
bin\Release\integration_cpp_07.exe
Expected output:
=== Block: with_values ===
a + b = 30
=== Block: count_matching ===
even numbers: 5
=== Function pointer: call_function_twice ===
fn(5)
fn(10)
=== Practical: for_each_fibonacci ===
fib(0) = 0
fib(1) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
=== Practical: reduce_range (sum 1..10) ===
sum = 55
=== Practical: reduce_range (factorial 10) ===
10! = 3628800
See also
Full source:
07_callbacks.cpp,
07_callbacks.das
Previous tutorial: tutorial_integration_cpp_interop
Next tutorial: tutorial_integration_cpp_methods