我正在尝试使用Java中的C ++ API和JNA。此API使用回调来处理会话事件。
我发现如何使用JNA注册回调的唯一资源是this,它处理C回调,我真的不知道它如何扩展到C ++非静态回调。
编辑:我刚发现this resource,我认为“重访回调”一章可能会有所帮助。
回调的所有函数指针都存储在以下sp_session_callbacks
结构中:
/**
* Session callbacks
*
* Registered when you create a session.
* If some callbacks should not be of interest, set them to NULL.
*/
typedef struct sp_session_callbacks {
void (__stdcall *logged_in)(sp_session *session, sp_error error);
void (__stdcall *logged_out)(sp_session *session);
void (__stdcall *connection_error)(sp_session *session, sp_error error);
void (__stdcall *message_to_user)(sp_session *session, const char *message);
// Other callbacks function pointers
} sp_session_callbacks;
我为描述这个结构而创建的Java类如下:
public class sp_session_callbacks extends Structure{
public Function logged_in;
public Function logged_out;
public Function connection_error;
public Function message_to_user;
}
您认为在这种情况下用com.sun.jna.Function对象表示函数指针是否有意义?
每个会话由一个sp_session
对象表示,该对象是一个C ++ opaque结构。我在初始化它时对sp_session_callbacks
对象有一个句柄。
以下是我的主要课程的代码段:
JLibspotify lib = (JLibspotify)Native.loadLibrary("libspotify", JLibspotify.class);
sp_session_config cfg = new sp_session_config();
/* Some cfg config here */
sp_session_callbacks sessCallbacks = new sp_session_callbacks(); // Handle on my sp_session_callbacks object
cfg.callbacks = sessCallbacks;
PointerByReference sessionPbr = new PointerByReference();
int errorId = lib.sessionCreate(cfg, sessionPbr);
sp_session session = new sp_session(sessionPbr.getValue()); // handle on my sp_session object
我应该如何注册这些回调函数,以便在触发它们时在Java端实际执行某些操作?
谢谢!
修改
使用Callback而不是Function的新代码:
public class sp_session_callbacks extends Structure{
public LoggedIn logged_in;
/* Other callbacks... */
}
public interface LoggedIn extends StdCallCallback {
public void logged_in(sp_session session, int error);
}
主要课程:
JLibspotify lib = (JLibspotify)Native.loadLibrary("libspotify", JLibspotify.class);
sp_session_config cfg = new sp_session_config();
/* Some cfg config here */
sp_session_callbacks sessCallbacks = new sp_session_callbacks(); // Handle on my sp_session_callbacks object
LoggedIn loggedInCallback = new LoggedIn(){
public void logged_in(sp_session session, int error){
System.out.println("It works");
}
};
sessCallbacks.logged_in = loggedInCallback;
/* Setting all the other callbacks to null */
cfg.callbacks = sessCallbacks;
PointerByReference sessionPbr = new PointerByReference();
int errorId = lib.sessionCreate(cfg, sessionPbr);
sp_session session = new sp_session(sessionPbr.getValue()); // handle on my sp_session object
当cfg.logged_in未设置为null但是设置为LoggedIn实例时,sessionCreate()调用抛出JRE致命错误(EXCEPTION_ACCES_VIOLATION 0x0000005)。奇怪的是,2个回调logged_in和connection-error具有相同的签名,并且当设置cfg.connection_error时,它不会抛出任何东西。
答案 0 :(得分:2)
来自javadoc函数表示本机方法,如果要调用java方法,则必须为每个函数指针创建Callback。
如果您只使用java回调而没有本机函数,则只需将Function替换为Callback。 (根据使用的调用约定,您可能希望使用StdCallLibrary.StdCallCallback)
public class sp_session_callbacks extends Structure{
public StdCallCallback logged_in;
public StdCallCallback logged_out;
public StdCallCallback connection_error;
public StdCallCallback message_to_user;
}
sp_session_callbacks calls = new sp_session_callbacks();
calls.logged_out = new StdCallCallback(){
public void someName(sp_session sp){...}
}
答案 1 :(得分:0)
基本上,为了将任何类型的回调连接到对象的成员函数,它必须传递目标函数和对象的实例以满足第一个不可见参数。这里的问题是JNA只接受特定格式的函数。所以,你必须在可能的情况下解决这个问题。遗憾的是,在C ++中,这可能导致使用临时全局变量,但只要您专注于将控制权转移回对象,就可以保持良好的OO设计。
它与“C vs C ++”无关。您只需要了解成员函数调用的含义以及它编译的基本内容。
myObject.memberFunction(); // This is what the programmer sees.
memberFunction(&myObject); // This is what the compiler sees.
成员函数是一个函数的精美描述,它简单地将对象作为第一个参数。它在实际参数列表中是不可见的。
void MyClass::memberFunction() // This is the declaration the programmer sees.
void memberFunction(MyClass* this) // This is what the compiler sees.
从那里,C ++添加了特殊的语义,使其易于以面向对象的方式工作。
简而言之,在JNA本身添加机制之前,您几乎无法将直接连接到对象的成员函数中。