(Added on August 29, 2020) When passing an instance of Numo :: DFloat as an argument Numo::DFloat(view)#shape= It may not be possible to pass correctly when it is in view like. This happens when you cut out a part of the array. In that case, if you copy it with dup and then pass it, there will be no problem. It doesn't seem to work just by casting.
Please refer to "Writing Ruby methods using C ++" for how to write a Ruby extension library in C ++. I think that you can create a library with almost no effort as long as you write C ++. Here, I will introduce how to make a function written in C into an extension library.
As a simple example, I wrote a function that passes a simple variable. It's just a normal C function. Let's create an extension library that can be called from Ruby using SWIG. A function that returns twice the value and a function that returns the length of the string.
test1.h
#include <string.h>
double twicefold_d(double x);
int string_length(char* str);
test1.c
#include "test1.h"
double twicefold_d(double x){
return(x*2.0);
}
int string_length(char* str){
return(strlen(str));
}
It is convenient to create a header file separately, so I am doing so. The second function takes a string as an argument. Next, create a file for SWIG. testF of module is the module name when calling from Ruby (however, the first character is converted to uppercase). It's just reading the header file.
test.i
%module testF
%{
#include "test1.h"
%}
%include test1.h
extconf.rb
require 'mkmf'
create_makefile("testF")
Put the above files in the same folder and execute the following command.
swig -ruby test.i
ruby extconf.rb
make
You will have testF.bundle (extension depends on the OS). Test program
test1.rb
require "./testF"
p TestF.twicefold_d(3.4)
p TestF.string_length("abcd")
In the above example, only one return value can be taken, but multiple return values are possible by using pointer arguments. Please refer to it as it is written in the SWIG manual. * Search with OUTPUT to find out how to do it.
There are several ways to pass an array. The manual describes how to use carray.i, but it's quite difficult to use.
Numo :: NArray can perform high-speed processing, so I think many people are using it. I will show you how to pass it to a function written in C via it.
A function that takes an array and calculates the sum, and a function that calculates the double value. int n is the number of elements in the array and double x [] is the pointer to the array.
test2.h
double sum_d(int n, double x[]);
void twicefold_dv(int n, double x[], double y[]);
test2.c
# include "test2.h"
double sum_d(int n, double x[]){
double sum=0;
for(int i=0; i<n; i++){
sum +=x[i];
}
return(sum);
}
void twicefold_dv(int n, double x[], double y[]){
for(int i=0; i<n; i++){
y[i]=x[i]*2.0;
}
}
test.i
%module testF
%{
#include "numo/narray.h"
#include "test2.h"
%}
%typemap(in) double [] {
narray_t *nary;
GetNArray($input, nary);
$1 = ($1_ltype)na_get_pointer_for_read($input);
}
%include test2.h
%typemap(in) double [] { So, it is applied to the argument that matches double [] in the header file (test2.h), double ?? []. Here, I'm just passing a pointer to the data in NArray to the function, I'm not checking the data type at all, and I'm only passing the size of the array as int n, so if I make a mistake, I'll definitely get an error. Become. Returning data with double y []. It's a pretty rough method, but it's a level where you can pass array data anyway.
extconf.rb
equire 'mkmf'
dir_config("numo-narray")
create_makefile("testF")
A second line has been added to get the position of numo / narray.h.
swig -ruby test.i
ruby extconf.rb -- --with-numo-narray-include=/opt/local/lib/ruby2.7/gems/2.7.0/gems/numo-narray-0.9.1.8/lib/numo/
make
The second line tells you the location of numa / narray.h, so please rewrite it according to your environment. Don't make a mistake as it is located in the num folder that contains this, not narray.h.
test2.rb
require "numo/narray"
require "./testF"
p TestF.sum_d(4, Numo::DFloat.cast([1.0,2.1,3.2,4.3]))
result= Numo::DFloat.zeros(4)
TestF.twicefold_dv(4, Numo::DFloat.cast([1.0,2.1,3.2,4.3]), result)
p result
The NArray data itself has information on the size of the array, so I improved it to use it. It also casts the data type to Numo :: DFloat. (The data type to be cast is specified by numa_cDFloat) Here, instead of reading with% include as before, it is written directly at the bottom of test.i.
There are two% typemap (in), but the first one double sum_d(int SIZE, double *NARRAY); Matches. The second one is void twicefold_dv(int SIZE, double *NARRAY_in, double *NARRAY_out); I try to match. This is further improved to create an NArray for the return value and return it.
test.i
%module testF
%{
#include "numo/narray.h"
#include "test2.h"
%}
%typemap(in) (int SIZE, double *NARRAY) {
narray_t *nary;
VALUE obj1;
obj1 = rb_funcall(numo_cDFloat, rb_intern("cast"), 1, $input);
GetNArray(obj1, nary);
$2 = ($2_ltype)na_get_pointer_for_read(obj1);
$1 = NA_SHAPE(nary)[0];
}
%typemap(in) (int SIZE, double *NARRAY_in, double *NARRAY_out) (VALUE temp) {
narray_t *nary1, *nary2;
VALUE obj1, obj2;
size_t obj2_shape[1];
obj1 = rb_funcall(numo_cDFloat, rb_intern("cast"), 1, $input);
GetNArray(obj1, nary1);
$2 = ($2_ltype)na_get_pointer_for_read(obj1);
obj2_shape[0] = NA_SHAPE(nary1)[0];
obj2 = rb_funcall(nary_new(numo_cDFloat, 1, obj2_shape), rb_intern("fill"), 1, INT2FIX(0));
temp=obj2;
GetNArray(obj2, nary2);
$3 = ($3_ltype)na_get_pointer_for_read(obj2);
$1 = NA_SHAPE(nary1)[0];
}
%typemap(argout) (int SIZE, double *NARRAY_in, double *NARRAY_out){
$result=temp1;
}
double sum_d(int SIZE, double *NARRAY);
void twicefold_dv(int SIZE, double *NARRAY_in, double *NARRAY_out);
It is designed to return the array calculated by% typemap (argout), but it is not linked with temp in% typemap (in). Since the variable name of temp here is converted to temp1, we expect it to be temp1 in% typemap (argout). It's a pretty dangerous way of writing, but I couldn't find a good way, so it was a compromise. If that causes an error, rewrite it. You can see it by looking at the contents of test_wrap.c.
I haven't checked the dimensions of the array, but there is information in narray.h, so please add it if necessary.
There is no change in other files, so you can use it as it is. The command is exactly the same.
test3.rb
require "numo/narray"
require "./testF"
p TestF.sum_d(Numo::DFloat.cast([1.0,2.1,3.2,4.3]))
p TestF.twicefold_dv(Numo::DFloat.cast([1.0,2.1,3.2,4.3]))
p TestF.sum_d(Numo::SFloat.cast([1.0,2.1,3.2,4.3]))
macOS 10.13.6 Ruby 2.7.1 SWIG 4.0.2
Recommended Posts