1. Perl
  2. XS
  3. here

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

Related Informatrion