# New Ticket Created by  Itsuki Toyota 
# Please include the string:  [perl #130187]
# in the subject line of all future correspondence about this issue. 
# <URL: https://rt.perl.org/Ticket/Display.html?id=130187 >


See the following codes and results:

*** codes ***

* t/05-pointer.c
----
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "05-pointer.h"

#ifdef _WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT extern
#endif

double dot(struct Feature* lhs, struct Feature* rhs) {
    double sum = 0.0;

    while(lhs->index != -1 && rhs->index != -1) {
        if (lhs->index == rhs->index) {
            sum += lhs->value * rhs->value;
            lhs++;
            rhs++;
        }
        else if (lhs->index < rhs->index) {
            lhs++;
        }
        else {
            rhs++;
        }
    }
    return sum;
}
----


*t/05-pointer.h
----
#if ! defined(HEADER_POINTER_H)
#define HEADER_POINTER_H

#ifdef __cplusplus
extern "C" {
#endif

struct Feature {
  int index;
  double value;
} Feature;

double dot(struct Feature*, struct Feature*);

#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif

#endif /* HEADER_POINTER_H */
----


* t/CompileTestLib.pm
----
unit module CompileTestLib;

my @cleanup;  # files to be cleaned up afterwards

sub compile_test_lib($name) is export {
    my ($c_line, $l_line);
    my $VM  := $*VM;
    my $cfg := $VM.config;
    my $libname = $VM.platform-library-name($name.IO);
    if $VM.name eq 'moar' {
        my $o  = $cfg<obj>;

        # MoarVM exposes exposes GNU make directives here, but we cannot pass 
this to gcc directly.
        my $ldshared = $cfg<ldshared>.subst(/'--out-implib,lib$(notdir $@).a'/, 
"--out-implib,$libname.a");

        $c_line = "$cfg<cc> -c $cfg<ccshared> $cfg<ccout>$name$o $cfg<cflags> 
t/$name.c";
        $l_line = "$cfg<ld> $ldshared $cfg<ldflags> $cfg<ldlibs> 
$cfg<ldout>$libname $name$o";
        @cleanup = << "$libname" "$name$o" >>;
    }
    elsif $VM.name eq 'jvm' {
        $c_line = "$cfg<nativecall.cc> -c $cfg<nativecall.ccdlflags> 
-o$name$cfg<nativecall.o> $cfg<nativecall.ccflags> t/04-nativecall/$name.c";
        $l_line = "$cfg<nativecall.ld> $cfg<nativecall.perllibs> 
$cfg<nativecall.lddlflags> $cfg<nativecall.ldflags> 
$cfg<nativecall.ldout>$libname $name$cfg<nativecall.o>";
        @cleanup = << $libname "$name$cfg<nativecall.o>" >>;
    }
    else {
        die "Unknown VM; don't know how to compile test libraries";
    }
    shell($c_line);
    shell($l_line);
}

sub compile_cpp_test_lib($name) is export {
    my @cmds;
    my $VM  := $*VM;
    my $cfg := $VM.config;
    my $libname = $VM.platform-library-name($name.IO);
    @cleanup = $libname;
    if $*DISTRO.is-win {
        @cmds    = "cl /LD /EHsc /Fe$libname t/$name.cpp",
                   "g++ --shared -fPIC -o $libname t/$name.cpp",
    }
    else {
        @cmds    = "g++ --shared -fPIC -o $libname t/$name.cpp",
                   "clang++ -stdlib=libc++ --shared -fPIC -o $libname 
t/$name.cpp",
    }

    my (@fails, $succeeded);
    for @cmds -> $cmd {
        my $handle = shell("$cmd 2>&1", :out);
        my $output = $handle.out.slurp-rest;
        if $handle.out.close.status {
            @fails.push: "Running '$cmd':\n$output"
        }
        else {
            $succeeded = 1;
            last
        }
    }
    fail @fails.join('=' x 80 ~ "\n") unless $succeeded;
}


END {
    #    say "cleaning up @cleanup[]";
    unlink @cleanup;
}

----


* t/05-pointer.t
----
use v6;
use Test;
use NativeCall;
use lib <lib t>;
use CompileTestLib;

compile_test_lib('05-pointer');

class Feature is repr('CStruct') {
      has int32 $.index;
      has num64 $.value;
}

my sub dot(Feature, Feature) returns num64 is native("./05-pointer") { * }

my CArray[Feature] $lhs .= new;
my CArray[Feature] $rhs .= new;
$lhs[5] = Feature.new(index => -1, value => 0e0);
$rhs[5] = Feature.new(index => -1, value => 0e0);

for 1..5 -> $index {
    $rhs[$index - 1] = Feature.new(index => $index, value => 2.5e0);
    $lhs[$index - 1] = Feature.new(index => $index, value => 2.5e0);
}

for ^5 {
    is $lhs[$_].value, 2.5e0, "\$lhs[$_].value = 2.5e0";
    is $rhs[$_].value, 2.5e0, "\$rhs[$_].value = 2.5e0";
    is $lhs[$_].index, $_ + 1, "\$lhs[$_].index = {$_ + 1}";
    is $rhs[$_].index, $_ + 1, "\$rhs[$_].index = {$_ + 1}";
}

is dot($lhs[0], $rhs[0]), [+] ((2.5 * 2.5) xx 5);

done-testing;
----

*** results ***
----
$ mi6 test -v t/05-pointer.t
==> Set PERL6LIB=/home/itoyota/Programs/p6-Foo/lib
==> prove -e /home/itoyota/.rakudobrew/bin/../moar-nom/install/bin/perl6 -r -v 
t/05-pointer.t
t/05-pointer.t .. 
ok 1 - $lhs[0].value = 2.5e0
ok 2 - $rhs[0].value = 2.5e0
ok 3 - $lhs[0].index = 1
ok 4 - $rhs[0].index = 1
ok 5 - $lhs[1].value = 2.5e0
ok 6 - $rhs[1].value = 2.5e0
ok 7 - $lhs[1].index = 2
ok 8 - $rhs[1].index = 2
ok 9 - $lhs[2].value = 2.5e0
ok 10 - $rhs[2].value = 2.5e0
ok 11 - $lhs[2].index = 3
ok 12 - $rhs[2].index = 3
ok 13 - $lhs[3].value = 2.5e0
ok 14 - $rhs[3].value = 2.5e0
ok 15 - $lhs[3].index = 4
ok 16 - $rhs[3].index = 4
ok 17 - $lhs[4].value = 2.5e0
ok 18 - $rhs[4].value = 2.5e0
ok 19 - $lhs[4].index = 5
ok 20 - $rhs[4].index = 5
not ok 21 - 

# Failed test at t/05-pointer.t line 33
# expected: '31.25'
#      got: '6.25'
1..21
# Looks like you failed 1 test of 21
Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/21 subtests 

Test Summary Report
-------------------
t/05-pointer.t (Wstat: 256 Tests: 21 Failed: 1)
  Failed test:  21 
  Non-zero exit status: 1
Files=1, Tests=21,  1 wallclock secs ( 0.02 usr  0.00 sys +  0.51 cusr  0.06 
csys =  0.59 CPU)
Result: FAIL

----

In the above example, dot method accepts entry address of given two vectors and 
computes the dot product of the values they have.
Hence, in the test case 21, dot($lhs[0],$rhs[0]) should return 31.25e0, where 
$lhs[0] is the entry address of the CArray[Feature] (the values it has are 
2.5e0 xx 5) and $rhs[0] is the entry address of the other CArray[Feature] (the 
values it has are 2.5e0 xx 5).

However, in fact, it returns 6.25e0. 
It seems that dot function in C side receive the entry address of the given 
vector correctly, but it fails in incrementing the pointer address and points a 
irrelevant address, because CArray cannot allocate contiguous memory.
I think that NativeCall needs something for allocating contiguous memory.

(I faced this type of error while creating a libsvm bindings for Perl 6: 
https://github.com/cjlin1/libsvm/blob/master/svm.cpp#L294-L314 )


$ perl6 --version
This is Rakudo version 2016.10-309-g3dcc52b built on MoarVM version 
2016.10-71-g9d5c874
implementing Perl 6.c.

Reply via email to