Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"

Mixing C and perl

by jpl (Monk)
on Jan 17, 2012 at 17:57 UTC ( #948367=perlquestion: print w/replies, xml ) Need Help??
jpl has asked for the wisdom of the Perl Monks concerning the following question:

I have an XS module, mostly following the models in Extending and Embedding Perl. I use a T_PTROBJ type, and keep everything I need in a dynamically allocated C structure, whose address is the T_PTROBJ object. But as I add functionality, it's a nuisance to have to keep adjusting the contents of the structure. I'd like to be able to do things that are not so time critical in plain perl, but still get directly to the methods defined in the XS module. If I add an HV * to my C structure, I can initialize it with an anonymous hash that acts somewhat like the usual pure-perl blessed object, for holding (and hiding) arbitrary state information. So my skeletal XS code looks something like

#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" #include <am_common.h> #include "" MODULE = AddrMatch PACKAGE = AddrMatch INCLUDE: AddrMatch * new_xs(class, stash) char * class HV * stash PREINIT: AddrMatch *amd; CODE: amd = malloc(sizeof(AddrMatch)); SvREFCNT_inc((SV *) stash); amd->stash = stash; RETVAL = amd; OUTPUT: RETVAL MODULE = AddrMatch PACKAGE = AddrMatchPtr void DESTROY(amd) AddrMatch * amd CODE: SvREFCNT_dec((SV *)(amd->stash)); free(amd); HV * stash(amd) AddrMatch * amd CODE: RETVAL = amd->stash; OUTPUT: RETVAL
where the stash structure element gives me a place to stash my perl-level state. Then my can do something like
package AddrMatch; sub new { my %state = ( what => "ever" ); my $amd = AddrMatch->xs_new(\%state); return $amd; } # then, later, recover the perl-ish state with package AddrMatchPtr; sub something { my $self = shift; my $state = $self->stash(); my $what = $state->{what}; $state{how} = "now"; }
So the question (there must be a question in here somewhere) is, can this be done more elegantly in some other way? This isn't too onerous, and it makes it quite easy to change my mind about whether some method is implemented in pure perl, or is worth coding into the XS module. But I'd be happy to do it more simply, if possible.

Replies are listed 'Best First'.
Re: Mixing C and perl
by salva (Abbot) on Jan 17, 2012 at 18:32 UTC
    Instead of accessing the hash through a method on the Perl side, you can just use it as the perl wrapper for the C object.

    You will have to store the C pointer inside the hash, for instance as $hash{__c_ptr}. Another option is to use <c>~ magic to save it, that way it will be invisible from the Perl side.

      I want to be able to get directly to the XS methods using
      If $amd is just a blessed hash reference, I think I'd have to "indirect through" the perl wrapper:
      # In sub method { my $self = shift; my $Cptr = $self->{__c_ptr}; return $Cptr->xs_method(); }
      Also not too onerous. But will h2xs do all the right stuff to do the dynamic linking? I confess to considerable uncertainty about how the linking is made to work. I have tested the original model, and it appears to be doing the right thing with linking and reference counts.

        In the Perl-hash-wraps-C-struct setup, an object looks like {foo => 'bar', ... '__cptr' => \2131231231}. Well, with- or without the scalar reference to the number-that-is-a-pointer

        What you can do to make the XS methods work appropriately on this construct while being able to store stuff regularly from Perl as well, is to define a typemap that will a) check it got a hashref b) check that the class inherits from your package, c) fetch the __cptr slot of your hash-object, d) check it's a scalar ref, e) use the integer as a pointer, f) cast that into your C type. b), d), e), f) are done in T_PTROBJ already. This would allow you to write XS like with T_PTROBJ. The only downside is that you'd lose the ability to access the outer hash from XS.

Re: Mixing C and perl
by bulk88 (Priest) on Jan 18, 2012 at 07:09 UTC
    If you have control over the free/destroy portion, and your code looks like you do, why not keep the struct in a scalar or SV as a string? If its inside an object, there is a implicit guarantee not to look at it from Perl (and it would be an error to crack open the object). This way you get Perls reference counting system and you can vaguely visually guess if something is null or not in that struct from Perl's debugger.

    Its very easy manipulate blessed hash objects from XS. Just use "self" and "HV * self" and then do hv_fetch'es on it. If you want to be extra safe (and slower), make your own typemap entry and your own type that does a sv_derived_from (look at Perl's default typemap), and finally a "#define MYHVTYPE HV" to trick XSPP not to overwrite the default HV * typemap entry. XS typemaps are evaled as the inside of a double quotes expression by XSPP. "${\'some string'}" or " ".(1 == 2 ? "true" : "false"). " " work. In the 2nd typemap example, I broke the double quotes and put in raw perl code. Step through output_init in and you'll see the eval XSPP makes. Not 100% if all the details above are correct, but theories are correct.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://948367]
Approved by toolic
Front-paged by Arunbear
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (7)
As of 2017-03-28 17:22 GMT
Find Nodes?
    Voting Booth?
    Should Pluto Get Its Planethood Back?

    Results (339 votes). Check out past polls.