给定标题,例如:
#include <iostream>
#include <algorithm>
#include <iterator>
inline void foo(const signed char *arr, size_t sz) {
std::copy_n(arr, sz, std::ostream_iterator<int>(std::cout, "\n"));
}
inline void bar(const signed char *begin, const signed char *end) {
std::copy(begin, end, std::ostream_iterator<int>(std::cout, "\n"));
}
(为了方便起见,我在这里使用了C 11,但是如果您更改了实现,则可以是C或C )
如何包装这些函数以仅在Java端获取一个数组,并使用数组的(已知)大小为这些函数提供第二个参数?
关键是要包装这两个函数,您将需要使用多参数typemap。
序言是SWIG的相当标准。我使用了我个人最喜欢的prgama来自动加载共享库,而界面的用户则无需知道:
%module test
%{
#include "test.hh"
%}
%pragma(java) jniclasscode=%{
static {
try {
System.loadLibrary("test");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. \n" + e);
System.exit(1);
}
}
%}
首先,尽管您将需要使用一些Java类型映射来指示SWIG
byte[]
用作Java接口的两个部分的类型-JNI和调用它的包装器。在generate模块文件中,我们将使用JNI类型jbyteArray
。我们将输入直接从SWIG接口传递到它生成的JNI。
%typemap(jtype) (const signed char *arr, size_t sz) "byte[]"
%typemap(jstype) (const signed char *arr, size_t sz) "byte[]"
%typemap(jni) (const signed char *arr, size_t sz) "jbyteArray"
%typemap(javain) (const signed char *arr, size_t sz) "$javainput"
完成后,我们可以编写一个多参数类型映射:
%typemap(in,numinputs=1) (const signed char *arr, size_t sz) {
$1 = JCALL2(GetByteArrayElements, jenv, $input, NULL);
$2 = JCALL1(GetArrayLength, jenv, $input);
}
intypemap的工作是将JNI调用给出的内容转换为实际函数真正希望作为输入的内容。我曾经numinputs=1
指出两个实函数参数在Java端仅接受一个输入,但是无论如何这都是默认值,因此不需要明确声明。
在此typemap中,$1
是typemap
的第一个参数,即本例中函数的第一个参数。我们通过请求指向Java数组的html" target="_blank">底层存储的指针(实际上可能是副本,也可能不是副本)来进行设置。我们将$2
第二个typemap参数设置为数组的大小。
JCALLn
此处的宏确保类型图可以同时使用C和C ++ JNI进行编译。它扩展为对语言的适当调用。
一旦实函数调用返回,我们需要另一个类型图进行清理:
%typemap(freearg) (const signed char *arr, size_t sz) {
// Or use 0 instead of ABORT to keep changes if it was a copy
JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT);
}
这需要ReleaseByteArrayElements
告诉JVM我们已经完成了数组操作。它需要指针 和
我们从中获得指针的Java数组对象。此外,它需要一个参数,指示内容是否应被复制回 当且仅当
他们进行了修改,我们得到的指针是摆在首位的副本。(我们传递给NULL的参数是指向的可选指针,jboolean
它指示是否已获得副本)。
对于第二个变体,类型映射基本上相似:
%typemap(in,numinputs=1) (const signed char *begin, const signed char *end) {
$1 = JCALL2(GetByteArrayElements, jenv, $input, NULL);
const size_t sz = JCALL1(GetArrayLength, jenv, $input);
$2 = $1 + sz;
}
%typemap(freearg) (const signed char *begin, const signed char *end) {
// Or use 0 instead of ABORT to keep changes if it was a copy
JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT);
}
%typemap(jtype) (const signed char *begin, const signed char *end) "byte[]"
%typemap(jstype) (const signed char *begin, const signed char *end) "byte[]"
%typemap(jni) (const signed char *begin, const signed char *end) "jbyteArray"
%typemap(javain) (const signed char *begin, const signed char *end) "$javainput"
唯一的区别是使用局部变量sz
来end
使用begin
指针计算延展性。
剩下要做的唯一一件事就是告诉SWIG使用我们刚刚编写的类型映射来包装头文件本身:
%include "test.hh"
我使用以下命令测试了这两个功能:
public class run {
public static void main(String[] argv) {
byte[] arr = {0,1,2,3,4,5,6,7};
System.out.println("Foo:");
test.foo(arr);
System.out.println("Bar:");
test.bar(arr);
}
}
哪个按预期工作。
为方便起见,我分享我在写这本使用的文件我的网站。通过依次遵循此答案,可以重建该存档中每个文件的每一行。
作为参考,我们可以在没有任何JNI调用的情况下完成整个过程,使用它%pragma(java) modulecode
来生成一个重载,然后使用重载将输入(在纯Java中)转换为实际函数期望的形式。为此,模块文件将是:
%module test
%{
#include "test.hh"
%}
%include <carrays.i>
%array_class(signed char, ByteArray);
%pragma(java) modulecode = %{
// Overload foo to take an array and do a copy for us:
public static void foo(byte[] array) {
ByteArray temp = new ByteArray(array.length);
for (int i = 0; i < array.length; ++i) {
temp.setitem(i, array[i]);
}
foo(temp.cast(), array.length);
// if foo can modify the input array we'll need to copy back to:
for (int i = 0; i < array.length; ++i) {
array[i] = temp.getitem(i);
}
}
// How do we even get a SWIGTYPE_p_signed_char for end for bar?
public static void bar(byte[] array) {
ByteArray temp = new ByteArray(array.length);
for (int i = 0; i < array.length; ++i) {
temp.setitem(i, array[i]);
}
bar(temp.cast(), make_end_ptr(temp.cast(), array.length));
// if bar can modify the input array we'll need to copy back to:
for (int i = 0; i < array.length; ++i) {
array[i] = temp.getitem(i);
}
}
%}
// Private helper to make the 'end' pointer that bar expects
%javamethodmodifiers make_end_ptr "private";
%inline {
signed char *make_end_ptr(signed char *begin, int sz) {
return begin+sz;
}
}
%include "test.hh"
%pragma(java) jniclasscode=%{
static {
try {
System.loadLibrary("test");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. \n" + e);
System.exit(1);
}
}
%}
除了将数据转换为正确类型所需的显而易见的(两个)副本(没有简单的方法可以从byte[]
到SWIGTYPE_p_signed_char
),然后将其返回还存在另一个缺点-
它特定于函数foo
和bar
,而我们先前编写的类型映射并不特定于给定的函数-
如果碰巧有一个采用两个范围或两个指针+长度组合的函数,它们将在匹配的任何位置应用,甚至多次应用于同一函数。这样做的一个好处是,如果碰巧有其他包装的函数可以给您SWIGTYPE_p_signed_char
回馈,那么您仍然可以根据需要使用重载。即使在您有一个ByteArray
from
的情况下,您%array_class
仍然无法执行生成Java所需的Java指针算法end
为了你。
所示的原始方法在Java中提供了更简洁的界面,并具有不产生过多副本和更可重用的附加优点。
包装的另一种替代方法是%inline
为foo
和编写一些重载bar
:
%inline {
void foo(jbyteArray arr) {
// take arr and call JNI to convert for foo
}
void bar(jbyteArray arr) {
// ditto for bar
}
}
这些在Java接口中以重载形式表示,但是它们仍然是特定于模块的,此外,此处所需的JNI比原本需要的更为复杂-
您需要安排以jenv
某种方式掌握这些内容,而这些内容无法通过默认。这些选项是调用它的缓慢调用,或者是numinputs=0
自动填充参数的类型映射。无论哪种方式,多参数类型映射都看起来更好。
C++ 指针 C++ 允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。 下面的实例中,我们传递一个无符号的 long 型指针给函数,并在函数内改变这个值: #include <iostream> #include <ctime> using namespace std; void getSeconds(unsigned long *par); int main () {
我正在尝试编写一个类模板,在内部它使用一个函数(BFGS优化的实现,由环境提供),接口如下: 其中< code>fn和< code>gr是类型的函数指针 和 分别是。我的< code>C 类模板如下所示: 这样,它可以由任何具有两个指定成员函数的类进行实例化,例如: 这可能吗?或者也许有更好的方法来做到这一点 - 将两个成员函数分别作为函数指针传递?(如果目标函数和相应的梯度很简单,那么编写两个函
当我构建这个应用程序时,我得到错误: 我也试过: 这将错误更改为: 下面是调用这个例程的类和我传递的回调函数的示例:
我有以下代码: 我正在尝试将char和char指针数组传递给函数,但上面的代码会导致以下错误和警告: 我正在努力理解问题是什么。
问题内容: 是否可以通过某种方式将一个函数的范围传递给另一个函数? 例如, 我宁愿直接访问变量,即,不使用类似或的任何东西,而只是直接使用或。 问题答案: 真正访问函数私有范围的唯一方法是在函数内部声明,这样就形成了一个闭包,允许隐式访问变量。 这是您的一些选择。 直接访问 在内部声明。 如果您不想在中使用,则可以将它们都放在更大的容器范围内: 这些是您可以直接使用的变量而无需任何额外的代码来移动
C ++允许您传递指向函数的指针。 为此,只需将函数参数声明为指针类型即可。 下面是一个简单的例子,我们将一个无符号长指针传递给一个函数并更改函数内部的值,该函数反映在调用函数中 - #include <iostream> #include <ctime> using namespace std; void getSeconds(unsigned long *par); int main () {