PerlFFI / FFI-Platypus

Write Perl bindings to non-Perl libraries with FFI. No XS required.
91 stars 24 forks source link

Add support for pass by value records #57

Closed felliott closed 5 years ago

felliott commented 9 years ago

Hey @plicease,

Here is a simplified test case for segfaulting while using FFI::Platypus::Record to interface with libtcod. I'm running this on OS X against libtcod v1.5.1 from Homebrew (the games keg). I'm using perl v5.18.2 and FFI::Platypus v0.37. The segfault specifically occurs when trying to build a TcodColor object retrieved via the api.

https://gist.github.com/anonymous/fc337a8ec8083de00b19

Cheers, Fitz

libtcod docs: libtcod api documentation console drawing api

felliott commented 9 years ago

Here's another example script that shows that Record objects passed as argument are misunderstood.

https://gist.github.com/anonymous/d0672cc52ca540428b5b

The @ on the left is drawn using a wrapper function provided by libtcod that only needs primitive types. It consistently draws the requested red-on-white character. The @ on the right uses the FFI::Platypus::Record objects to specify fg and bg colors. It should be blue-on-white, but both fg and bg are random colors every time.

Cheers, Fitz

plicease commented 9 years ago

Interesting, so I see the problem, which is the color structs are being passed in as actual structs not pointers to structs. structs are frequently passed in as pointers to save copying memory so when I implemented the record() type I did so as pointers to structs. I suppose they should really be called record()* instead.

Here is some quick example code:

#include <stdio.h>

struct color {
  unsigned char r;
  unsigned char g;
  unsigned char b;
};

void
print_color_ptr(struct color *color)
{
  printf("[r,g,b] = [%d,%d,%d]\n", color->r, color->g, color->b);
}

void
print_color_struct(struct color color)
{
  printf("[r,g,b] = [%d,%d,%d]\n", color.r, color.g, color.b);
}
use strict;
use warnings;

package Color;

use FFI::Platypus::Record;

record_layout(qw(
  uchar r
  uchar g
  uchar b
));

package main;

use FFI::Platypus;

my $ffi = FFI::Platypus->new(lib => './libfoo.dylib');

$ffi->attach(print_color_ptr => ['record(Color)'] => 'void');
$ffi->attach(print_color_struct => ['record(Color)'] => 'void');

my $color = Color->new( r=> 22 );
print_color_struct($color);
print_color_ptr($color);

The perl prints out this:

urquhart% perl foo.pl 
[r,g,b] = [224,127,208]
[r,g,b] = [22,0,0]

the first print displays random values, probably what ever happened to be on the stack.

First problem is that the documentation should be more clear about record being a pointer type. Second is that platypus should support non pointer struct types.

plicease commented 5 years ago

update: Pass-by-value records is now implemented in the PR #135 that I hope to merge soon.

plicease commented 5 years ago

I realize this has been a loooooong time coming. But this is finally complete in master. The latest prod version 0.92 has pass-by-value records for argument types, but not return types. I figured out the last few issues with making it work for return types today, and that will go in the next dev version, and eventually the next prod version.

pass-by-value records is experimental at the moment, but I expect it remove the experimental status in the next month or so.