🦋 108. Basic usage of NativeCall in Perl 6

NativeCall is both a module and a technology in Perl 6 that allows you to call C functions from your Perl 6 code. Today, let’s meet the most basic usage.

Take the rand() function from the C standard library:

#include <stdio.h>
#include <stdlib.h>

int main() {    
    int r = rand();
    printf("%i\n", r);

    return 0;
}

If you are used to Perl, it may be a surprise that the program actually prints the same number every time you call it. (That’s a kind of surprise of forgotten knowledge.) Compile and run:

$ gcc rand.c

$ ./a.out 
16807

$ ./a.out 
16807

Now let’s call C’s rand() from Perl 6. Refer to the documentation of NativeCall to see the options it offers.

Your Perl 6 program may look like this:

use NativeCall;

sub c_rand() returns int32 is native('c') is symbol('rand') {*}

say c_rand();

Although the rand() function was chosen as one from the standard library that do not need parameters, the choice added a few small complications to the Perl 6 code. But it is good that we can demonstrate them all together.

Similarly to how you declare external functions in C, you need to tell Perl 6 that there is some function that it should load from an external library. The name of the library is passed in the native trait. Its argument, c, will be converted to libc, and the corresponding shared library will be searched for in the standard places on your computer.

The symbol trait tells us the original function name. It is a bit weird to me, but Rakudo cannot handle the following code where you use the same name as in C:

use NativeCall;

sub rand() returns int32 is native('c') {*}

say rand();

This is how you would declare an external C function if you didn’t want to rename it in your program. Unfortunately, Perl 6 also has rand, and we got an error:

===SORRY!=== Error while compiling rand.pl6
 Unsupported use of rand(); in Perl 6 please use rand
 at rand.pl6:5
 ------> say rand⏏();

Finally, returns int32 tells that the function returns a native 32-bit integer.

Another surprise pops up when you run our Perl 6 program. It returns a random value, and that value is different each time you call it:

$ perl6 rand.pl6 
1790432239

$ perl6 rand.pl6 
1431059869

Is it really working? It is random, but why it does not copy the behaviour of the reference C program?

Does Rakudo call srand when it starts up? A shallow investigation gives the following log comment:

nqp/MoarVM/docs/ChangeLog:+ Do not call srand() if not using rand()

Let’s go back to our test case. To make sure it works, let us create our own version of the function, which will be returning the same value again and again:

myrand.c:

int myrand() {
    return 42;
}

myrand.h:

int myrand();

Test it in pure C first:

#include "myrand.h"
#include <stdio.h>

int main() {
    int r = myrand();
    printf("%i\n", r);

    return 0;
}
$ gcc myrand-main.c myrand.c 

$ ./a.out 
42

$ ./a.out 
42

It works. Now use it from Perl 6, and the program is almost the same as before:

use NativeCall;

sub myrand() returns int32 is native('libmyrand.so') {*}

say myrand();

The main differences are the name of the function (there is no clash with Perl 6 built-in functions, and thus no need to introduce an alias) and the library name, which is a file name this time.

(This is how you create a shared library:)

$ gcc -shared -olibmyrand.so myrand.c 

If you run the updated Perl 6 program, you will only see 42 in the output.

$ perl6 myrand.pl6 
42

$ perl6 myrand.pl6 
42

This confirms that the returns int32 clause works correctly, and the value returned from the C library is understood by Perl 6.