使用 SWIG 在重载的 C++ 方法中设置类型
Using SWIG for setting types in overloaded C++ method
我正在编写将由不同平台上的应用程序使用的 C++ 库,包括 Android。
Android 使用 build SWIG(我无法更改此选择,而且我从未使用过 swig)。
Java-to-C++ 的自动类型转换工作正常,直到我被分配到 init 方法重载任务。
对于旧的 init
C++ 接口是:
/**
* Initialize classifier from file
* @param modelPath std::string Full path to file with model
* @return Classifier Possible values:
* pointer to Classifier instance in case of success
* nullptr otherwise
*/
static Classifier* init(const std::string& modelPath);
正在生成以下代码:
public static Classifier init(String modelPath) {
long cPtr = CLJNI.Classifier_init__SWIG_0(modelPath);
return (cPtr == 0) ? null : new Classifier(cPtr, true);
}
对于新的 init
C++ 是:
/**
* Initialize classifier from memory block
* @param modelMemory char* Pointer to the memory block where model definition is stored
* @param modelMemorySize size_t Size of memory block allocated for model storage
* @return Classifier Possible values:
* pointer to Classifier instance in case of success
* nullptr otherwise
*/
static Classifier* init(char* modelMemory, const size_t modelMemorySize);
SWIG 生成的 和 Java 代码是:
public static Classifier init(String modelMemory, long modelMemorySize) {
long cPtr = CLJNI.Classifier_init__SWIG_1(modelMemory, modelMemorySize);
return (cPtr == 0) ? null : new Classifier(cPtr, true);
}
但我需要 Java 方法:
Classifier init(byte[] modelMemory, long modelMemorySize);
即char*
必须转换为 byte[]
而不是 String
。
我应该在 Classifier.i 文件(现在看起来如下)中做哪些更改才能使两个 init
方法可供 Java?
调用
%{
#include "Group.h"
#include "Classifier.h"
%}
//Java programmers are afraid of 'delete' method, so we have to rename it.
//Yes, bloody stupid, I know.
%typemap(javadestruct, methodname="dispose", methodmodifiers="public synchronized") CL::Classifier {
if(swigCPtr != 0 && swigCMemOwn) {
swigCMemOwn = false;
$jnicall;
}
swigCPtr = 0;
}
%typemap(javafinalize) CL::Classifier %{
protected void finalize() {
dispose();
}
%}
%newobject CL::Classifier::init;
%include "Classifier.h"
在 SWIG 2.0.11 和 SWIG 3.0.8 上检查了以下解决方案(基于 Flexo's example):
%module Classifier
%{
#include "Group.h"
#include "Classifier.h"
%}
%typemap(jtype) (signed char* modelMemory, const size_t modelMemorySize) "byte[]"
%typemap(jstype) (signed char* modelMemory, const size_t modelMemorySize) "byte[]"
%typemap(jni) (signed char* modelMemory, const size_t modelMemorySize) "jbyteArray"
%typemap(javain) (signed char* modelMemory, const size_t modelMemorySize) "$javainput"
%typemap(in,numinputs=1) (signed char* modelMemory, const size_t modelMemorySize) {
= JCALL2(GetByteArrayElements, jenv, $input, NULL);
= JCALL1(GetArrayLength, jenv, $input);
}
%typemap(freearg) (signed char* modelMemory, const size_t modelMemorySize) {
// Or use 0 instead of ABORT to keep changes if it was a copy
JCALL3(ReleaseByteArrayElements, jenv, $input, , JNI_ABORT);
}
%typemap(javadestruct, methodname="dispose", methodmodifiers="public synchronized") CL::Classifier {
if(swigCPtr != 0 && swigCMemOwn) {
swigCMemOwn = false;
$jnicall;
}
swigCPtr = 0;
}
%typemap(javafinalize) CL::Classifier %{
protected void finalize() {
dispose();
}
%}
%newobject CL::Classifier::init;
%include "Classifier.h"
%include "arrays_java.i"
和 %apply
(写在原始示例中)发现不必要。
这很好用并提供了两个 init
方法:
public static Classifier init(String modelPath) {
long cPtr = CLJNI.Classifier_init__SWIG_0(modelPath);
return (cPtr == 0) ? null : new Classifier(cPtr, true);
}
public static Classifier init(byte[] modelMemory) {
long cPtr = CLJNI.Classifier_init__SWIG_1(modelMemory);
return (cPtr == 0) ? null : new Classifier(cPtr, true);
}
但需要使用 signed char*
类型而不是 char*
。
否则
public static Classifier init(String modelMemory, long modelMemorySize)
生产。
我还发现对于 SWIG 3.0.8 解决方案只能在一个额外的行中:
%apply(char *STRING, size_t LENGTH) { (signed char *modelMemory, size_t modelMemorySize) };
即Classifier.i 是:
%module Classifier
%{
#include "Group.h"
#include "Classifier.h"
%}
%apply(char *STRING, size_t LENGTH) { (signed char *modelMemory, size_t modelMemorySize) };
%typemap(javadestruct, methodname="dispose", methodmodifiers="public synchronized") CL::Classifier {
if(swigCPtr != 0 && swigCMemOwn) {
swigCMemOwn = false;
$jnicall;
}
swigCPtr = 0;
}
%typemap(javafinalize) CL::Classifier %{
protected void finalize() {
dispose();
}
%}
%newobject CL::Classifier::init;
%include "Classifier.h"
对于 SWIG 2.0.11,这种方式会导致编译错误,因为不正确的包装器 (CLJAVA_wrap.cxx) 生成:
SWIGEXPORT jlong JNICALL Java_impl_tools_CLJNI_Classifier_1init_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jbyteArray jarg1) {
jlong jresult = 0 ;
signed char *arg1 = (signed char *) 0 ;
size_t arg2 ;
CL::Classifier *result = 0 ;
(void)jenv;
(void)jcls;
{
if (jarg1) {
arg1 = (char *) jenv->GetByteArrayElements(jarg1, 0); // ERROR HERE: invalid conversion from 'char*' to 'signed char*'
arg2 = (size_t) jenv->GetArrayLength(jarg1);
} else {
arg1 = 0;
arg2 = 0;
}
}
result = (CL::Classifier *)CL::Classifier::init(arg1,arg2);
*(CL::Classifier **)&jresult = result;
{
if (jarg1) jenv->ReleaseByteArrayElements(jarg1, (jbyte *)arg1, 0);
}
return jresult;
}
我正在编写将由不同平台上的应用程序使用的 C++ 库,包括 Android。 Android 使用 build SWIG(我无法更改此选择,而且我从未使用过 swig)。 Java-to-C++ 的自动类型转换工作正常,直到我被分配到 init 方法重载任务。
对于旧的 init
C++ 接口是:
/**
* Initialize classifier from file
* @param modelPath std::string Full path to file with model
* @return Classifier Possible values:
* pointer to Classifier instance in case of success
* nullptr otherwise
*/
static Classifier* init(const std::string& modelPath);
正在生成以下代码:
public static Classifier init(String modelPath) {
long cPtr = CLJNI.Classifier_init__SWIG_0(modelPath);
return (cPtr == 0) ? null : new Classifier(cPtr, true);
}
对于新的 init
C++ 是:
/**
* Initialize classifier from memory block
* @param modelMemory char* Pointer to the memory block where model definition is stored
* @param modelMemorySize size_t Size of memory block allocated for model storage
* @return Classifier Possible values:
* pointer to Classifier instance in case of success
* nullptr otherwise
*/
static Classifier* init(char* modelMemory, const size_t modelMemorySize);
SWIG 生成的 和 Java 代码是:
public static Classifier init(String modelMemory, long modelMemorySize) {
long cPtr = CLJNI.Classifier_init__SWIG_1(modelMemory, modelMemorySize);
return (cPtr == 0) ? null : new Classifier(cPtr, true);
}
但我需要 Java 方法:
Classifier init(byte[] modelMemory, long modelMemorySize);
即char*
必须转换为 byte[]
而不是 String
。
我应该在 Classifier.i 文件(现在看起来如下)中做哪些更改才能使两个 init
方法可供 Java?
%{
#include "Group.h"
#include "Classifier.h"
%}
//Java programmers are afraid of 'delete' method, so we have to rename it.
//Yes, bloody stupid, I know.
%typemap(javadestruct, methodname="dispose", methodmodifiers="public synchronized") CL::Classifier {
if(swigCPtr != 0 && swigCMemOwn) {
swigCMemOwn = false;
$jnicall;
}
swigCPtr = 0;
}
%typemap(javafinalize) CL::Classifier %{
protected void finalize() {
dispose();
}
%}
%newobject CL::Classifier::init;
%include "Classifier.h"
在 SWIG 2.0.11 和 SWIG 3.0.8 上检查了以下解决方案(基于 Flexo's example):
%module Classifier
%{
#include "Group.h"
#include "Classifier.h"
%}
%typemap(jtype) (signed char* modelMemory, const size_t modelMemorySize) "byte[]"
%typemap(jstype) (signed char* modelMemory, const size_t modelMemorySize) "byte[]"
%typemap(jni) (signed char* modelMemory, const size_t modelMemorySize) "jbyteArray"
%typemap(javain) (signed char* modelMemory, const size_t modelMemorySize) "$javainput"
%typemap(in,numinputs=1) (signed char* modelMemory, const size_t modelMemorySize) {
= JCALL2(GetByteArrayElements, jenv, $input, NULL);
= JCALL1(GetArrayLength, jenv, $input);
}
%typemap(freearg) (signed char* modelMemory, const size_t modelMemorySize) {
// Or use 0 instead of ABORT to keep changes if it was a copy
JCALL3(ReleaseByteArrayElements, jenv, $input, , JNI_ABORT);
}
%typemap(javadestruct, methodname="dispose", methodmodifiers="public synchronized") CL::Classifier {
if(swigCPtr != 0 && swigCMemOwn) {
swigCMemOwn = false;
$jnicall;
}
swigCPtr = 0;
}
%typemap(javafinalize) CL::Classifier %{
protected void finalize() {
dispose();
}
%}
%newobject CL::Classifier::init;
%include "Classifier.h"
%include "arrays_java.i"
和 %apply
(写在原始示例中)发现不必要。
这很好用并提供了两个 init
方法:
public static Classifier init(String modelPath) {
long cPtr = CLJNI.Classifier_init__SWIG_0(modelPath);
return (cPtr == 0) ? null : new Classifier(cPtr, true);
}
public static Classifier init(byte[] modelMemory) {
long cPtr = CLJNI.Classifier_init__SWIG_1(modelMemory);
return (cPtr == 0) ? null : new Classifier(cPtr, true);
}
但需要使用 signed char*
类型而不是 char*
。
否则
public static Classifier init(String modelMemory, long modelMemorySize)
生产。
我还发现对于 SWIG 3.0.8 解决方案只能在一个额外的行中:
%apply(char *STRING, size_t LENGTH) { (signed char *modelMemory, size_t modelMemorySize) };
即Classifier.i 是:
%module Classifier
%{
#include "Group.h"
#include "Classifier.h"
%}
%apply(char *STRING, size_t LENGTH) { (signed char *modelMemory, size_t modelMemorySize) };
%typemap(javadestruct, methodname="dispose", methodmodifiers="public synchronized") CL::Classifier {
if(swigCPtr != 0 && swigCMemOwn) {
swigCMemOwn = false;
$jnicall;
}
swigCPtr = 0;
}
%typemap(javafinalize) CL::Classifier %{
protected void finalize() {
dispose();
}
%}
%newobject CL::Classifier::init;
%include "Classifier.h"
对于 SWIG 2.0.11,这种方式会导致编译错误,因为不正确的包装器 (CLJAVA_wrap.cxx) 生成:
SWIGEXPORT jlong JNICALL Java_impl_tools_CLJNI_Classifier_1init_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jbyteArray jarg1) {
jlong jresult = 0 ;
signed char *arg1 = (signed char *) 0 ;
size_t arg2 ;
CL::Classifier *result = 0 ;
(void)jenv;
(void)jcls;
{
if (jarg1) {
arg1 = (char *) jenv->GetByteArrayElements(jarg1, 0); // ERROR HERE: invalid conversion from 'char*' to 'signed char*'
arg2 = (size_t) jenv->GetArrayLength(jarg1);
} else {
arg1 = 0;
arg2 = 0;
}
}
result = (CL::Classifier *)CL::Classifier::init(arg1,arg2);
*(CL::Classifier **)&jresult = result;
{
if (jarg1) jenv->ReleaseByteArrayElements(jarg1, (jbyte *)arg1, 0);
}
return jresult;
}