8

I am trying to get this example of NativeCall running:

use NativeCall;

class p_timespec is repr('CPointer') {
    has uint32 $.tv_sec;
    has long $.tv_nanosecs;
}

sub clock_gettime(uint32 $clock-id, p_timespec $tspec --> uint32) is native(Str) { * };

my p_timespec $this-time;

my $result = clock_gettime( 0, $this-time);

say "$result, $this-time";

It simply segfaults, which is what happens when you use pointers and you should not. In this case, it's probably due to the declaration of p_timespec; I have actually declared it as a CPointer, although the struct should be OK. However, from the segmentation fault I can't fathom what's really wrong. Can someone help?

jjmerelo
  • 22,578
  • 8
  • 40
  • 86
  • 1
    It might be (nearly) impossible to make more forgiving, simply because the C side doesn't do any validations. In the end, you'll have to use the usual C debugging tools (like gdb, valgrind) to your aid. – moritz Apr 24 '18 at 09:23
  • 1
    Shouldn't the repr be `CStruct` insetad of `CPointer`? Atributes on a `CPointer` make no sense and probably should be made to throw a compiler error... – Christoph Apr 24 '18 at 09:28
  • 1
    Do you have the right type for `p_timespec.tv_sec` for your system? On my system it should be an `int64`, not `uint32` – Kaiepi Apr 24 '18 at 10:25
  • 1
    @Kaiepi it seems to be a bit more forgiving in that sense. The problem is declaring a CPointer with structure, apparently. – jjmerelo Apr 24 '18 at 10:26
  • 1
    @jjmerelo: are you working on a Perl 6 version of `Time::HiRes` by any chance? – Elizabeth Mattijsen Apr 24 '18 at 12:38
  • @ElizabethMattijsen maybe I should; but I'm just trying to get some real use case examples for the documentation. – jjmerelo Apr 24 '18 at 13:03

1 Answers1

9

There's two things wrong here.

  1. The CStruct representation should be used
  2. You need to make an instance of the structure for it to fill out with data, otherwise you're passing a null pointer

This seems to work:

use NativeCall;

class p_timespec is repr('CStruct') {
    has uint32 $.tv_sec;
    has long $.tv_nanosecs;
}

sub clock_gettime(uint32 $clock-id, p_timespec $tspec --> uint32) is native(Str) { * };

my p_timespec $this-time .= new;

my $result = clock_gettime( 0, $this-time);

say "$result, $this-time.tv_sec(), $this-time.tv_nanosecs()";

As for debugging, Rakudo's installation process also installs a perl6-gdb-m and a perl6-valgrind-m; the latter, while slow, will tend to provide some useful information on memory errors.

Jonathan Worthington
  • 29,104
  • 2
  • 97
  • 136
  • 2
    Out of curiosity, why does virtually no one use the `repr` syntax? – Christoph Apr 24 '18 at 09:35
  • In fact, clock_gettime uses a pointer as a second argument. Does this mean that you can use CStruct instead of CPointers? When do you actually need to use CPointers? – jjmerelo Apr 24 '18 at 09:40
  • 2
    A `CStruct` instance already is passed as a pointer to the memory, so there's no need for an extra level of pointer. `CPointer` is useful when you have want to deal with a data structure simply as an opaque pointer, without caring what is inside of it. This works out well when, for example, having a C library that allocates/frees the structure and provides functions for operating on it, and you should never poke into it directly. – Jonathan Worthington Apr 24 '18 at 12:43
  • 1
    @JonathanWorthington "`CPointer` ... to deal with ... as an opaque pointer ... and you should never poke into it directly." Would you say [Christoph's comment](https://stackoverflow.com/questions/49997598/how-to-correctly-use-cpointer-and-cstruct-in-nativecall-interface#comment87012281_49997598) is spot on, or at least that use of `has` and/or `HAS` with a `repr('CPointer')` warrants a warning? – raiph Apr 25 '18 at 10:25