5.3.12. C++ Integration: Smart Pointers
This tutorial shows how to expose reference-counted C++ types to
daslang using smart_ptr<T>. Topics covered:
Inheriting from
das::ptr_ref_countfor reference countingManagedStructureAnnotationwithcanNew/canDeletevar inscope— automatic cleanup of smart pointersFactory functions returning
smart_ptr<T>smart_ptr_cloneandsmart_ptr_use_countnew Tfor heap allocation from scripts
5.3.12.1. Prerequisites
Tutorial 11 completed (tutorial_integration_cpp_context_variables).
Understanding of reference counting concepts.
5.3.12.2. Making a type reference-counted
A C++ type becomes smart-pointer-compatible by inheriting from
das::ptr_ref_count, which provides addRef(), delRef(),
and use_count():
#include "daScript/daScript.h"
class Entity : public das::ptr_ref_count {
public:
das::string name;
float x, y;
int32_t health;
Entity() : name("unnamed"), x(0), y(0), health(100) {
printf(" Entity constructed\n");
}
~Entity() {
printf(" Entity destroyed\n");
}
// ... methods ...
};
When delRef() decrements the count to zero, the object is
automatically deleted.
5.3.12.3. Annotation — canNew and canDelete
ManagedStructureAnnotation takes two boolean template parameters
that control what scripts can do:
struct EntityAnnotation
: ManagedStructureAnnotation<Entity, true, true>
// ^^^^ ^^^^
// canNew ---+ |
// canDelete -------+
{
EntityAnnotation(ModuleLibrary & ml)
: ManagedStructureAnnotation("Entity", ml)
{
addField<DAS_BIND_MANAGED_FIELD(name)>("name", "name");
addField<DAS_BIND_MANAGED_FIELD(x)>("x", "x");
addField<DAS_BIND_MANAGED_FIELD(y)>("y", "y");
addField<DAS_BIND_MANAGED_FIELD(health)>("health", "health");
addProperty<DAS_BIND_MANAGED_PROP(is_alive)>(
"is_alive", "is_alive");
}
};
isSmart() is auto-detected — ManagedStructureAnnotation checks
is_base_of<ptr_ref_count, Entity> at compile time.
|
|
|
|
5.3.12.4. Factory returning smart_ptr<T>
The typical pattern is a factory function that returns smart_ptr:
smart_ptr<Entity> make_entity(const char * name, float x, float y) {
auto e = make_smart<Entity>();
e->name = name;
e->x = x;
e->y = y;
return e;
}
// Registration:
addExtern<DAS_BIND_FUN(make_entity)>(*this, lib, "make_entity",
SideEffects::modifyExternal, "make_entity")
->args({"name", "x", "y"});
5.3.12.5. Using smart pointers in daslang
Smart pointer variables must be declared with var inscope, which
ensures delRef() is called when the variable goes out of scope:
options gen2
require tutorial_12_cpp
[export]
def test() {
// Factory returns smart_ptr<Entity> — use <- to move
var inscope hero <- make_entity("Hero", 0.0, 0.0)
print("{hero.name} hp={hero.health}\n")
// Dereference with * when passing to functions taking Entity &
move_entity(*hero, 3.0, 4.0)
// Clone increases reference count
var inscope hero2 : smart_ptr<Entity>
smart_ptr_clone(hero2, hero)
print("use_count = {int(smart_ptr_use_count(hero))}\n") // 2
// `new Entity` — heap allocation (requires unsafe)
unsafe {
var inscope fresh <- new Entity
fresh.health = 50
}
// fresh destroyed here (delRef → ref_count==0 → delete)
Key points:
var inscope— required forsmart_ptrvariables<-— move semantics (not=)*ptr— dereference to getEntity &for function callssmart_ptr_clone(dest, src)— clone (addRef)smart_ptr_use_count(ptr)— returnsuint(cast tointfor decimal printing)new Entity— allocates + addRef (needsunsafe)
5.3.12.6. Building and running
cmake --build build --config Release --target integration_cpp_12
bin\Release\integration_cpp_12.exe
Expected output:
=== Factory-created entities ===
[C++] Entity('unnamed') constructed
[C++] Entity('unnamed') constructed
hero: Hero at (0, 0) hp=100
enemy: Goblin at (10, 5) hp=100
use_count(hero) = 1
use_count(enemy) = 1
=== Moving and combat ===
hero moved to (3, 4)
distance = 7.071068
enemy hp after 30 damage = 70
enemy.is_alive = true
enemy hp after 100 more = 0
enemy.is_alive = false
=== Cloning smart_ptr ===
After clone:
use_count(hero) = 2
use_count(hero2) = 2
hero2.name = Hero
=== new Entity() ===
[C++] Entity('unnamed') constructed
fresh.name = Newbie
fresh.health = 50
use_count = 1
[C++] Entity('Newbie') destroyed
=== End of test (hero, enemy, hero2 destroyed here) ===
[C++] Entity('Goblin') destroyed
[C++] Entity('Hero') destroyed
See also
Full source:
12_smart_pointers.cpp,
12_smart_pointers.das
Previous tutorial: tutorial_integration_cpp_context_variables
Next tutorial: tutorial_integration_cpp_aot