2016-12-23 57 views
1

我試圖從C++傳遞一個輸入和一個輸出緩衝區到Java類。 爲了提高效率,我需要使用ByteBuffer。SWIG typemap uint8_t * from C/C++ to java.nio.ByteBuffer

這兩個緩衝區都分配在C++部分中,我需要將它們傳遞給一個java函數,該函數將使用輸入緩衝區進行一些計算並將結果寫入輸出緩衝區。

這裏是的C++如何看起來像一個簡化的例子:

// 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"; 
} 

在Java端,我要實現這一點(JavaDelegate類已通過SWIG生成):

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

問題是,我簡直無法弄清楚如何將類型映射包裝成一個簡單的char *到一個ByteBuffer,特別是與爲抽象基類打開的SWIG的導向器功能相結合。 我知道如何將ByteBuffer從Java傳遞到C++,但我無法找到如何做相反的事情。

任何幫助表示讚賞!

+0

我會通過一個DirectByteBuffer從Java到C++爲它填充,可避免通過改變地址和容量複製數據。 –

+0

輸出緩衝區實際上是一個硬件緩衝區,所以我無法真正從java分配它。當然,我可以在之後將它複製回硬件緩衝區,但如果可能的話,我寧願直接將其直接映射到Java。 – pokey909

+0

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

回答

1

前言:這不是SWIG十歲上下的解決方案,這是通用的,我發現this多少與你的問題


首先,在Java中沒有ref參數,據我所知,這樣傳遞它直接來自C++不是一種選擇,除非您準備好更改您的簽名並將其作爲返回值。這是有可能通過從Java傳遞typewrapper和具有C++創建/設置在其內部緩衝器,一拉繞過:

public class BufferWrapper { 
    private ByteBuffer mBuffer; 

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

只是通過這對C++,並具有其分配並設置緩衝到包裝。

NewDirectByteBuffer JNI函數,不正是你所需要的:

分配並返回一個直接java.nio.ByteBuffer中提到的內存 塊起始於內存地址地址和延長 容量字節。

本機代碼調用此函數並返回結果 字節的緩衝區對象的Java級代碼應確保緩衝 指的是內存的有效區域是用於讀取訪問,並 如果合適的話,寫作。嘗試從Java代碼訪問無效內存位置 將返回任意值,沒有 可見效果,或者導致引發未指定的異常。


P.S我們可以去其他的方式 - 字節緩衝區具有wrap方法,將允許您將現有的byte[]緩衝區,是不完全char*但我們可以嫁給他們(看看這個answer)。

在你的JNI方法implmentation的深度:

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

然而注意,根據doc此功能即被副本不只是包裝,所以我註定它不是非常有幫助這裏

+0

我認爲你的答案是現貨。我發現這個https://svn.apache.org/repos/asf/qpid/branches/address-refactor2/qpid/cpp/bindings/swig_java_typemaps.i,它的功能與您所描述的非常相似。我很好地改變我的簽名。現在,我試圖傳遞一個跟蹤指向數據的指針的包裝器。這似乎是去這裏的路。一旦我得到它的工作,我會發布完整的代碼。謝謝奧列格! – pokey909

+0

你是否設法讓它工作? –

0

的一種方式要做的就是擴展C++類以返回一個ByteBuffer。粗略地說,一個能去這樣的:

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

此外,您將需要的地方在swig.i文件的代碼緩存在啓動時jenv。

%{ 
#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; 
} 
%} 

如果不啓用導演功能支持,您也許能夠脫身。

現在,您可以在一定程度調用Java中的擴展功能是這樣的:

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