I am working on an XS wrapper module for some functions in the GNU scientific library. Instead of using the library directly here, I have simpilfied the problem by creating my own library:
mylib/mylib.h:
typedef struct {
int foo;
double bar;
} my_struct_type;
extern my_struct_type *my_symbol1;
extern my_struct_type *my_symbol2;
void use_struct( my_struct_type *s );
mylib/mylib.c:
#include "mylib.h"
#include <stdio.h>
static my_struct_type my_struct1 = { 3, 3.14 };
static my_struct_type my_struct2 = { 2, 1.06 };
my_struct_type *my_symbol1 = &my_struct1;
my_struct_type *my_symbol2 = &my_struct2;
void use_struct( my_struct_type *s ) {
printf( "use_struct: foo = %d\n", s->foo);
printf( "use_struct: bar = %g\n", s->bar);
}
This is compiled into a shared library using:
$ gcc -c -o mylib.o mylib.c
$ gcc -shared -o libmylib.so mylib.o
So I will use mylib.so
as an example instead of libgsl.so
. Now I would like to refer to the C symbols my_symbol1
and my_symbol2
from a Perl script. First I created an XS file:
XsTest.xs:
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "mylib.h"
/* These definition are created ad hoc to provide an interface to the perl module */
#define STRUCT_TYPE1 1
#define STRUCT_TYPE2 2
MODULE = My::XsTest PACKAGE = My::XsTest
PROTOTYPES: DISABLE
# export STRUCT_TYPE1, STRUCT_TYPE2, ... to My::XsTest
# NOTE: I would like to avoid having to repeat the string, e.g. "STRUCT_TYPE1"
# in the lines below (if possible?)
BOOT:
{
SV* const_sv = get_sv( "My::XsTest::STRUCT_TYPE1", GV_ADD );
sv_setiv( const_sv, STRUCT_TYPE1 );
SvREADONLY_on( const_sv );
SV* const_sv2 = get_sv( "My::XsTest::STRUCT_TYPE2", GV_ADD );
sv_setiv( const_sv2, STRUCT_TYPE2 );
SvREADONLY_on( const_sv2 );
}
void
use_struct(type)
int type
CODE:
if (type == STRUCT_TYPE1 ) {
use_struct(my_symbol1);
}
else if (type == STRUCT_TYPE2) {
use_struct(my_symbol2);
}
else {
croak("Unknown struct type");
}
lib/My/XsTest.pm:
package My::XsTest;
our $VERSION = '0.01';
use strict;
use warnings;
use Exporter qw(import);
# NOTE: I would like to avoid having to define the line below here,
# it would be better if it was enough to define them in XsTest.xs
our %EXPORT_TAGS = ( 'symbols' => [ qw( STRUCT_TYPE1 STRUCT_TYPE2 ) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{symbols} } );
our @EXPORT = qw(use_struct);
# NOTE: I would like to avoid having to declare here the two line below.
# this should be done automatically from the .xs file
our $STRUCT_TYPE1;
our $STRUCT_TYPE2;
require XSLoader;
XSLoader::load();
# NOTE: I would like to avoid having to define the subs below.
# This should be done automatically from the .xs file
sub STRUCT_TYPE1 {
return $STRUCT_TYPE1;
}
sub STRUCT_TYPE2 {
return $STRUCT_TYPE2;
}
1;
Then to compile the extension, I used a ExtUtils::MakeMaker
:
Makefile.PL:
use strict;
use warnings;
use utf8;
use ExtUtils::MakeMaker;
my $lib_dir = 'mylib';
WriteMakefile(
NAME => 'My::XsTest',
VERSION_FROM => 'lib/My/XsTest.pm',
PREREQ_PM => { 'ExtUtils::MakeMaker' => 0 },
ABSTRACT_FROM => 'lib/My/XsTest.pm',
AUTHOR => 'Håkon Hægland <hakon.hagland@gmail.com>',
OPTIMIZE => '-g3 -O0',
LICENSE => 'perl',
LIBS => ["-L$lib_dir -lmylib"],
INC => "-I$lib_dir",
);
and then compiling:
$ perl Makefile.PL
$ make
Finally, I tested the module from a Perl script:
p.pl:
#! /usr/bin/env perl
use feature qw(say);
use strict;
use warnings;
use ExtUtils::testlib;
use My::XsTest qw(use_struct :symbols);
use_struct(STRUCT_TYPE1);
use_struct(STRUCT_TYPE2);
Output:
use_struct: foo = 3
use_struct: bar = 3.14
use_struct: foo = 2
use_struct: bar = 1.06
So this works, but it is not pretty. How can I improve this code and avoid all the repetition of the symbol names especially in the file lib/My/XsTest.pm
?