【问题标题】:C++ non static callbacks and JNAC++ 非静态回调和 JNA
【发布时间】:2011-03-04 12:49:37
【问题描述】:

我正在尝试将 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++ 不透明结构。我在初始化 sp_session_callbacks 对象时确实有一个句柄。

这是我的主要课程中的代码 sn-p:

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 端实际执行某些操作?

谢谢!

编辑

使用回调而不是函数的新代码:

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)。奇怪的是,logged_in 和 connection-error 这两个回调的签名相同,而且当设置了 cfg.connection_error 时,它不会抛出任何东西。

【问题讨论】:

  • 您的示例中的所有函数指针都是 c,没有 c++ 功能可以与 jna 混淆。
  • @josefx 我认为将元素作为结构的非静态字段(这是一个面向对象的特性)这一事实是 C++ 的典型特征。
  • 您使用的结构是标准 c 的一部分。我不明白您对非静态字段的意思,只有静态字段的结构将毫无用处。(再说一次,我主要是 java 程序员,所以我可能会混淆静态的含义)
  • @josefx 好的。我也是一名 Java 程序员,对 C/C++ 了解不多,这就是我问的原因。我的意思是非静态字段,是一个可以为结构的 2 个不同实例保存不同值/对象的字段。我虽然这个概念是由 OOP 带来的,所以我认为在不是 OO 语言的 C 中是不可能的,但我没有把握。

标签: java c++ pointers callback jna


【解决方案1】:

从javadoc函数代表一个native方法,如果你想调用一个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){...}
}

【讨论】:

  • @josefx 谢谢,看了我引用的第二篇文章后,我认为是解决方案。我在最初的帖子中添加了新代码。
  • @josefx 我在传递error 参数时遇到问题,该参数的类型为sp_error(整数枚举)。当我使用 int 或 IntByReference 时,它​​不起作用。我怎样才能做到这一点? (我在初始帖子的末尾添加了代码)。谢谢!
  • @josefx 糟糕,抱歉,上一条评论中的信息有误。只有logged_in 会使JRE 崩溃。所以我想是因为它实际上被调用了。
  • @nathanb sp_error 不是通过引用传递的,因此您应该使用 int 本身或创建一个仅包含 int 的类以确保类型安全。
  • @nathanb 你通过引用传递 sp_session 吗?我忘记在我的示例代码中这样做了。它应该使用指向 sp_session 的指针,而不是 sp_session 本身。
【解决方案2】:

基本上,为了将任何类型的回调连接到对象的成员函数中,它必须传递目标函数对象的实例以满足第一个不可见参数。这里的问题是 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 本身添加了这样做的机制,否则您几乎永远无法直接将其连接到对象的成员函数中。

【讨论】:

    猜你喜欢
    • 2014-04-14
    • 1970-01-01
    • 1970-01-01
    • 2022-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-17
    • 2017-02-04
    相关资源
    最近更新 更多