use strict; use warnings; package Clownfish::Binding::Perl::TypeMap; use base qw( Exporter ); use Scalar::Util qw( blessed ); use Carp; use Fcntl; use Config; our @EXPORT_OK = qw( from_perl to_perl ); # Convert from a Perl scalar to a primitive type. my %primitives_from_perl = ( double => sub {"$_[0] = SvNV( $_[1] );"}, float => sub {"$_[0] = (float)SvNV( $_[1] );"}, int => sub {"$_[0] = (int)SvIV( $_[1] );"}, short => sub {"$_[0] = (short)SvIV( $_[1] );"}, long => sub { $Config{longsize} <= $Config{ivsize} ? "$_[0] = (long)SvIV( $_[1] );" : "$_[0] = (long)SvNV( $_[1] );"; }, size_t => sub {"$_[0] = (size_t)SvIV( $_[1] );"}, uint64_t => sub {"$_[0] = (uint64_t)SvNV( $_[1] );"}, uint32_t => sub {"$_[0] = (uint32_t)SvUV( $_[1] );"}, uint16_t => sub {"$_[0] = (uint16_t)SvUV( $_[1] );"}, uint8_t => sub {"$_[0] = (uint8_t)SvUV( $_[1] );"}, int64_t => sub {"$_[0] = (int64_t)SvNV( $_[1] );"}, int32_t => sub {"$_[0] = (int32_t)SvIV( $_[1] );"}, int16_t => sub {"$_[0] = (int16_t)SvIV( $_[1] );"}, int8_t => sub {"$_[0] = (int8_t)SvIV( $_[1] );"}, chy_bool_t => sub {"$_[0] = SvTRUE( $_[1] ) ? 1 : 0;"}, ); # Convert from a primitive type to a Perl scalar. my %primitives_to_perl = ( double => sub {"$_[0] = newSVnv( $_[1] );"}, float => sub {"$_[0] = newSVnv( $_[1] );"}, int => sub {"$_[0] = newSViv( $_[1] );"}, short => sub {"$_[0] = newSViv( $_[1] );"}, long => sub { $Config{longsize} <= $Config{ivsize} ? "$_[0] = newSViv( $_[1] );" : "$_[0] = newSVnv( (NV)$_[1] );"; }, size_t => sub {"$_[0] = newSViv( $_[1] );"}, uint64_t => sub { $Config{uvsize} == 8 ? "$_[0] = newSVuv( $_[1] );" : "$_[0] = newSVnv( (NV)$_[1] );"; }, uint32_t => sub {"$_[0] = newSVuv( $_[1] );"}, uint16_t => sub {"$_[0] = newSVuv( $_[1] );"}, uint8_t => sub {"$_[0] = newSVuv( $_[1] );"}, int64_t => sub { $Config{ivsize} == 8 ? "$_[0] = newSViv( $_[1] );" : "$_[0] = newSVnv( (NV)$_[1] );"; }, int32_t => sub {"$_[0] = newSViv( $_[1] );"}, int16_t => sub {"$_[0] = newSViv( $_[1] );"}, int8_t => sub {"$_[0] = newSViv( $_[1] );"}, chy_bool_t => sub {"$_[0] = newSViv( $_[1] );"}, ); # Extract a Clownfish object from a Perl SV. sub _sv_to_cf_obj { my ( $type, $cf_var, $xs_var ) = @_; my $struct_sym = $type->get_specifier; my $vtable = uc($struct_sym); if ( $struct_sym =~ /^[a-z_]*(Obj|CharBuf)$/ ) { # Share buffers rather than copy between Perl scalars and Clownfish # string types. Assume that the appropriate ZombieCharBuf has been # declared on the stack. return "$cf_var = ($struct_sym*)XSBind_sv_to_cfish_obj($xs_var, " . "$vtable, alloca(cfish_ZCB_size()));"; } else { return "$cf_var = ($struct_sym*)XSBind_sv_to_cfish_obj($xs_var, " . "$vtable, NULL);"; } } sub _void_star_to_clownfish { my ( $type, $cf_var, $xs_var ) = @_; # Assume that void* is a reference SV -- either a hashref or an arrayref. return qq|if (SvROK($xs_var)) { $cf_var = SvRV($xs_var); } else { $cf_var = NULL; /* avoid uninitialized compiler warning */ CFISH_THROW(CFISH_ERR, "$cf_var is not a reference"); }\n|; } sub from_perl { my ( $type, $cf_var, $xs_var ) = @_; confess("Not a Clownfish::Type") unless blessed($type) && $type->isa('Clownfish::Type'); if ( $type->is_object ) { return _sv_to_cf_obj( $type, $cf_var, $xs_var ); } elsif ( $type->is_primitive ) { if ( my $sub = $primitives_from_perl{ $type->to_c } ) { return $sub->( $cf_var, $xs_var ); } } elsif ( $type->is_composite ) { if ( $type->to_c eq 'void*' ) { return _void_star_to_clownfish( $type, $cf_var, $xs_var ); } } confess( "Missing typemap for " . $type->to_c ); } sub to_perl { my ( $type, $xs_var, $cf_var ) = @_; confess("Not a Clownfish::Type") unless ref($type) && $type->isa('Clownfish::Type'); my $type_str = $type->to_c; if ( $type->is_object ) { return "$xs_var = $cf_var == NULL ? newSV(0) : " . "XSBind_cfish_to_perl((cfish_Obj*)$cf_var);"; } elsif ( $type->is_primitive ) { if ( my $sub = $primitives_to_perl{$type_str} ) { return $sub->( $xs_var, $cf_var ); } } elsif ( $type->is_composite ) { if ( $type_str eq 'void*' ) { # Assume that void* is a reference SV -- either a hashref or an # arrayref. return "$xs_var = newRV_inc( (SV*)($cf_var) );"; } } confess("Missing typemap for '$type_str'"); } sub write_xs_typemap { my ( undef, %args ) = @_; my $hierarchy = $args{hierarchy}; my $typemap_start = _typemap_start(); my $typemap_input = _typemap_input_start(); my $typemap_output = _typemap_output_start(); for my $class ( $hierarchy->ordered_classes ) { my $full_struct_sym = $class->full_struct_sym; my $vtable = $class->full_vtable_var; my $label = $vtable . "_"; $typemap_start .= "$full_struct_sym*\t$label\n"; $typemap_input .= <. =head1 FUNCTIONS =head2 from_perl my $c_code = from_perl( $type, $cf_var, $xs_var ); Return C code which converts from a Perl scalar to a variable of type $type. Variable declarations must precede the returned code, as from_perl() won't make any declarations itself. =over =item * B - A Clownfish::Type, which will be used to select the mapping code. =item * B - The name of the variable being assigned to. =item * B - The C name of the Perl scalar from which we are extracting a value. =back =head2 to_perl my $c_code = to_perl( $type, $xs_var, $cf_var ); Return C code which converts from a variable of type $type to a Perl scalar. Variable declarations must precede the returned code, as to_perl() won't make any declarations itself. =over =item * B - A Clownfish::Type, which will be used to select the mapping code. =item * B - The C name of the Perl scalar being assigned to. =item * B - The name of the variable from which we are extracting a value. =back =head1 CLASS METHODS =head2 write_xs_typemap Clownfish::Binding::Perl::Typemap->write_xs_typemap( hierarchy => $hierarchy, ); =over =item * B - A L. =back Auto-generate a "typemap" file that adheres to the conventions documented in L. We generate this file on the fly rather than maintain a static copy because we want an entry for each Clownfish type so that we can differentiate between them when checking arguments. Keeping the entries up-to-date manually as classes come and go would be a pain. =head1 COPYRIGHT AND LICENSE Copyright 2008-2010 Marvin Humphrey This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut