4.2. C++ API Reference
This page is the comprehensive reference for binding C++ code to daslang. For step-by-step tutorials with compilable examples, see the C++ integration tutorials.
4.2.1. Creating a module
Derive from Module, register bindings in the constructor, and use
REGISTER_MODULE:
class Module_MyMod : public Module {
public:
Module_MyMod() : Module("my_module_name") {
ModuleLibrary lib(this);
lib.addBuiltInModule();
// addAnnotation, addExtern, addEnumeration, addConstant ...
}
};
REGISTER_MODULE(Module_MyMod);
The host uses NEED_MODULE(Module_MyMod) before Module::Initialize().
Scripts access it with require my_module_name.
See tutorial_integration_cpp_custom_modules for a complete example.
4.2.1.1. ModuleAotType
Three AOT compilation modes for modules:
no_aotModule functions are never AOT-compiled. Used for dynamic or debug-only modules.
hybridFunctions use full ABI (slower calls, but no semantic hash dependency). Required when the module may be compiled separately from the script.
cppFull AOT — function calls use semantic hashes for dispatch. Default and fastest mode.
4.2.1.2. Embedding daslang source in modules
Use the XDD mechanism (CMAKE_TEXT_XXD CMake macro) to embed .das
files as C++ byte arrays, then compile them with compileBuiltinModule:
#include "my_module.das.inc"
compileBuiltinModule("my_module.das",
my_module_das, sizeof(my_module_das));
See tutorial_integration_cpp_class_adapters for a full example.
4.2.1.3. Builtin module constants
addConstant(*this, "MY_CONST", 42);
The type is inferred automatically from the C++ value.
4.2.2. Binding C++ types
To expose a C++ type to daslang, two things are needed:
MAKE_TYPE_FACTORY(DasName, CppType)at file scope — createstypeFactory<CppType>andtypeName<CppType>A
TypeAnnotationsubclass — describes fields, properties, and behavior
4.2.2.1. ManagedStructureAnnotation
The most common helper for binding C++ structs (POD or non-POD):
struct Object {
float3 pos;
float3 vel;
float mass;
float length() const { return sqrt(pos.x*pos.x + pos.y*pos.y); }
};
MAKE_TYPE_FACTORY(Object, Object)
struct ObjectAnnotation
: ManagedStructureAnnotation<Object, true /* canNew */> {
ObjectAnnotation(ModuleLibrary & ml)
: ManagedStructureAnnotation("Object", ml) {
addField<DAS_BIND_MANAGED_FIELD(pos)>("pos", "pos");
addField<DAS_BIND_MANAGED_FIELD(vel)>("vel", "vel");
addField<DAS_BIND_MANAGED_FIELD(mass)>("mass", "mass");
addProperty<DAS_BIND_MANAGED_PROP(length)>(
"length", "length");
}
};
// In module constructor:
addAnnotation(make_smart<ObjectAnnotation>(lib));
Key points:
Field binding order matters — if type B contains type A, register A’s annotation first.
Properties look like fields in daslang (
obj.length) but call C++ methods.addFieldExprovides custom offsets and type overrides.addPropertyExtConsthandles const/non-const overloads.Auto-implements
walkfor data inspection.
Handled types are reference types in daslang — mutable local
variables (var) require unsafe blocks. Provide factory functions
(make_xxx()) returning by value so scripts can use let x = make_xxx()
without unsafe.
See tutorial_integration_cpp_binding_types for a complete example.
4.2.2.2. DummyTypeAnnotation
Exposes an opaque type with no accessible fields:
addAnnotation(make_smart<DummyTypeAnnotation>(
"OpaqueHandle", "OpaqueHandle", sizeof(OpaqueHandle),
alignof(OpaqueHandle)));
Use case: passing handles through daslang without exposing internals.
4.2.2.3. ManagedVectorAnnotation
Exposes std::vector<T> with automatic push, pop, clear,
resize, length, and iteration support:
addAnnotation(make_smart<ManagedVectorAnnotation<int32_t>>(
"IntVector", lib));
4.2.2.4. ManagedValueAnnotation
For POD types passed by value (must fit in 128 bits). Requires
cast<T> specialization.
4.2.2.5. TypeAnnotation virtual methods
For full control, subclass TypeAnnotation directly. Key methods:
Capability queries:
canAot, canCopy, canMove, canClone, isPod,
isRawPod, isRefType, isLocal, canNew, canDelete,
canDeletePtr, needDelete, isIndexable, isIterable,
isShareable, isSmart, canSubstitute
Size and alignment:
getSizeOf, getAlignOf
Field access:
makeFieldType, makeSafeFieldType
Indexing and iteration:
makeIndexType, makeIteratorType
Simulation:
simulateDelete, simulateDeletePtr, simulateCopy,
simulateClone, simulateRef2Value, simulateGetNew,
simulateGetAt, simulateGetAtR2V, simulateGetIterator
AOT visitors:
aotPreVisitGetField, aotVisitGetField,
aotPreVisitGetFieldPtr, aotVisitGetFieldPtr
4.2.3. Cast infrastructure
cast<T> converts between vec4f (daslang’s universal register)
and C++ types:
// Value types (≤128 bits)
vec4f v = cast<int32_t>::from(42);
int32_t i = cast<int32_t>::to(v);
// Reference types (pointer packed into vec4f)
vec4f v = cast<MyObj &>::from(obj);
MyObj & ref = cast<MyObj &>::to(v);
MAKE_TYPE_FACTORY(DasName, CppType) creates the typeFactory and
typeName specializations needed by the binding system:
MAKE_TYPE_FACTORY(Point3, float3)
This creates typeFactory<float3>::make() (returns TypeDeclPtr)
and typeName<float3>() (returns "Point3").
Type aliases — to expose Point3 as an alias for float3,
provide a custom typeFactory<Point3> specialization that returns
the existing float3 type declaration.
4.2.4. Binding C++ functions
4.2.4.1. addExtern + DAS_BIND_FUN
int32_t add(int32_t a, int32_t b) { return a + b; }
addExtern<DAS_BIND_FUN(add)>(*this, lib, "add",
SideEffects::none, "add")
->args({"a", "b"});
Return by value (> 128 bits) — use SimNode_ExtFuncCallAndCopyOrMove:
addExtern<DAS_BIND_FUN(make_matrix),
SimNode_ExtFuncCallAndCopyOrMove>(
*this, lib, "make_matrix",
SideEffects::none, "make_matrix");
Return by reference — use SimNode_ExtFuncCallRef:
addExtern<DAS_BIND_FUN(get_ref),
SimNode_ExtFuncCallRef>(
*this, lib, "get_ref",
SideEffects::modifyExternal, "get_ref");
See tutorial_integration_cpp_binding_functions for a complete example.
4.2.4.2. Binding C++ methods
daslang has no member functions — methods are free functions where the
first argument is self. Pipe syntax (obj |> method()) provides
method-call ergonomics:
using method_get = DAS_CALL_MEMBER(Counter::get);
addExtern<DAS_CALL_METHOD(method_get)>(*this, lib, "get",
SideEffects::none,
DAS_CALL_MEMBER_CPP(Counter::get))
->args({"self"});
Non-const methods:
SideEffects::modifyArgumentConst methods:
SideEffects::none
See tutorial_integration_cpp_methods for details.
4.2.4.3. Binding operators
Register functions with the operator symbol as the daslang name:
addExtern<DAS_BIND_FUN(vec3_add),
SimNode_ExtFuncCallAndCopyOrMove>(
*this, lib, "+",
SideEffects::none, "vec3_add")
->args({"a", "b"});
Available operator names: +, -, *, /, %, <<,
>>, <, >, <=, >=, &, |, ^.
addEquNeq<T>(*this, lib) binds both == and !=.
See tutorial_integration_cpp_operators_and_properties.
4.2.4.4. addInterop — low-level binding
addInterop provides raw access to simulation-level arguments
(vec4f *) and call metadata (SimNode_CallBase *). When a
template parameter is vec4f, it means “any daslang type”:
vec4f my_interop(Context & ctx, SimNode_CallBase * call,
vec4f * args) {
TypeInfo * ti = call->types[0]; // type of first arg
// inspect ti->type, ti->structType, etc.
return v_zero();
}
addInterop<my_interop, void, vec4f>(*this, lib, "my_func",
SideEffects::none, "my_interop");
Key capabilities (vs addExtern):
Access to
call->types[]— per-argumentTypeInfoAccess to
call->debugInfo— source location of call sitevec4fargument type = “any” — accepts any daslang type
TypeInfo union warning: TypeInfo has a union —
structType, enumType, and annotation_or_name share memory.
Which member is valid depends on ti->type:
tStructure→ti->structTypetEnumeration/tEnumeration8/tEnumeration16→ti->enumTypetHandle→ti->getAnnotation()
See tutorial_integration_cpp_interop.
4.2.5. Binding C++ enumerations
// MUST come BEFORE `using namespace das`
DAS_BASE_BIND_ENUM(CppEnum, DasName, Value1, Value2, Value3)
using namespace das;
// In module constructor:
addEnumeration(make_smart<EnumerationDasName>());
DAS_BASE_BIND_ENUMcreatesEnumerationDasNameclass +typeFactory<CppEnum>DAS_BASE_BIND_ENUM_98— for unscoped (C-style) enumsPlace enum macros before
using namespace dasto avoid name collisions
See tutorial_integration_cpp_binding_enums.
4.2.6. Function side effects
Every bound function declares its side effects:
SideEffects::nonePure function — no external state changes.
SideEffects::unsafeFunction is unsafe.
SideEffects::userScenarioUser-defined scenario.
SideEffects::modifyExternalModifies external state (stdout, files, globals).
SideEffects::accessExternalReads external state.
SideEffects::modifyArgumentMutates a reference argument.
SideEffects::accessGlobalReads shared global state.
SideEffects::invokeCalls daslang callbacks (blocks, functions, lambdas).
SideEffects::worstDefaultSafe fallback — assumes all side effects.
4.2.7. Callbacks — blocks, functions, lambdas
Three closure types exist for calling daslang code from C++:
Type |
Template |
Invocation |
Lifetime |
|---|---|---|---|
Block |
|
|
Stack-bound (current call only) |
Function |
|
|
Context-bound (storable) |
Lambda |
|
|
Heap-allocated (captures variables) |
Block callback example:
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);
}
addExtern<DAS_BIND_FUN(with_values)>(*this, lib, "with_values",
SideEffects::invoke, "with_values")
->args({"a", "b", "blk", "context", "at"});
Use SideEffects::invoke for any function that invokes script callbacks.
See tutorial_integration_cpp_callbacks.
4.2.8. File access
FsFileAccess is the default file system access layer. Override
getNewFileInfo and getModuleInfo for custom file resolution:
struct ModuleInfo {
string moduleName;
string fileName;
string importDir;
};
setFileInfo registers virtual files (strings as files):
auto fAccess = make_smart<FsFileAccess>();
auto fileInfo = make_unique<TextFileInfo>(
text.c_str(), uint32_t(text.length()), false);
fAccess->setFileInfo("virtual.das", das::move(fileInfo));
See tutorial_integration_cpp_dynamic_scripts.
4.2.9. Project file resolution
A custom module_get function resolves require paths:
options gen2
require daslib/ast_boost public
[function_macro(name="module_get")]
class ModuleGetMacro : AstModuleGetMacro {
def override getModule(
req : string;
from : string
) : ModuleInfo {
// resolve require paths here
}
}
See tutorial_integration_cpp_custom_modules for the C++ equivalent.