The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Class::HPLOO::InlineC - Add a pseudo syntax over C to work easier with SV*, AV*, HV* and RV*.

DESCRIPTION

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*.

USAGE

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) ) ;

THE PSEUDO SYNTAX

FETCH HV:
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
  }
}
FETCH AV:
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
  }
}
FETCH SV:
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.
  }
}
FETCH RV:
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.
}
STORE

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);
STORE and REFERENCES

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) ;
FETCH/STORE AV and HV elements

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.

EASILY RETURNING N ELEMENTS

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.
}

SV* CONVERTIONS

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 ) ;

SEE ALSO

Class::HPLOO, Inline::C.

AUTHOR

Graciliano M. P. <gmpassos@cpan.org>

I will appreciate any type of feedback (include your opinions and/or suggestions). ;-P

COPYRIGHT

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.