How to treat a C language structure as a Perl object in XS
In XS, you can also treat the structure itself as a Perl object. I will explain how to treat a C language structure as a Perl object.
Create a module with h2xs
First, create a module for XS with h2xs.
h2xs -A -n SomeModule
This will create a directory called "SomeModule". The following files and directories will be created.
Changes
lib/Makefile.PL
MANIFEST
ppport.h
README
SomeModule.xs
t/</pre>
<h3>XS file description</h3>
Let's write an XS file. The pointer of the structure is converted to size_t type by PTR2INT. In addition, size_t type is converted to SV * type, SV * type is converted to SV * type reference, and finally bless is converted to object.
The size_t type is an integer type, but the address value should be received as the size_t type.
When retrieving, dereference is performed, the value of IV contained in SV * is fetched, and INT2PTR is converted into a pointer to the structure.
<pre>
# include "EXTERN.h"
# include "perl.h"
# include "XSUB.h"
# include "ppport.h"
typedef struct {
double x;
double y;
} Point;
MODULE = Point PACKAGE = Point
void
new(...)
PPCODE:
{
// name of the class
char * class_name = SvPV_nolen (ST (0));
// x and y
double x = SvNV (ST (1));
double y = SvNV (ST (2));
// Create structure (create as pointer)
Point * point = (Point *) malloc (sizeof (Point));
point->x = x;
point->y = y;
// Convert pointer to size_t type
size_t point_iv = PTR2IV (point);
// Convert size_t type to SV * type
SV * point_sv = sv_2mortal (newSViv (point_iv));
// Create SV * type reference
SV * point_svrv = sv_2mortal (newRV_inc (point_sv));
// create an object
SV * point_obj = sv_bless(point_svrv, gv_stashpv (class_name, 1));
XPUSHs (point_obj);
XSRETURN (1);
}
void
x (...)
PPCODE:
{
// get the object
SV * point_obj = ST (0);
// Dereference
SV * point_sv = SvROK (point_obj)? SvRV (point_obj): point_obj;
// Convert SV * type to size_t type
size_t point_iv = SvIV (point_sv);
// Convert size_t type to pointer
Point * point = INT2PTR (Point *, point_iv);
// get x
double x = point->x;
// Convert x to SV * type
SV * x_sv = sv_2mortal (newSVnv (x));
XPUSHs (x_sv);
XSRETURN (1);
}
void
y (...)
PPCODE:
{
// get the object
SV * point_obj = ST (0);
// Dereference
SV * point_sv = SvROK (point_obj)? SvRV (point_obj): point_obj;
// Convert SV * type to size_t type
size_t point_iv = SvIV (point_sv);
// Convert size_t type to pointer
Point * point = INT2PTR (Point *, point_iv);
// get x
double y = point->y;
// Convert x to SV * type
SV * y_sv = sv_2mortal (newSVnv (y));
XPUSHs (y_sv);
XSRETURN (1);
}
void
DESTORY (...)
PPCODE:
{
// get the object
SV * point_obj = ST (0);
// Dereference
SV * point_sv = SvROK (point_obj)? SvRV (point_obj): point_obj;
// Convert SV * type to size_t type
size_t point_iv = SvIV (point_sv);
// Convert size_t type to pointer
Point * point = INT2PTR (Point *, point_iv);
// Release Point *
free (point);
XSRETURN (0);
}
MODULE = SomeModule PACKAGE = SomeModule
Creating a Point module
Please put a file called Point.pm under lib. SomeModule is loaded because SomeModule has a binding description.
package Point; use SomeModule; 1;
Test script
Create a test script. This should be in the same directory where the XS files are located.
use strict; use warnings; use Point; my $point = Point->new(1, 2); print $point->x . "\n"; print $point->y . "\n";
Compile and run
Let's compile and run it.
perl Makefile.PL make perl -Mblib test.pl
If the output is as follows, it is successful.
1 2
Perl ABC