🦋 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.

3 thoughts on “🦋 108. Basic usage of NativeCall in Perl 6”

  1. Note that Perl 6 complains only about the fact that you call `rand` **with** parentheses. If you don’t, you’re fine:

    use NativeCall;
    sub rand() returns int32 is native(“c”) {*}
    say rand; # 989945918

    That’s because `rand()` is considered to be a Perl 5-isms. If you want to allow Perl 5-isms in your code, you can use the `isms` pragma:

    use isms “Perl5”;
    say rand(); # 0.7855565742431416

    Liked by 2 people

  2. on ubuntu I encounter an error with library selection, because of dev packages installed, so I filled a bug , and I was told to specify the version on native like :

    sub c_rand() returns int32 is native(‘c’,v6) is symbol(‘rand’) {*}

    now it’s OK

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s