Reputation: 3262
I am new to Perl XS and I am trying to convert a C function to Perl subroutine.
I have the following C function
void parse(struct parser *result, const char *string, size_t len);
where the parse
function accepts a pointer to struct parser
, a string and the length of the string. struct parser
is defined something like this:
struct parser {
char *data;
long a;
long b;
long c;
};
The function stores its results in the result
argument.
I want to convert this function to Perl XS. What I am doing is something like this:
struct parser *result
parse_xs (string)
const char* string
PREINIT:
long len = strlen(string);
CODE:
struct parser par;
parse(&par,s,len);
RETVAL = par;
OUTPUT:
RETVAL
How can I change the above code to run parse_xs
in Perl code like this
my $result = parse_xs();
print $result->data; # will print the data field from the struct.
where $result
is the result of the parse
C function.
Upvotes: 3
Views: 1191
Reputation: 33638
First, you have to choose a name for your result class. I'll simply use ParseResult
from now on.
You'll also need a typemap
file. To map C structs to Perl classes, use the built-in T_PTROBJ
:
TYPEMAP
ParseResult T_PTROBJ
Then add a typedef and the ParseResult
package to your XS code:
typedef struct parser *ParseResult;
MODULE = YourModule PACKAGE = ParseResult
Make sure that the typedef comes before all MODULE
sections. Now you can add the XSUB for the parse_xs
function (you should probably just name it parse
):
ParseResult
parse_xs(string)
SV *string
PREINIT:
const char *c_string;
STRLEN len;
CODE:
Newx(RETVAL, 1, struct parser);
c_string = SvPV(string, len);
parse(RETVAL, c_string, len);
OUTPUT:
RETVAL
Note that I allocate memory for the result struct using Perl's Newx
function. Returning a pointer to a struct on the stack cannot work. I also added some minor optimizations like passing the string as SV
and getting the content and length using SvPV
.
In order to free the allocated memory, you have to implement a destructor (named DESTROY
in Perl):
void
DESTROY(result)
ParseResult result
CODE:
/* Possibly free data in result here. */
Safefree(result);
Your result struct probably points to other allocated memory. Make sure to free this memory before calling Safefree
.
Then you can write accessors like this:
const char *
data(result)
ParseResult result
CODE:
RETVAL = result->data;
OUTPUT:
RETVAL
Now you have to write a Perl module for your class. Add a file named ParseResult.pm
with the following contents:
package ParseResult;
use strict;
use warnings;
use XSLoader;
our $VERSION = '0.01';
XSLoader::load('ParseResult', $VERSION);
1;
Your new XS-based class can then be used like this:
use ParseResult;
my $result = ParseResult::parse_xs('input');
print $result->data, "\n";
Note that I use a fully-qualified name for parse_xs
. If you want to call it without the class name, you have to export it from ParseResult.pm
.
Upvotes: 6
Reputation: 21
You can use C struct as Perl Object. See the following page(This page is Japanese, but you may understand source code).
http://d.hatena.ne.jp/perlcodesample/20140807/1407291461
// creat struct as pointer
Point* point = (Point*)malloc(sizeof(Point));
point->x = x;
point->y = y;
// Convert pointer to size_t
size_t point_iv = PTR2IV(point);
// Convert size_t to SV*
SV* point_sv = sv_2mortal(newSViv(point_iv));
// Create reference to SV*
SV* point_svrv = sv_2mortal(newRV_inc(point_sv));
// Create Object
SV* point_obj = sv_bless(point_svrv, gv_stashpv(class_name, 1));
Upvotes: 2