diff --git a/doc/Inline/C/Cookbook.swim b/doc/Inline/C/Cookbook.swim index 9f13175..123f818 100644 --- a/doc/Inline/C/Cookbook.swim +++ b/doc/Inline/C/Cookbook.swim @@ -332,6 +332,36 @@ How do I pass a variable-sized list of arguments to an Inline C function? ( http://www.microship.com ), but he is close ( http://bike-nomad.com ). Thanks Ned. +== Taking an array-ref as an argument + +- Problem + + How can I take a Perl array-ref as an argument in my C function? + +- Solution + + SV *sum(SV *array) { + int total = 0; + int numelts, i; + SvGETMAGIC(array); + if ((!SvROK(array)) + || (SvTYPE(SvRV(array)) != SVt_PVAV) + || ((numelts = av_len((AV *)SvRV(array))) < 0) + ) { + return &PL_sv_undef; + } + for (i = 0; i <= numelts; i++) { + total += SvIV(*av_fetch((AV *)SvRV(array), i, 0)); + } + return newSViv(total); + } + +- Discussion + + This example returns `undef` if given a non-ref, or a non-array-ref, + or a ref to an empty array. You can see how you might expand this to + take more than one array-ref. + == Using Memory - Problem diff --git a/test/29refargs.t b/test/29refargs.t new file mode 100644 index 0000000..72080e2 --- /dev/null +++ b/test/29refargs.t @@ -0,0 +1,32 @@ +use strict; use warnings; +use lib -e 't' ? 't' : 'test'; +use diagnostics; +use TestInlineSetup; +use Inline Config => DIRECTORY => $TestInlineSetup::DIR; +use Test::More; + +my $c_text = <<'EOC'; +SV *sum(SV *array) { + int total = 0; + int numelts, i; + SvGETMAGIC(array); + if ((!SvROK(array)) + || (SvTYPE(SvRV(array)) != SVt_PVAV) + || ((numelts = av_len((AV *)SvRV(array))) < 0) + ) { + return &PL_sv_undef; + } + for (i = 0; i <= numelts; i++) { + total += SvIV(*av_fetch((AV *)SvRV(array), i, 0)); + } + return newSViv(total); +} +EOC +Inline->bind(C => $c_text); + +is sum([(1..4)]), 10, 'correct sum'; +is sum(undef), undef, 'return undef when given undef'; +is sum('hello'), undef, 'return undef when given non-ref'; +is sum([]), undef, 'return undef when given empty list'; + +done_testing;