in reply to XS: How to enable passing ref or undef?

How do I tell XS that a reference OR undef is OK for a parameter (c)?

I think I would just pass c to the XSub as an SV, and then let the XSub determine whether c is a reference or not ( using SvROK(c) ). Once the XSub knows what it has, you can then have it take whatever steps are needed to pass satisfactory values on to the C routine.
If the XSub needs to know whether c is an object or not, use sv_isobject(c):
use warnings; use Math::BigInt; use strict; use Inline C => Config => BUILD_NOISY => 1, USING => 'ParseRegExp'; use Inline C => <<'END'; int is_ref(SV * x) { if(SvROK(x)) return 1; return 0; } int is_obj(SV * x) { if(sv_isobject(x)) return 1; return 0; } END my $undef; my $ref = \$undef; my $obj = Math::BigInt->new(0); for($undef, $ref, $obj) { print is_ref($_), " ", is_obj($_), "\n"; } # Output: # 0 0 # 1 0 # 1 1

Replies are listed 'Best First'.
Re^2: XS: How to enable passing ref or undef?
by tlhackque (Beadle) on Oct 16, 2010 at 10:22 UTC
    Thanks - I appreciate the effort. But that isn't the approach I need here.

    I have made this work by making the declaration variadic, which gives me the unfettered ability to process the third argument. But it also required me to copy all the ugly casts that the xs compiler did to convert to the structure pointer.

    routine(a, b=4,...) char * a; int b PREINIT: foo_t * c = NULL; CODE: if (items > 2 ) { if (SvOK(ST(2))) { /* defined */ if (SvROK(ST(2))) { /* reference */ IV tmp = SvIV((SV*)SvRV(ST(2))); c = INT2PTR(foo_t *,tmp); } else Perl_croak(aTHX_ "c is not a reference"); } } ... continuing as before
    There must be some typemap magic that would have the same effect with less mumbling. But the documentation is sparse and the crystal is cloudy...

    This communication may not represent my employer's views, if any, on the matters discussed.

      Here is some typemap code I copied from my own project. I mostly figured this out (aka copy/pasted) from the perlxstut manpage I think.
      TYPEMAP # You need a typedef for BarPointOrNull in your .xs file or a header.. +. # Like this: typedef bar_t * BarPointerOrNull; # Then you can use it in the XS parameter list: # routine( a, b, c ) # char * a # int b # BarPointerOrNull c BarPointerOrNull T_BAR_OR_NULL INPUT #--------------------------------------------------------------------- +-------- # Map a perl reference to a C pointer... #--------------------------------------------------------------------- +-------- T_BAR_OR_NULL if ( ! SvOK( $arg )) { $var = NULL; } /* Make sure we are given an object of class Bar::Class... */ else if ( sv_derived_from( $arg, \"Bar::Class\" )) { IV tmp = SvIV( (SV *) SvRV( $arg )); $var = INT2PTR( $type, tmp ); } /* Holy crap! I must have copied this from the manpage... nice. */ else { Perl_croak(aTHX_ \"%s: %s is not of type %s\", ${$ALIAS?\q[GvNAME(CvGV(cv))]:\qq[\"$pname\"]}, \"$var\", \"Bar::Class\") } OUTPUT #--------------------------------------------------------------------- +-------- # Map a C pointer to a perl reference... #--------------------------------------------------------------------- +-------- T_BAR_OR_NULL if ( $var == NULL ) { $arg = &PL_sv_unef; } else { /* We bless the pointer as a Bar::Class object... can be handy * for wrapping structures in objects. */ sv_setref_pv( $arg, \"Bar::Class\", (void *)$var ); } # $var is the C variable, $type is the C type, $arg is the perl variab +le # Um that's all I know. OH and always escape your double-quotes!
      EDIT On second thought... this won't work. The XS function will complain about not enough arguments if you don't give it a third argument. You could always wrap it in a pure-perl function, though...
      # Silly really... but so easy! sub myroutine { my ($a, $b, $c) = @_; routine( $a, $b, $c ); }