[英]JNA passing the equivalent of swift pointer from Java Android to C
[英]Passing pointer to pointer to float from Java through JNA to a C dynamic library
Catboost提供了一個動態C庫 ,理論上可以從任何編程語言中使用。
我正在嘗試使用JNA通過Java調用它。
我在頭文件中定義的CalcModelPrediction
函數遇到問題,如下所示:
EXPORT bool CalcModelPrediction(
ModelCalcerHandle* calcer,
size_t docCount,
const float** floatFeatures, size_t floatFeaturesSize,
const char*** catFeatures, size_t catFeaturesSize,
double* result, size_t resultSize);
在Java中,我將接口函數定義如下:
public interface CatboostModel extends Library {
public Pointer ModelCalcerCreate();
public String GetErrorString();
public boolean LoadFullModelFromFile(Pointer calcer, String filename);
public boolean CalcModelPrediction(Pointer calcer, int docCount,
PointerByReference floatFeatures, int floatFeaturesSize,
PointerByReference catFeatures, int catFeaturesSize,
Pointer result, int resultSize);
public int GetFloatFeaturesCount(Pointer calcer);
public int GetCatFeaturesCount(Pointer calcer);
}
然后我這樣稱呼它:
CatboostModel catboost;
Pointer modelHandle;
catboost = Native.loadLibrary("catboostmodel", CatboostModel.class);
modelHandle = catboost.ModelCalcerCreate();
if (!catboost.LoadFullModelFromFile(modelHandle, "catboost_test.model"))
{
throw new RuntimeException("Cannot load Catboost model.");
}
final PointerByReference ppFloatFeatures = new PointerByReference();
final PointerByReference ppCatFeatures = new PointerByReference();
final Pointer pResult = new Memory(Native.getNativeSize(Double.TYPE));
float[] floatFeatures = {0.5f, 0.8f, 0.3f, 0.3f, 0.1f, 0.5f, 0.4f, 0.8f, 0.3f, 0.3f} ;
String[] catFeatures = {"1", "2", "3", "4"};
int catFeaturesLength = 0;
for (String s : catFeatures)
{
catFeaturesLength += s.length() + 1;
}
try
{
final Pointer pFloatFeatures = new Memory(floatFeatures.length * Native.getNativeSize(Float.TYPE));
for (int dloop=0; dloop<floatFeatures.length; dloop++) {
pFloatFeatures.setFloat(dloop * Native.getNativeSize(Float.TYPE), floatFeatures[dloop]);
}
ppFloatFeatures.setValue(pFloatFeatures);
final Pointer pCatFeatures = new Memory(catFeaturesLength * Native.getNativeSize(Character.TYPE));
long offset = 0;
for (final String s : catFeatures) {
pCatFeatures.setString(offset, s);
pCatFeatures.setMemory(offset + s.length(), 1, (byte)(0));
offset += s.length() + 1;
}
ppCatFeatures.setValue(pCatFeatures);
}
catch (Exception e)
{
throw new RuntimeException("Couldn't initialize parameters for catboost");
}
try
{
if (!catboost.CalcModelPrediction(
modelHandle,
1,
ppFloatFeatures, 10,
ppCatFeatures, 4,
pResult, 1
))
{
throw new RuntimeException("No prediction made: " + catboost.GetErrorString());
}
else
{
double[] result = pResult.getDoubleArray(0, 1);
log.info("Catboost prediction: " + String.valueOf(result[0]));
Assert.assertFalse("ERROR: Result empty", result.length == 0);
}
}
catch (Exception e)
{
throw new RuntimeException("Prediction failed: " + e);
}
我試過將Pointer
, PointerByReference
和Pointer[]
傳遞給CalcModelPrediction
函數,以代替float **floatFeatures
和char ***catFeatures
但是沒有任何效果。 我總是遇到分段錯誤,大概是當CalcModelPrediction
函數試圖通過調用floatFeatures[0][0]
和catFeatures[0][0]
來獲取floatFeatures
和catFeatures
的元素時。
所以問題是,將Java中的多維數組通過JNA傳遞到C的正確方法是什么,在多維數組中它可以被視為指向值的指針?
有趣的是,僅float **floatFeatures
然后僅調用*floatFeatures
的CalcModelPredictionFlat
函數在傳遞PointerByReference
時工作得很好。
更新-5.5.2018
第1部分
在嘗試通過稍微修改原始的Catboost .cpp和.h文件並重新編譯libcatboost.so庫來調試segfault之后,我發現該segfault是由於我將C中的size_t
映射到Java中的int
所致。 修復此問題后,我在Java中的接口函數如下所示:
public interface CatboostModel extends Library {
public boolean LoadFullModelFromFile(Pointer calcer, String filename);
public boolean CalcModelPrediction(Pointer calcer, size_t docCount,
Pointer[] floatFeatures, size_t floatFeaturesSize,
String[] catFeatures, size_t catFeaturesSize,
Pointer result, size_t resultSize);
}
其中size_t
類的定義如下:
public static class size_t extends IntegerType {
public size_t() { this(0); }
public size_t(long value) { super(Native.SIZE_T_SIZE, value); }
}
第2部分:深入研究Catboost代碼,我注意到**floatFeatures
被行訪問,例如floatFeatures[i]
而***catFeatures
被行和列訪問,例如catFetures[i][catFeatureIdx]
。
在將Java中的floatFeatures
更改為Pointer
數組之后,我的代碼開始使用經過訓練的沒有分類特征的模型,即catFeatures
長度為零。
但是,此技巧catFeatures
用於通過雙下標運算符[i][catFeatureidx]
訪問的[i][catFeatureidx]
。 因此,現在,我修改了原始的Catboost代碼,使其可以接受char **catFeatures
字符串數組。 在Java接口函數中,我設置了String[] catFeatures
。 現在,我可以一次對一個元素進行預測,這並不理想。
我設法使其與原始的Catboost代碼和libcatboost.so
。
Java接口函數是這樣定義的。 請注意,為了模擬浮點值和字符串的2D數組(或指向指針的指針),我使用Pointer[]
類型。
public interface CatboostModel extends Library {
public boolean LoadFullModelFromFile(Pointer calcer, String filename);
public boolean CalcModelPrediction(Pointer calcer, size_t docCount,
Pointer[] floatFeatures, size_t floatFeaturesSize,
Pointer[] catFeatures, size_t catFeaturesSize,
Pointer result, size_t resultSize);
}
之后,我像這樣填充floatFeatures
和catFeatures
參數(此處為一些虛擬數據)。 請注意,對於字符串,我使用的是JNA的StringArray
。
float[] floatFeatures = {0.4f, 0.8f, 0.3f, 0.3f, 0.1f, 0.5f, 0.4f, 0.8f, 0.3f, 0.3f} ;
String[] catFeatures = {"1", "2", "3", "4"};
final Pointer pFloatFeatures = new Memory(floatFeatures.length * Native.getNativeSize(Float.TYPE));
final Pointer[] ppFloatFeatures = new Pointer[2];
for (int dloop=0; dloop<10; dloop++) {
pFloatFeatures.setFloat(dloop * Native.getNativeSize(Float.TYPE), floatFeatures[dloop]);
}
ppFloatFeatures[0] = pFloatFeatures;
ppFloatFeatures[1] = pFloatFeatures;
final Pointer[] ppCatFeatures = new Pointer[catFeatures.length];
final Pointer pCatFeatures = new StringArray(catFeatures);
ppCatFeatures[0] = pCatFeatures;
ppCatFeatures[1] = pCatFeatures;
最后,我將這些參數傳遞給Catboost:
if (!catboost.CalcModelPrediction(
modelHandle,
new size_t(2L),
ppFloatFeatures, new size_t((long)floatFeatures.length),
ppCatFeatures, new size_t((long)catFeatures.length),
pResult, new size_t(2L)
))
{
throw new RuntimeException("No prediction made: " + catboost.GetErrorString());
}
要獲得預測,我們可以執行以下操作:
double[] result = pResult.getDoubleArray(0, 2);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.