Class::HPLOO::InlineC - Add a pseudo syntax over C to work easier with SV*, AV*, HV* and RV*.
Who have worked with XS and perlapi knows that to access values from AV* and HV*, and work with references is not very friendly. To work arounf that I have added a pseudo syntax over the C syntax, that helps to work easily with SV*, AV*, HV* and RV*.
use Class::HPLOO ; class Point { sub Point ($x , $y) { $this->{x} = $x ; $this->{y} = $y ; } sub move_x( $mv_x ) { $this->{x} += $mv_x ; } sub[C] void move_y( SV* self , int mv_y ) { int y = self->{y}->int + mv_y ; self->{y} = int2sv(y) ; } sub[C] SV* get_xy_ref( SV* self ) { AV* ret = newAV() ; ret->[0] = self->{x} ; ret->[1] = self->{y} ; return \{ret} ; } } my $p = Point->new(10,20) ; $p->move_x(100) ; $p->move_y(100) ; my $xy = $p->get_xy_ref() ; ## returns an ARRAY reference. print "XY> @$xy\n" ; ## XY> 110 120
As you can see, is very easy to access and set an integer value from $Point->{y} (at self). Also is simple to create an ARRAY and return a reference to it.
FETCH:
self->{y}->int ## Rewrited to: SvIV( *hv_fetch((HV*)SvRV( self ) , "y" , strlen("y") , 0) )
STORE:
self->{y} = int2sv(y) ; ## Rewrited to: sv_setsv_mg( *hv_fetch((HV*)SvRV( self ) , "y" , strlen("y") , newSViv(y) ) ;
void foo(SV* self) { self->{y}->int ; // $this->{y} as int. self->{y}->float ; // $this->{y} as double. self->{list}->av ; // @{$this->{list}} as AV*. self->{hash}->hv ; // %{$this->{hash}} as HV*. self->{y}->sv; // explicity $this->{y} as SV*. self->{hash}->rv ; // New RV: \%{$this->{hash}} { HV* hv1 = %{ self->{hash} } ; // %{$this->{hash}} HV* hv2 = self->{hash}->hv ; // same } }
void foo(SV* self) { self->[0]->int ; // $this->[0] as int. self->[1]->float ; // $this->[1] as double. self->[2]->av ; // @{$this->[2]} as AV*. self->[3]->hv ; // %{$this->[3]} as HV*. self->[3]->sv; // explicity $this->[3] as SV*. self->[4]->rv ; // New RV: \%{$this->[4]} { AV* av1 = @{ self->{list} } ; // @{$this->{list}} AV* av2 = self->{list}->av ; // same } }
void foo(SV* val) { val->str ; // $val as char* val->pvx ; // return a pointer to the char* buffer inside the SCALAR. val->int ; // $val as int. val->float ; // $val as double. val->av ; // @{$val} as AV*. val->hv ; // %{$val} as HV*. val->sv ; // explicity $val as SV*. val->rv ; // New RV: \%{$val} { SV* sv = val ; // make sv to point to val. SV* sv = val->sv ; // make sv to point to val but explicity declare val as (SV*) SV* sv = ${val} ; // Access the SV that val points if val is a reference (RV) to another SV. } }
void foo(SV* val) { SV* sv_ref = \{val} ; // Create a reference to $val SV* sv = ${sv_ref} // de-reference sv_ref: ${$sv_ref} ; AV* array = newAV() ; // Create a new AV. SV* av_ref = \{array} ; // Create a reference to array ; AV* av = @{av_ref} // Get the array that av_ref make reference. HV* hash = newHV() ; // Create a new HV. SV* hv_ref = \{hash} ; // Create a reference to hash ; HV* hv = %{hv_ref} // Get the hash that hv_ref make reference. }
The STORE syntax is always an access to an SV = a new SV:
self->{y} = int2sv(10) ; self->[0] = int2sv(10) ;
If you have a SV directly in a C variable you can use ->sv to explicity say that it's an SV and enable the syntax:
SV* y = self->{y} ; // ... y->sv = int2sv(10);
Basically to store using a reference we just need to access the SV inside the reference:
AV* array = newAV() ; // Create a new AV. SV* av_ref = \{array} ; // Create a reference to array ; AV* av = @{av_ref} // Get the array that av_ref make reference. array->[0] = int2sv(10) ; array->[1] = int2sv(20) ; array->[2] = int2sv(30) ; // and with av will change the same array: av->[0] = int2sv(10) ; av->[1] = int2sv(20) ; av->[2] = int2sv(30) ; // with the reference is: @{av_ref}[0] = int2sv(10) ; @{av_ref}[1] = int2sv(20) ; @{av_ref}[2] = int2sv(30) ;
As a normal Perl code the pseudo syntax make the fetch and store to elements that doesn't exists yet true. So, if you fetch an ARRAY position that doesn't exists, a new undef SV will be created in the position before you fetch it. The same idea works for HV, so you can fetch a key of a hash even if it wasn't created yet. And since a STORE is done with a previous FETCH, you can store elements in positions/keys that doesn't exists yet:
AV* array = newAV() ; // Create a new AV. array->[0] = int2sv(10) ; // Store 10 in the position 0 without need to FILL the array before.
Note that each FETCH to an array ensure that the array is filled with the element. And each FETCH to a HASH will automatically create the key.
The easier way to return a list of elements (SV*) is to return a reference to an ARRAY. With this approach you don't need to work with the XS STACK:
sub SV* foo() { AV* ret = newAV() ; // Create a new AV. ret->[0] = int2sv(10) ; // store 10 at $ret[0]. AV* table = newHV() ; // Create a new HV. table->{a} = int2sv(1) ; // set the key a => 1. table->{b} = int2sv(2) ; // set the key b => 2. ret->[0] = \{table} // Store a reference to the hash table. return \{ret} ; // Return a reference to the array @ret. }
Here's the list of convertion functions:
SV* bool2sv( bool b ) ; SV* int2sv( long n ) ; SV* long2sv( long n ) ; SV* float2sv( float n ) ; SV* double2sv( double n ) ; SV* str2sv( char* s ) ; bool sv2bool( SV* s ) ; int sv2int( SV* s ) ; long sv2long( SV* s ) ; float sv2float( SV* s ) ; double sv2double( SV* s ) ; char* sv2str( SV* s ) ;
Class::HPLOO, Inline::C.
Graciliano M. P. <gmpassos@cpan.org>
I will appreciate any type of feedback (include your opinions and/or suggestions). ;-P
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
To install Class::HPLOO, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Class::HPLOO
CPAN shell
perl -MCPAN -e shell install Class::HPLOO
For more information on module installation, please visit the detailed CPAN module installation guide.