Take a walk on the C side, ddt, du du, du du ...

Someone on #perl6 asked if Data::Dump::Tree (DDT) could display an int32 properly, and the adventure began.

I have programmed in C and C++ many years; from hardware related code to mangling libraries for RPC across different cpu architectures . Few years ago we had a very large testing and integration project that used Perl as the main development language; we had to write quite some XS even if we used SWIG extensively. So NativeCall is something that is really of interest even if nowadays I have little use for it, I still think it's a nice way of interfacing libraries.

Data::Dump::Tree works with types but NativeCall works with representation, types, and what I believe is dynamically create types for array (with VMArray representation), so it took some times to comprehend and get DDT to groak it.

The documentation for NativeCall is here https://docs.perl6.org/language/nativecall#Getting_Started

I'll walk you through how the different NativeCall elements are displayed by DDT. The general principle is to make it clear what belongs to NativeCall while hiding a bit of the naming specific to NativeCall.


DDT will show that a sub is a NativeCall sub by adding the "NativeCall" tag

sub add_p6(Int, Int) returns Int  { * } # normal sub
sub some_argless_function() is native('something')  { * }
our sub init() is native('foo') is symbol('FOO_INIT') { * }
sub add(int32, int32) returns int32 is native("calculator") { * }
sub Foo_init() returns Pointer is native("foo") { * }



There are two ways to declare pointers

class MyHandle is repr('CPointer') {...}
my Pointer[int32] $pointer ;



my int32 $int32 = 7 ;
my @with_int32 = $int32, 7, 8 ;
my int32 @int32 = $int32, 7, 8 ;

my $string = "FOO";
my @carray := CArray[uint8].new($string.encode.list);

my $carray_titles = CArray[Str].new;
$carray_titles[0] = 'Me';
$carray_titles[1] = 'You';


In the code I create an int32, put it in a normal array, create an int32 @array, and two CArrays.

As you can see the int32 displays as an Int (newest version of DDT does not display the type for Ints to reduce noise). That is because being an int32 is an attribute of the variable not the value. The same goes for an int32 put in a normal array, it displays as an Int.

But int32 @array is different, we can catch it by type not representation. DDT sees it belongs to NativeCall, it adds "array" tag to it.

CArrays are handled in a similar manner, make them look as normal type arrays plus the "CArray" tag.


class Types is repr('CStruct') {
    has int8 $.a1 ;
    has int16 $.a2 ;
    has int32 $.a3 ;
    has int64 $.a4 ;
    has uint8 $.a5 ;
    has uint16 $.a6 ;
    has uint32 $.a7 ;
    has uint64 $.a8 ;
    has long $.a9 ;
    has longlong $.a10 ;
    has ulong $.a11 ;
    has ulonglong $.a12 ;
    has num32 $.a13 ;
    has num64 $.a14 ;
    has Str $.a15 ;
    has CArray[int32] $.a16 ;
    has Pointer[void] $.a17 ;
    has bool $.a18 ;
    has size_t $.a19 ;
    has ssize_t $.a20 ;

ddt (Types, Types.new), :flat(0) ;

When you render an undefined NativeCall structure, ddt will list the elements of the structure and their type on the right-hand side. If you render a defined NativeCall structure, ddt renders then name of the element followed with '.' ~ type of the element in the C world, on the right hand side the value in the Perl world.


CStructs, CUnions, and complex structures

class Point is repr('CStruct') {
    has num64 $.x;
    has num32 $.y;
    has int32 $.z = 3;

my $point = Point.new: :x(2e56), :y(10e10) ;

class Parts is repr('CUnion') {
    has int32 $.xyz;
    has int64 $.abc;

my Parts $union = Parts.new: :abc(10 ** 10) ;  

class MyStruct is repr('CStruct') {
    has Point $.point;  # referenced
    has int32 $.flags;


Type handlers

You can also create a DDT handler for a NativeCall structure you create, this makes it up to you to render the structure. Below a very simple example that just renders a string.

#define a NativeCall structure
class StructiWithHandler is repr('CStruct')
    has int32 $.flags;

# define a DDT handler
role DDT_SWH
multi method get_header (StructiWithHandler $s)
    { 'In DDT Handler', '.' ~ $s.^name, DDT_FINAL  }


What's left?

I need to understand the difference between has and HAS, from NativeCall, and see if I can differentiate them. I also would like to run on a lot of different input data; if you are using NativeCall, you can help by trying it on your data and send me the results. If you encounter errors, open an issue with code to repropduce it or reach for me on #perl6.

1 Comment

You are either a music connoisseur or you are dating yourself with that title.

Of course, I am also dating myself. Lol.

Great article.

Leave a comment

About Nadim Khemir

user-pic I blog about Perl.