package ExtUtils::Typemaps;
use 5.006001;
use strict;
use warnings;
our $VERSION = '3.38';
require ExtUtils::ParseXS;
require ExtUtils::ParseXS::Constants;
require ExtUtils::Typemaps::InputMap;
require ExtUtils::Typemaps::OutputMap;
require ExtUtils::Typemaps::Type;
=head1 NAME
ExtUtils::Typemaps - Read/Write/Modify Perl/XS typemap files
=head1 SYNOPSIS
# read/create file
my $typemap = ExtUtils::Typemaps->new(file => 'typemap');
# alternatively create an in-memory typemap
# $typemap = ExtUtils::Typemaps->new();
# alternatively create an in-memory typemap by parsing a string
# $typemap = ExtUtils::Typemaps->new(string => $sometypemap);
# add a mapping
$typemap->add_typemap(ctype => 'NV', xstype => 'T_NV');
$typemap->add_inputmap(
xstype => 'T_NV', code => '$var = ($type)SvNV($arg);'
);
$typemap->add_outputmap(
xstype => 'T_NV', code => 'sv_setnv($arg, (NV)$var);'
);
$typemap->add_string(string => $typemapstring);
# will be parsed and merged
# remove a mapping (same for remove_typemap and remove_outputmap...)
$typemap->remove_inputmap(xstype => 'SomeType');
# save a typemap to a file
$typemap->write(file => 'anotherfile.map');
# merge the other typemap into this one
$typemap->merge(typemap => $another_typemap);
=head1 DESCRIPTION
This module can read, modify, create and write Perl XS typemap files. If you don't know
what a typemap is, please confer the L and L manuals.
The module is not entirely round-trip safe: For example it currently simply strips all comments.
The order of entries in the maps is, however, preserved.
We check for duplicate entries in the typemap, but do not check for missing
C entries for C or C entries since these might be hidden
in a different typemap.
=head1 METHODS
=cut
=head2 new
Returns a new typemap object. Takes an optional C parameter.
If set, the given file will be read. If the file doesn't exist, an empty typemap
is returned.
Alternatively, if the C parameter is given, the supplied
string will be parsed instead of a file.
=cut
sub new {
my $class = shift;
my %args = @_;
if (defined $args{file} and defined $args{string}) {
die("Cannot handle both 'file' and 'string' arguments to constructor");
}
my $self = bless {
file => undef,
%args,
typemap_section => [],
typemap_lookup => {},
input_section => [],
input_lookup => {},
output_section => [],
output_lookup => {},
} => $class;
$self->_init();
return $self;
}
sub _init {
my $self = shift;
if (defined $self->{string}) {
$self->_parse(\($self->{string}), $self->{lineno_offset}, $self->{fake_filename});
delete $self->{string};
}
elsif (defined $self->{file} and -e $self->{file}) {
open my $fh, '<', $self->{file}
or die "Cannot open typemap file '"
. $self->{file} . "' for reading: $!";
local $/ = undef;
my $string = <$fh>;
$self->_parse(\$string, $self->{lineno_offset}, $self->{file});
}
}
=head2 file
Get/set the file that the typemap is written to when the
C method is called.
=cut
sub file {
$_[0]->{file} = $_[1] if @_ > 1;
$_[0]->{file}
}
=head2 add_typemap
Add a C entry to the typemap.
Required named arguments: The C (e.g. C 'double'>)
and the C (e.g. C 'T_NV'>).
Optional named arguments: C 1> forces removal/replacement of
existing C entries of the same C. C 1>
triggers a I<"first come first serve"> logic by which new entries that conflict
with existing entries are silently ignored.
As an alternative to the named parameters usage, you may pass in
an C object as first argument, a copy of which will be
added to the typemap. In that case, only the C or C named parameters
may be used after the object. Example:
$map->add_typemap($type_obj, replace => 1);
=cut
sub add_typemap {
my $self = shift;
my $type;
my %args;
if ((@_ % 2) == 1) {
my $orig = shift;
$type = $orig->new();
%args = @_;
}
else {
%args = @_;
my $ctype = $args{ctype};
die("Need ctype argument") if not defined $ctype;
my $xstype = $args{xstype};
die("Need xstype argument") if not defined $xstype;
$type = ExtUtils::Typemaps::Type->new(
xstype => $xstype,
'prototype' => $args{'prototype'},
ctype => $ctype,
);
}
if ($args{skip} and $args{replace}) {
die("Cannot use both 'skip' and 'replace'");
}
if ($args{replace}) {
$self->remove_typemap(ctype => $type->ctype);
}
elsif ($args{skip}) {
return() if exists $self->{typemap_lookup}{$type->ctype};
}
else {
$self->validate(typemap_xstype => $type->xstype, ctype => $type->ctype);
}
# store
push @{$self->{typemap_section}}, $type;
# remember type for lookup, too.
$self->{typemap_lookup}{$type->tidy_ctype} = $#{$self->{typemap_section}};
return 1;
}
=head2 add_inputmap
Add an C entry to the typemap.
Required named arguments:
The C (e.g. C 'T_NV'>)
and the C to associate with it for input.
Optional named arguments: C 1> forces removal/replacement of
existing C entries of the same C. C 1>
triggers a I<"first come first serve"> logic by which new entries that conflict
with existing entries are silently ignored.
As an alternative to the named parameters usage, you may pass in
an C object as first argument, a copy of which will be
added to the typemap. In that case, only the C or C named parameters
may be used after the object. Example:
$map->add_inputmap($type_obj, replace => 1);
=cut
sub add_inputmap {
my $self = shift;
my $input;
my %args;
if ((@_ % 2) == 1) {
my $orig = shift;
$input = $orig->new();
%args = @_;
}
else {
%args = @_;
my $xstype = $args{xstype};
die("Need xstype argument") if not defined $xstype;
my $code = $args{code};
die("Need code argument") if not defined $code;
$input = ExtUtils::Typemaps::InputMap->new(
xstype => $xstype,
code => $code,
);
}
if ($args{skip} and $args{replace}) {
die("Cannot use both 'skip' and 'replace'");
}
if ($args{replace}) {
$self->remove_inputmap(xstype => $input->xstype);
}
elsif ($args{skip}) {
return() if exists $self->{input_lookup}{$input->xstype};
}
else {
$self->validate(inputmap_xstype => $input->xstype);
}
# store
push @{$self->{input_section}}, $input;
# remember type for lookup, too.
$self->{input_lookup}{$input->xstype} = $#{$self->{input_section}};
return 1;
}
=head2 add_outputmap
Add an C