4

Trying to interface with a C library that takes a struct with a bunch of pointers to functions it calls at various points.

something like this:

struct callbacks {
    int (*foo)(int);
    int (*bar)(int);
}

int set_callbacks(callbacks *cbs);

I can make my callbacks:

sub foo-callback(int32 --> int32) {...}
sub bar-callback(int32 --> int32) {...}

It would be cool if this worked:

class Callbacks is repr<CStruct>
{
    has &.foo (int32 --> int32);
    has &.bar (int32 --> int32);
}

but it doesn't. I'm trying to do something with:

class Callbacks is repr<CStruct>
{
    has Pointer $.foo;
    has Pointer $.bar;
}

and set those to nativecast(Pointer, &foo-callback) or some such, but I can't seem to force them in there.

Is there any way to do this beyond writing a little C function that takes all the Perl 6 function pointers and plugging them in the structure in C?

brian d foy
  • 129,424
  • 31
  • 207
  • 592
Curt Tilmes
  • 3,035
  • 1
  • 12
  • 24

1 Answers1

5

I still can't find an official way to do this, but I figured out a work-around.

Declare a version of sprintf that takes a function pointer of the right type (so it will set up the calling shim correctly), but cheat and have sprintf just give me back the pointer as a number (%lld) I can stuff in the Pointer.

class Callbacks is repr<CStruct>
{
    has Pointer $.foo;
    has Pointer $.bar;

    sub sprintf(Blob, Str, & (int32 --> int32) --> int32) is native {}

    submethod BUILD(:&foo, :&bar)
    {
        my $buf = buf8.allocate(20);
        my $len = sprintf($buf, "%lld", &foo);
        $!foo := Pointer.new($buf.subbuf(^$len).decode.Int);
        $len = sprintf($buf, "%lld", &bar);
        $!bar := Pointer.new($buf.subbuf(^$len).decode.Int);
    }
}

sub foo-callback(int32 $x --> int32) {...}
sub bar-callback(int32 $x --> int32) {...}

my $callbacks = Callbacks.new(foo => &foo-callback,
                              bar => &bar-callback);

If you have multiple callbacks with different signatures, just declare a sprintf for each C calling signature you need, e.g.:

sub sprintf-ii(Blob, Str, & (int32 --> int32) --> int32)
    is native is symbol('sprintf') {}

sub sprintf-isi(Blob, Str, & (int32, Str --> int32) --> int32)
    is native is symbol('sprintf') {}

and call the right sprintf-* for each callback pointer you need.

There is probably an easier way than going back and forth with the pointer to number to string to number to pointer, but this does the trick for now.

Curt Tilmes
  • 3,035
  • 1
  • 12
  • 24