C++ non static callbacks and JNA
I am trying to use a C++ API in Java with JNA. This API uses callbacks to handle sessions events.
The only resource I found on how to register callbacks with JNA is this, and it deals with C callbacks, and I don't really know how it can be extended to C++ non-static callbacks.
EDIT: I just found this resource, I think the "Revisiting Callbacks" chapter might help.
All the function pointers for the callbacks are stored in the following sp_session_callbacks
structure:
/**
* 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;
The Java class I created to described this structure is the following:
public class sp_session_callbacks extends Structure{
public Function logged_in;
public Function logged_out;
public Function connection_error;
public Function message_to_user;
}
Does it make sense to represent the function pointers by com.sun.jna.Function objects in this case in your opinion?
Each session is represented by a sp_session
object, which is a C++ opaque struct. I do have a handle on the开发者_Python百科 sp_session_callbacks
object when I initialize it though.
Here is a code snippet from my main class:
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
How should I register those callbacks function to actually do something on the Java side when they are triggered?
Thank you!
EDIT
New code using Callback instead of 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);
}
Main class:
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
The sessionCreate() call is throwing a JRE fatal error (EXCEPTION_ACCES_VIOLATION 0x0000005) when cfg.logged_in is not set to null but to an instance of LoggedIn. The weird thing is that the 2 callbacks logged_in and connection-error have the same signature, and when cfg.connection_error is set, it doesn't throw anything.
From the javadoc Function represents a native method, if you want to call a java method you have to create a Callback for each function pointer.
If you only use java callbacks and no native Functions you can just replace Function with Callback. (Depending on the calling convention used you might want to use StdCallLibrary.StdCallCallback instead)
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){...}
}
Basically, in order to wire a callback of any kind into a member function of an object, it would have to pass the target function and an instance of the object to satisfy that first invisible parameter. The issue here is that JNA only accepts a particular format of function. So, you have to work around the issue where possible. Sadly, in C++, this can result in the use of temporary global variables, but as long as you focus on transferring control back over to an object, you can maintain good OO design.
It has nothing to do with a "C vs C++" thing. You just need to understand what a member function call entails and basically what it compiles down to.
myObject.memberFunction(); // This is what the programmer sees.
memberFunction(&myObject); // This is what the compiler sees.
A member function is a fancy description of a function that simply takes the object as a first parameter. It's just invisible in the actual parameter list.
void MyClass::memberFunction() // This is the declaration the programmer sees.
void memberFunction(MyClass* this) // This is what the compiler sees.
From there, C++ adds special semantics to make it easy to work in an object-oriented fashion.
In short, you will virtually never be able to wire directly into an object's member function until the JNA itself adds mechanisms to do so.
精彩评论