2016-12-23 1 views
1

Ich versuche, einen Eingabe- und einen Ausgabepuffer an eine Java-Klasse von C++ zu übergeben. Aus Effizienzgründen muss ich einen ByteBuffer verwenden.SWIG typemap uint8_t * von C/C++ zu java.nio.ByteBuffer

Beide Puffer wurden im C++ Teil zugewiesen und ich muss sie an eine Java-Funktion übergeben, die den Eingabepuffer für einige Berechnungen verwendet und das Ergebnis in den Ausgabepuffer schreibt. Hier

ist ein vereinfachtes Beispiel dafür, wie die C++ wie folgt aussieht:

// SWIG mapped with %feature("director") JavaDelegate; 
class JavaDelegate { 
public: 
    JavaDelegate() {} 
    virtual ~JavaDelegate() {} 
    virtual int compute(const uint8_t* input, size_t inSize, 
         uint8_t* output, size_t maxOutSize, 
         int someFlag) = 0; 
}; 

// assume inputBuf is filled with valid data 
std::vector<uint8_t> inputBuf; 
std::vector<uint8_t> outputBuf(10000); 

// delegate also points to a valid SWIG mapped class 
JavaDelegate* delegate; 

void computeOutput() { 
    int someFlag = 1; 
    int numValuesComputed = delegate->compute(inputBuf.data(), inputBuf.size(), 
              outputBuf.data(), outputBuf.size(), 
              someFlag); 
    std::cout << "Computed " << numValuesComputed << " value from input.\n"; 
} 

Auf der Java-Seite, ich will dieses Ziel erreichen (JavaDelegate Klasse wird von SWIG generiert):

public class Delegate extends JavaDelegate { 

    public long compute(java.nio.ByteBuffer input, 
         java.nio.ByteBuffer output, 
         long someFlag) 
    { 
     // read input 
     // compute something n values from m input values 
     // write result to output 
     return numOutputValues; 
    } 
} 

Das Problem ist, ich kann einfach nicht herausfinden, wie man die Typemaps macht, um ein einfaches char * in einen ByteBuffer zu schreiben, besonders in Verbindung mit der Director-Funktion von SWIG, die für die abstrakte Basisklasse aktiviert ist. Ich weiß, wie man einen ByteBuffer von Java zu C++ übergibt, aber ich kann nicht herausfinden, wie man das Gegenteil tut.

Jede Hilfe wird geschätzt!

+0

ich eine DirectByteBuffer von Java nach C passieren würde ++ für sie, füllen Sie durch Änderung der Adresse und Kapazität, um die Daten zu kopieren zu vermeiden. –

+0

Der Ausgabepuffer ist eigentlich ein Hardwarepuffer, also kann ich ihn nicht wirklich aus Java zuweisen. Natürlich könnte ich es später wieder in den Hardwarepuffer kopieren, aber ich würde es vorziehen, es einfach direkt nach Java zu mappen. – pokey909

+0

http://stackoverflow.com/questions/15414753/shared-memory-between-c-and-java-processes –

Antwort

1

Vorwort: nicht SWIG-ish Lösung ist, ist dies für allgemeine Zwecke, fand ich this etwas in Bezug auf Ihre Frage


Erstens gibt es keine ref Parameter in Java, soweit ich weiß, so vorbei es direkt aus C++ ist keine Option, es sei denn, Sie sind bereit, Ihre Unterschrift zu ändern und es als Rückgabewert zu haben. Dies wäre möglich, indem man typewrapper von Java zu umgehen und mit C++ Erstellung/Einstellpufferregisters Inneren einen la:

public class BufferWrapper { 
    private ByteBuffer mBuffer; 

    public void setBuffer(ByteBuffer buffer) { 
     mBuffer = buffer; 
    } 
} 

um nur diese passieren C++ und haben es zuzuteilen und auf wrapper puffern.

Da ist NewDirectByteBuffer JNI-Funktion, die genau das tut, was Sie brauchen:

Ordnet und gibt eine direkte java.nio.ByteBuffer zum Speicherblock mit Bezug auf die Adresse Speicheradresse beginnt und Kapazität Bytes erstrecken.

nativen Code, der diese Funktion aufruft und gibt das resultierende Byte-Puffer-Objekt Java-Level-Code sollte sicherstellen, dass der Puffer auf einen gültigen Speicherbereich verweist, die für das Lesen und zugänglich ist, falls zu schreiben. Ein Versuch, von Java-Code aus auf einen ungültigen Speicherbereich von zuzugreifen, gibt entweder einen beliebigen Wert zurück, hat keinen sichtbaren Effekt oder verursacht eine nicht angegebene Ausnahme.


P.S wir andere Art und Weise könnte gegangen - ByteBuffer hat wrap Methode, die Sie vorhandene Puffer von byte[] wickeln würde es ermöglichen, die char* nicht genau, aber wir konnten sie (in diesem answer anschauen) heiraten.

Vertiefen Ihrer JNI-Methode implmentation:

jsize n = sizeof(yourBuf); 
jbyteArray arr = (*env)->NewByteArray(env, n); 
(*env)->SetByteArrayRegion(env,arr,0,n, (jbyte*)yourBuf); 

jedoch beachten Sie, dass nach doc diese Funktion Kopie entstehen nicht nur so Einwickeln ich Verhängnis es nicht sehr hilfreich hier

+0

Ich denke, deine Antwort ist genau richtig. Ich habe dieses https://svn.apache.org/repos/asf/qpid/branches/address-refactor2/qpid/cpp/bindings/swig_java_typemaps.i gefunden, das etwas ähnlich dem was du beschreibst, tut. Mir geht es auch gut, meine Signaturen zu ändern. Im Moment versuche ich einen Wrapper zu übergeben, der den Zeiger auf die Daten verfolgt. Dies scheint der richtige Weg zu sein. Ich poste den kompletten Code sobald ich es funktioniere. Danke Oleg! – pokey909

+0

Haben Sie es geschafft, dass es funktioniert? –

0

Ein Weg, um zu gehen ist die C++ Klasse zu erweitern, um einen ByteBuffer zurückzugeben. Grob gesagt, könnte man darüber so:

%extend some::namespace::Buffer { 
    jobject byteBuffer() { 
     if (cached_jvm != 0) { 
      JNIEnv *env = JNU_GetEnv(); 
      return env->NewDirectByteBuffer($self->get(), $self->size()); 
     } 
    } 
} 

Darüber hinaus müssen Sie diesen Code irgendwo in Ihrem swig.i Datei die JENV beim Start zwischenzuspeichern.

%{ 
#include <stdexcept> 
#include "jni.h" 

static JavaVM *cached_jvm = 0; 

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { 
    cached_jvm = jvm; 
    return JNI_VERSION_1_2; 
} 

static JNIEnv * JNU_GetEnv() { 
    JNIEnv *env; 
    jint rc = cached_jvm->GetEnv((void **)&env, JNI_VERSION_1_2); 
    if (rc == JNI_EDETACHED) 
    throw std::runtime_error("current thread not attached"); 
    if (rc == JNI_EVERSION) 
    throw std::runtime_error("jni version not supported"); 
    return env; 
} 
%} 

Sie können möglicherweise ohne die Director-Feature-Unterstützung zu entkommen.

Jetzt können Sie die erweiterte Funktion in Java etwas wie folgt aufrufen:

ByteBuffer bb = (ByteBuffer)nativeBufferReference.byteBuffer(); 
Verwandte Themen