When a string is passed to a native sub, it will be encoded (UTF-8 by default)
into a NULL delimited C string. This C string will be free()
'd immediately after
the native sub returns.
Some native subs expect to gain ownership on the passed string and will call
free()
on it themselves. In that case the argument must be marked
explicitly-managed
:
sub foo(Str $s is explicitly-managed) is native("foo") { * }
In other cases the native sub will just store the pointer and expect it to be
still accessible to other native subs after it returns. The explicitly-managed
trait can be used for that if either:
- some other native sub will
free()
the string later on or - we expect the string to live indefinitely
Note however that each call to a native sub will cause a new C string to be
created, even if the argument is marked explicitly-managed
.
If the same C string needs to be used over several native calls, but eventually
freed by the Perl 6 runtime, the explicitly-managed
trait will therefore not be
enough. Instead, the Perl 6 string must be encoded into a character array
explicitly. That's done by using the encode()
method and adjusting the native
sub's signature to take a blob
. There's one more caveat however. The VM is free
to move objects in memory (e.g. for generational GC). If we want to use the same
pointer in multiple calls, we need to explicitly request stability by using
the stable
trait on the argument and by calling explicitly-manage()
and
using the return value:
sub foo(utf8 $s is stable) is native("foo") { * }
my $blob = explicitly-manage("some string".encode);
foo($blob);
foo($blob);
If the same C string needs to be used over several native calls and will also
be free()
'd by one of them, it must be detached from Perl 6's garbage
collection. This can be done by passing the :!free
argument to
explicitly-manage()
:
sub foo(utf8 is stable $s) is native("foo") { * }
sub free-the-foo(utf8 is stable $s) is native("foo") { * }
my $blob = explicitly-manage("some string".encode, :!free);
foo($blob);
free-the-foo($blob);