The next trick is to get it to work with
$a and
$b. The following seems to work, but I'm not sure it isn't corrupting the stack. Any improvements?
#! perl -slw
use strict;
use List::Util qw[ shuffle ];
use Inline C => 'DATA';
$^W = 0;
my $max = 120;
my @values = 1 .. $max;
my @values_mixed = shuffle( @values );
my @top = topN( 10, sub { $a <=> $b; }, \@values_mixed );
print "@top";
__END__
__C__
int callComp( SV* cmp, SV* a, SV* b ) {
dSP;
int rv;
ENTER;
SAVETMPS;
GvSV(gv_fetchpv("main::a", TRUE, SVt_PV)) = a;
GvSV(gv_fetchpv("main::b", TRUE, SVt_PV)) = b;
PUSHMARK(SP);
PUTBACK;
if( call_sv( cmp, G_SCALAR|G_NOARGS ) != 1 )
croak( "Bad comparator" );
SPAGAIN;
rv = POPi;
PUTBACK; // << Was missing and is required!
FREETMPS;LEAVE;
return rv;
}
void topN( int n, SV* comp, AV*data ) {
int *topN;
int len = av_len( data );
int i, j, k;
Inline_Stack_Vars;
Newz( 1, topN, n + 1, SV* );
for( i = 0; i < n+1; i++ ) topN[ i ] = newSViv( 0 );
for( i = 0; i <= len; i++ ) {
SV* val = *av_fetch( data, i, 0 );
for( j = 0; j < n; j++ ) {
int cmp = callComp( comp, topN[ j ], val );
if( cmp >= 0 ) continue;
if( cmp < 0 ) {
for( k = n; k > j; k-- ) topN[ k ] = topN[ k-1 ];
topN[ j ] = val;
break;
}
}
}
Inline_Stack_Reset;
for( i = 0; i < n; i++ )
Inline_Stack_Push( sv_2mortal( newSVsv( topN[ i ] ) ) );
Safefree( topN );
Inline_Stack_Done;
}