Schreiben Sie Ruby-Methoden mit C (Numo :: NArray)

(Hinzugefügt am 29. August 2020) Beim Übergeben einer Instanz von Numo :: DFloat als Argument Numo::DFloat(view)#shape= Es ist möglicherweise nicht möglich, korrekt zu übergeben, wenn es wie angezeigt wird. Dies geschieht, wenn Sie einen Teil des Arrays ausschneiden. In diesem Fall gibt es kein Problem, wenn Sie es mit dup kopieren und dann übergeben. Es scheint nicht nur durch Casting zu funktionieren.

Einführung

Informationen zum Schreiben von Ruby-Erweiterungsbibliotheken in C ++ finden Sie unter "Schreiben von Ruby-Methoden mit C ++". Ich denke, dass Sie eine Bibliothek fast ohne Aufwand erstellen können, solange Sie C ++ schreiben. Hier zeigen wir Ihnen, wie Sie eine in C geschriebene Funktion in eine Erweiterungsbibliothek verwandeln.

Übergabe an eine einfache Variable

Als einfaches Beispiel habe ich eine Funktion geschrieben, die eine einfache Variable übergibt. Es ist nur eine normale C-Funktion. Erstellen wir eine Erweiterungsbibliothek, die mit SWIG von Ruby aus aufgerufen werden kann. Eine Funktion, die den doppelten Wert zurückgibt, und eine Funktion, die die Länge der Zeichenfolge zurückgibt.

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));
}

Es ist praktisch, eine separate Header-Datei zu erstellen. Die zweite Funktion verwendet eine Zeichenfolge als Argument. Erstellen Sie als Nächstes eine Datei für SWIG. Die testF des Moduls ist der Modulname beim Aufruf von Ruby (das erste Zeichen wird jedoch in Großbuchstaben konvertiert). Es liest nur die Header-Datei.

test.i


%module testF
%{
#include "test1.h"
%}

%include test1.h

extconf.rb


require 'mkmf'
create_makefile("testF")

Legen Sie die obigen Dateien in denselben Ordner und führen Sie den folgenden Befehl aus.

swig -ruby test.i
ruby extconf.rb
make

Sie haben testF.bundle (Erweiterung hängt vom Betriebssystem ab). Testprogramm

test1.rb


require "./testF"
p TestF.twicefold_d(3.4)
p TestF.string_length("abcd")

Im obigen Beispiel kann nur ein Rückgabewert verwendet werden, aber mit dem Zeigerargument sind mehrere Rückgabewerte möglich. Bitte beziehen Sie sich darauf, wie es im SWIG-Handbuch beschrieben ist. * Suchen Sie mit OUTPUT, um herauszufinden, wie es geht.

Beim Übergeben eines Arrays

Es gibt verschiedene Möglichkeiten, ein Array zu übergeben. Das Handbuch beschreibt die Verwendung von carray.i, ist jedoch recht schwierig zu verwenden.

Verwendung von Numo :: NArray

Numo :: NArray kann Hochgeschwindigkeitsverarbeitung durchführen, daher denke ich, dass viele Leute es verwenden. Ich werde Ihnen zeigen, wie Sie es an die in C geschriebene Funktion übergeben können.

Eine Funktion, die ein Array empfängt und die Summe berechnet, und eine Funktion, die den Doppelwert berechnet. int n ist die Anzahl der Elemente im Array und double x [] ist der Zeiger auf das 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 [] { Es wird also auf das Argument angewendet, das mit double [] in der Header-Datei (test2.h), double ?? [] übereinstimmt. Hier übergebe ich nur einen Zeiger auf die Daten in NArray an die Funktion, überprüfe den Datentyp überhaupt nicht und übergebe nur die Größe des Arrays als int n. Wenn ich also einen Fehler mache, erhalte ich definitiv einen Fehler. Werden. Rückgabe von Daten mit doppeltem y []. Es ist eine ziemlich grobe Methode, aber es ist eine Ebene, auf der Sie trotzdem Array-Daten übergeben können.

extconf.rb


equire 'mkmf'
dir_config("numo-narray")
create_makefile("testF")

Eine zweite Zeile wurde hinzugefügt, um die Position von numo / narray.h zu ermitteln.

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

In der zweiten Zeile wird der Speicherort von numa / narray.h angegeben. Schreiben Sie ihn daher entsprechend Ihrer Umgebung neu. Machen Sie keinen Fehler, da er sich im numo-Ordner befindet, der dies enthält, nicht 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

Verbesserte Version

Die NArray-Daten selbst enthalten Informationen zur Größe des Arrays, daher habe ich sie verbessert, um sie zu verwenden. Außerdem wird der Datentyp in Numo :: DFloat umgewandelt. (Der zu konvertierende Datentyp wird durch numa_cDFloat angegeben.) Anstatt wie zuvor mit% include zu lesen, wird hier direkt am Ende von test.i geschrieben.

Es gibt zwei% Typemap (in), aber die erste double sum_d(int SIZE, double *NARRAY); Stimmt überein mit. Der zweite ist void twicefold_dv(int SIZE, double *NARRAY_in, double *NARRAY_out); Ich versuche zu passen. Dies wird weiter verbessert, um ein NArray für den Rückgabewert zu erstellen und es zurückzugeben.

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);

Es gibt das von% typemap (argout) berechnete Array zurück, ist jedoch nicht mit temp in% typemap (in) verknüpft. Da der Variablenname von temp hier in temp1 konvertiert wurde, erwarten wir, dass er in% typemap (argout) temp1 ist. Es ist eine ziemlich gefährliche Art zu schreiben, aber ich konnte keinen guten Weg finden, also war es ein Kompromiss. Wenn dies zu einem Fehler führt, schreiben Sie ihn neu. Sie können es sehen, indem Sie sich den Inhalt von test_wrap.c ansehen.

Ich habe die Abmessungen des Arrays nicht überprüft, aber es gibt Informationen in narray.h. Fügen Sie diese daher gegebenenfalls hinzu.

Es gibt keine Änderung in anderen Dateien, so dass Sie es so verwenden können, wie es ist. Die Befehle sind genau gleich.

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

Schreiben Sie Ruby-Methoden mit C (Numo :: NArray)
Schreiben Sie Ruby-Methoden mit C (Teil 1)
Schreiben Sie Ruby-Methoden mit C ++ (Teil 2) Benchmark
Schreiben Sie Code mit Ruby-Klassen und -Instanzen
Über Ruby-Methoden
Versuchen Sie es mit der Methode java.lang.Math
Informationen zu Ruby-Instanzmethoden
[Ruby] -Methode, Instanzmethode ...
Ich habe einen C-Parser (wie) mit PEG in Ruby geschrieben