First, keep in mind I've never used XS. I can't guarantee the following works or even its soundness.
If you check the system typemap (lib/ExtUtils/typemap), you'll see the definition of T_PTROBJ. (What is T_OBJECT?) We can create a new rule based on T_PTROBJ to suit our needs.
# typedef struct bar* Bar;
# typedef struct type_foo* Foo;
TYPEMAP
#######
Bar T_NULLABLE_PTROBJ
Foo T_NULLABLE_PTROBJ
INPUT
#####
T_NULLABLE_PTROBJ
if ( !SvOK( $arg ) ) { /* if not defined */
$var = NULL;
}
else if ( sv_derived_from( $arg, \"${ntype}\" ) ) {
IV tmp = SvIV( (SV*)SvRV( $arg ) );
$var = INT2PTR( $type, tmp );
}
else {
Perl_croak( aTHX_ \"$var is not of type ${ntype}\" )
}
OUTPUT
######
T_NULLABLE_PTROBJ
if ( $var ) /* if defined */
sv_setref_pv( $arg, \"${ntype}\", (void*)$var );
else
$arg = &PL_sv_undef;
The usage would be
my $foo = undef;
my $bar = Foo::create_bar( $foo );
Update: I don't think addressing the problem in the typemap is the best method. This makes pointers of that type always nullable, whereas that should be decided on a per-function (and even per-argument) basis. That means we should addresses the issue in the XS stub.
# typedef struct bar* Bar;
# typedef struct type_foo* Foo;
# In typemap:
# Bar T_PTROBJ
# Foo T_PTROBJ
struct bar*
create_bar(foo_sv = 0)
SV* foo_sv
CODE:
struct type_foo* foo = NULL;
if ( !foo_sv ) {
/* Use default of NULL. */
}
else if ( !SvOK( foo_sv ) ) {
/* Use default of NULL. */
}
else if ( sv_derived_from( foo_sv, 'type_foo' ) ) {
IV tmp = SvIV( (SV*)SvRV( foo_sv ) );
foo = INT2PTR( struct type_foo*, tmp );
}
else {
Perl_croak( aTHX_ "\$foo is not of type foo_type" )
}
RETVAL = my_c_create_bar(foo);
OUTPUT:
RETVAL
Bonus: The = 0 and the if ( !foo_sv ) allows the parameter to be omitted. Feel free to remove this.
Update: Now uses tabs in typemap, as required.
Update: struct bar* and struct type_foo* are no good for type T_PTROBJ — search for T_PTROBJ_SPECIAL in perlxs — so I added typedefs.
|