Skip to content

Commit d4fc48d

Browse files
committed
allow [o] on OtherPars - #362
1 parent 3b3c6ac commit d4fc48d

File tree

8 files changed

+117
-16
lines changed

8 files changed

+117
-16
lines changed

Basic/Core/pdl.h.PL

+1
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ PDL_TYPELIST_ALL(X)
319319
#define PDL_TRANS_BADPROCESS 0x0002
320320
#define PDL_TRANS_BADIGNORE 0x0004
321321
#define PDL_TRANS_NO_PARALLEL 0x0008
322+
#define PDL_TRANS_OUTPUT_OTHERPAR 0x0010
322323
323324
#define PDL_LIST_FLAGS_PDLVTABLE(X) \
324325
X(PDL_TRANS_DO_BROADCAST) \

Basic/Core/pdlapi.c

+7
Original file line numberDiff line numberDiff line change
@@ -1180,5 +1180,12 @@ pdl_error pdl_trans_check_pdls(pdl_trans *trans) {
11801180
if (!pdls[i])
11811181
return pdl_make_error(PDL_EFATAL, "%s got NULL pointer on param %s", vtable->name, vtable->par_names[i]);
11821182
}
1183+
if (vtable->flags & PDL_TRANS_OUTPUT_OTHERPAR)
1184+
for (i = 0; i < vtable->npdls; i++)
1185+
if (!(trans->pdls[i]->state & PDL_NOMYDIMS) && trans->pdls[i]->ndims > vtable->par_realdims[i])
1186+
return pdl_make_error(PDL_EUSERERROR,
1187+
"Can't broadcast with output OtherPars but par '%s' has %"IND_FLAG" dims, > %"IND_FLAG"!",
1188+
vtable->par_names[i], trans->pdls[i]->ndims, vtable->par_realdims[i]
1189+
);
11831190
return PDL_err;
11841191
}

Basic/Gen/PP.pm

+27-10
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ sub dosubst_private {
381381
@pairs,
382382
((ref $src) ? %{$src->[1]} : ()),
383383
PRIV => sub {return "$sname->$_[0]"},
384-
COMP => sub {return "$pname->$_[0]"},
384+
COMP => sub {my $r="$pname->$_[0]";$sig->other_is_out($_[0])?"(*($r))":$r},
385385
CROAK => sub {PDL::PP::pp_line_numbers(__LINE__-1, "return PDL->make_error(PDL_EUSERERROR, \"Error in $name:\" @{[join ',', @_]})")},
386386
NAME => sub {return $name},
387387
MODULE => sub {return $::PDLMOD},
@@ -1573,6 +1573,7 @@ EOD
15731573
my %ptypes = map +($_=>$$optypes{$_} ? $$optypes{$_}->get_decl('', {VarArrays2Ptrs=>1}) : 'pdl *'), @args;
15741574
my %out = map +($_=>1), $sig->names_out_nca;
15751575
my %outca = map +($_=>1), $sig->names_oca;
1576+
my %other_out = map +($_=>1), $sig->other_out;
15761577
my %tmp = map +($_=>1), $sig->names_tmp;
15771578
# remember, otherpars *are* input vars
15781579
my $nout = grep $_, values %out;
@@ -1590,25 +1591,28 @@ EOD
15901591
# These are used in creating output variables. One variable (ex: SV * outvar1_SV;)
15911592
# is needed for each output and output create always argument
15921593
my $svdecls = join "\n", map indent("SV *${_}_SV = NULL;",$ci), $sig->names_out;
1593-
my ($xsargs, $xsdecls) = ('', ''); my %already_read;
1594+
my ($xsargs, $xsdecls) = ('', ''); my %already_read; my $cnt = 0; my %outother2cnt;
15941595
foreach my $x (@args) {
15951596
next if $outca{$x};
15961597
last if $out{$x} || ($other{$x} && exists $defaults->{$x});
15971598
$already_read{$x} = 1;
15981599
$xsargs .= "$x, "; $xsdecls .= "\n\t$ptypes{$x}$x";
1600+
$outother2cnt{$x} = $cnt if $other{$x} && $other_out{$x};
1601+
$cnt++;
15991602
}
1600-
my $pars = join "\n",map indent("$_;",$ci), $sig->alldecls(0, \%already_read);
1603+
my $pars = join "\n",map indent("$_;",$ci), $sig->alldecls(0, 0, \%already_read);
1604+
$svdecls = join "\n", grep length, $svdecls, map indent(qq{SV *${_}_SV = @{[defined($outother2cnt{$_})?"ST($outother2cnt{$_})":'NULL']};},$ci), $sig->other_out;
16011605
my @create = (); # The names of variables which need to be created by calling
16021606
# the 'initialize' perl routine from the correct package.
16031607
$ci = ' '; # Current indenting
16041608
# clause for reading in all variables
1605-
my $clause1 = $inplacecheck; my $cnt = 0;
1609+
my $clause1 = $inplacecheck; $cnt = 0;
16061610
foreach my $x (@args) {
16071611
if ($outca{$x}) {
16081612
push @create, $x;
16091613
} else {
16101614
my ($setter, $type) = typemap($ptypes{$x}, 'get_inputmap');
1611-
$setter = typemap_eval($setter, {var=>$x, type=>$type, arg=>($out{$x} ? "${x}_SV = " : '')."ST($cnt)"});
1615+
$setter = typemap_eval($setter, {var=>$x, type=>$type, arg=>($out{$x}||$other_out{$x} ? "${x}_SV = " : '')."ST($cnt)"});
16121616
$setter =~ s/.*?(?=$x\s*=\s*)//s; # zap any declarations like whichdims_count
16131617
$clause1 .= indent("$setter;\n",$ci) if !$already_read{$x};
16141618
$cnt++;
@@ -1626,7 +1630,7 @@ EOD
16261630
push @create, $x;
16271631
} else {
16281632
my ($setter, $type) = typemap($ptypes{$x}, 'get_inputmap');
1629-
$setter = typemap_eval($setter, {var=>$x, type=>$type, arg=>"ST($cnt)"});
1633+
$setter = typemap_eval($setter, {var=>$x, type=>$type, arg=>($other_out{$x} ? "${x}_SV = " : '')."ST($cnt)"});
16301634
$setter =~ s/^(.*?)=\s*//s, $setter = "$x = ($defaults_rawcond) ? ($defaults->{$x}) : ($setter)" if exists $defaults->{$x};
16311635
$clause3 .= indent("$setter;\n",$ci) if !$already_read{$x};
16321636
$cnt++;
@@ -1672,14 +1676,26 @@ END
16721676
my @outs = $sig->names_out; # names of output ndarrays in calling order
16731677
my $clause1 = join ';', map "ST($_) = $outs[$_]_SV", 0 .. $#outs;
16741678
$clause1 = PDL::PP::pp_line_numbers(__LINE__-1, "PDL_XS_RETURN($clause1)");
1679+
my @other_out = $sig->other_out;
1680+
my $optypes = $sig->otherobjs;
1681+
my %ptypes = map +($_=>$$optypes{$_}->get_decl('', {VarArrays2Ptrs=>1})), @other_out;
1682+
for my $x (@other_out) {
1683+
my ($setter, $type) = typemap($ptypes{$x}, 'get_outputmap');
1684+
$setter = typemap_eval($setter, {var=>$x, type=>$type, arg=>"tsv"});
1685+
$clause1 = <<EOF . $clause1;
1686+
{ SV *tsv = NULL;
1687+
$setter
1688+
sv_setsv(${x}_SV, tsv); sv_2mortal(tsv); }
1689+
EOF
1690+
}
16751691
$clause1;
16761692
}),
16771693

16781694
PDL::PP::Rule->new("NewXSHdr", ["NewXSName","SignatureObj"],
16791695
sub {
16801696
my($name,$sig) = @_;
16811697
my $shortpars = join ',', @{ $sig->allnames(1) };
1682-
my $longpars = join "\n", map "\t$_", $sig->alldecls(1);
1698+
my $longpars = join "\n", map "\t$_", $sig->alldecls(1, 0);
16831699
return<<END;
16841700
\nvoid
16851701
$name($shortpars)
@@ -1690,16 +1706,16 @@ END
16901706
PDL::PP::Rule->new("NewXSCHdrs", ["RunFuncName","SignatureObj","GlobalNew"],
16911707
sub {
16921708
my($name,$sig,$gname) = @_;
1693-
my $longpars = join ",", $sig->alldecls(0);
1709+
my $longpars = join ",", $sig->alldecls(0, 1);
16941710
my $opening = 'pdl_error PDL_err = {0, NULL, 0};';
16951711
my $closing = 'return PDL_err;';
16961712
return ["pdl_error $name($longpars) {$opening","$closing}",
16971713
"PDL->$gname = $name;"];
16981714
}),
16991715
PDL::PP::Rule->new(["RunFuncCall","RunFuncHdr"],["RunFuncName","SignatureObj"], sub {
17001716
my ($func_name,$sig) = @_;
1701-
my $shortpars = join ',', @{ $sig->allnames(0) };
1702-
my $longpars = join ",", $sig->alldecls(0);
1717+
my $shortpars = join ',', map $sig->other_is_out($_)?"&$_":$_, @{ $sig->allnames(0) };
1718+
my $longpars = join ",", $sig->alldecls(0, 1);
17031719
(PDL::PP::pp_line_numbers(__LINE__-1, "PDL->barf_if_error($func_name($shortpars));"),
17041720
"pdl_error $func_name($longpars)");
17051721
}),
@@ -1954,6 +1970,7 @@ EOF
19541970
push @op_flags, 'PDL_TRANS_BADPROCESS' if $badflag;
19551971
push @op_flags, 'PDL_TRANS_BADIGNORE' if defined $badflag and !$badflag;
19561972
push @op_flags, 'PDL_TRANS_NO_PARALLEL' if $noPthreadFlag;
1973+
push @op_flags, 'PDL_TRANS_OUTPUT_OTHERPAR' if $sig->other_any_out;
19571974
my $op_flags = join('|', @op_flags) || '0';
19581975
my $iflags = join('|', grep $_, $affflag, $revflag, $flowflag) || '0';
19591976
my $gentypes_txt = join(", ", (map PDL::Type->new($_)->sym, @$gentypes), '-1');

Basic/Gen/PP/CType.pm

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ sub get_decl {
5050
}
5151
} else { confess("Invalid decl @$_") }
5252
}
53+
$name = "*$name" if $opts->{AddIndirect};
5354
return "$this->{Base} $name";
5455
}
5556

Basic/Gen/PP/Signature.pm

+21-6
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,22 @@ sub new {
4747
my $i=0; my %ind2index = map +($_=>$i++), @{$this->{IndNamesSorted}};
4848
$this->{Ind2Index} = \%ind2index;
4949
$ind2obj{$_}->set_index($ind2index{$_}) for sort keys %ind2index;
50-
@$this{qw(OtherNames OtherObjs)} = $this->_otherPars_nft($otherpars||'');
50+
@$this{qw(OtherNames OtherObjs OtherAnyOut OtherFlags)} = $this->_otherPars_nft($otherpars||'');
5151
$this;
5252
}
5353

5454
sub _otherPars_nft {
5555
my ($sig,$otherpars) = @_;
5656
my $dimobjs = $sig && $sig->dims_obj;
57-
my (@names,%types,$type);
57+
my (@names,%types,$type,$any_out,%allflags);
5858
for (nospacesplit(';',$otherpars)) {
59+
my (%flags);
60+
if (s/^\s*$PDL::PP::PdlParObj::sqbr_re\s*//) {
61+
%flags = my %lflags = map +($_=>1), split /\s*,\s*/, my $opts = $1;
62+
my $this_out = delete $lflags{o};
63+
die "Invalid options '$opts' in '$_'" if keys %lflags;
64+
$any_out ||= $this_out;
65+
}
5966
if (/^\s*([^=]+?)\s*=>\s*(\S+)\s*$/) {
6067
# support 'int ndim => n;' syntax
6168
my ($ctype,$dim) = ($1,$2);
@@ -73,8 +80,9 @@ sub _otherPars_nft {
7380
push @names,$name;
7481
$types{$name} = $type;
7582
$types{"${name}_count"} = PDL::PP::CType->new("PDL_Indx ${name}_count") if $type->is_array;
83+
$allflags{$name} = \%flags;
7684
}
77-
return (\@names,\%types);
85+
(\@names,\%types,$any_out,\%allflags);
7886
}
7987

8088
=head1 AUTHOR
@@ -122,6 +130,9 @@ sub othernames {
122130
\@raw_names;
123131
}
124132
sub otherobjs { $_[0]{OtherObjs} }
133+
sub other_any_out { $_[0]{OtherAnyOut} }
134+
sub other_is_out { $_[0]{OtherFlags}{$_[1]} && $_[0]{OtherFlags}{$_[1]}{o} }
135+
sub other_out { grep $_[0]->other_is_out($_), @{$_[0]{OtherNames}} }
125136

126137
sub allnames { [
127138
(grep +(!$_[2] || !$_[2]{$_}) && !$_[0]{Objects}{$_}{FlagTemp}, @{$_[0]{Names}}),
@@ -132,14 +143,18 @@ sub allobjs {
132143
+{ ( map +($_,$pdltype), @{$_[0]{Names}} ), %{$_[0]->otherobjs} };
133144
}
134145
sub alldecls {
135-
my ($self, $omit_count, $except) = @_;
146+
my ($self, $omit_count, $indirect, $except) = @_;
136147
my $objs = $self->allobjs;
137-
map $objs->{$_}->get_decl($_, {VarArrays2Ptrs=>1}), @{$self->allnames($omit_count, $except)};
148+
my @names = @{$self->allnames($omit_count, $except)};
149+
$indirect = $indirect ? { map +($_=>$self->other_is_out($_)), @names } : {};
150+
map $objs->{$_}->get_decl($_, {VarArrays2Ptrs=>1,AddIndirect=>$indirect->{$_}}), @names;
138151
}
139152
sub getcomp {
140153
my ($self) = @_;
141154
my $objs = $self->otherobjs;
142-
join '', map "$_;", grep $_, map $objs->{$_}->get_decl($_, {VarArrays2Ptrs=>1}), @{$self->othernames(0)};
155+
my @names = @{$self->othernames(0)};
156+
my $indirect = { map +($_=>$self->other_is_out($_)), @names };
157+
join '', map "$_;", grep $_, map $objs->{$_}->get_decl($_, {VarArrays2Ptrs=>1,AddIndirect=>$indirect->{$_}}), @names;
143158
}
144159
sub getfree {
145160
my ($self,$symbol) = @_;

Basic/Pod/PP.pod

+30
Original file line numberDiff line numberDiff line change
@@ -1626,6 +1626,36 @@ and use it in the code:
16261626
where I have removed a macro wrapper call, but that would obscure the
16271627
discussion.
16281628

1629+
=head3 OtherPars as outputs
1630+
1631+
As of 2.081, you can specify an C<OtherPar> as an output. This looks like:
1632+
1633+
pp_def('output_op',
1634+
Pars => 'in(n=2)',
1635+
OtherPars => '[o] PDL_Anyval v0; [o] PDL_Anyval v1',
1636+
Code => '
1637+
pdl_datatypes dt = $PDL(in)->datatype;
1638+
ANYVAL_FROM_CTYPE($COMP(v0), dt, $in(n=>0));
1639+
ANYVAL_FROM_CTYPE($COMP(v1), dt, $in(n=>1));
1640+
',
1641+
);
1642+
1643+
The passed-in stack SV will be mutated in place, so this code will then work:
1644+
1645+
output_op([5,7], my $v0, my $v1);
1646+
is_deeply [$v0,$v1], [5,7], 'output OtherPars work';
1647+
1648+
An operation with output C<OtherPars> cannot broadcast, since that would
1649+
cause undefined results. A runtime check is generated that throws an
1650+
exception if any C<Par> would cause broadcasting.
1651+
1652+
Note the syntax for C<OtherPars> has C<[o]> go I<before> the type, while
1653+
it goes I<after> the type in C<Pars>. It was felt this was the best way
1654+
to avoid ambiguity given C types can have C<[]> in them.
1655+
1656+
This relies on the relevant C<OtherPar> having an C<OUTPUT> entry in an
1657+
XS typemap.
1658+
16291659
=head2 Other useful PP keys in data operation definitions
16301660

16311661
You have already heard about the C<OtherPars> key. Currently, there are not

Changes

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- fix MatrixOps::inv failing on native-complex (#403) - thanks @KJ7LNW for report
44
- fix MatrixOps::identity losing class of invocant if ndarray (#401) - thanks @pryrt for report
55
- change PP-generated XS PROTOTYPES to DISABLE
6+
- allow [o] on OtherPars (#362)
67

78
2.080 2022-05-28
89
- make IO::STL work on big-endian systems (#394) - thanks @sebastic for report

t/01-pptest.t

+29
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,25 @@ pp_deft("rice_compress",
241241
Code => ';', # do nothing
242242
);
243243
244+
pp_deft('output_op',
245+
Pars => 'in(n=2)',
246+
OtherPars => '[o] PDL_Anyval v0; [o] PDL_Anyval v1',
247+
Code => '
248+
pdl_datatypes dt = $PDL(in)->datatype;
249+
ANYVAL_FROM_CTYPE($COMP(v0), dt, $in(n=>0));
250+
ANYVAL_FROM_CTYPE($COMP(v1), dt, $in(n=>1));
251+
',
252+
);
253+
pp_deft('output_op2',
254+
Pars => 'in(n=2); [o] out()',
255+
OtherPars => '[o] PDL_Anyval v0; [o] PDL_Anyval v1',
256+
Code => '
257+
pdl_datatypes dt = $PDL(in)->datatype;
258+
ANYVAL_FROM_CTYPE($COMP(v0), dt, $in(n=>0));
259+
ANYVAL_FROM_CTYPE($COMP(v1), dt, $in(n=>1));
260+
',
261+
);
262+
244263
pp_done;
245264
246265
# this tests the bug with a trailing comment and *no* newline
@@ -368,6 +387,16 @@ test_polyfill_pp(zeroes(5,5), ones(2,3), 1);
368387
369388
is test_succ(2)."", 3, 'test pp_add_macros works';
370389
390+
test_output_op([5,7], my $v0, my $v1);
391+
is_deeply [$v0,$v1], [5,7], 'output OtherPars work';
392+
eval { test_output_op(sequence(2,3), my $v0, my $v1) };
393+
isnt $@, '', 'broadcast with output OtherPars throws';
394+
395+
test_output_op2([5,7], my $v0_2, my $v1_2);
396+
is_deeply [$v0_2,$v1_2], [5,7], 'output OtherPars work 2';
397+
eval { test_output_op2(sequence(2,3), my $v0_2, my $v1_2) };
398+
isnt $@, '', 'broadcast with output OtherPars throws 2';
399+
371400
done_testing;
372401
EOF
373402

0 commit comments

Comments
 (0)