2012-06-11 34 views
1

如何使用Java处理va_list参数,从本地库接收它?在JNA回调函数中处理va_list参数

我正在使用便于通过回调函数进行日志记录的C库。该库是libghoto2,我正在使用JNA封装器libgphoto2-java访问其功能。 有关应如何完成日志记录的示例,请参见this C file中的errordumper方法。

我已经设法使用库的gp_log_add_func添加一个Java编写的回调函数。唯一的问题是,回调函数的签名包含一个va_list参数,我不知道如何处理。

由于C example from earlier显示,va_list args直接传递到vfprintf。读取vfprintf manual,很明显它是某种可迭代的数据结构,它已经被“va_start宏初始化”,并且在迭代使用va_arg后需要使用va_end进行清理。 但我发现保留JVM不会崩溃的唯一方法是使args类型参数的类型为com.sun.jna.Pointer,而String[]Object[]会更合适。

如何从这个va_list中获取数据?

N.B.为了得到gp_log_add_func访问,我添加了一些Java代码:

添置org.gphoto2.jna.GPhoto2Native

int gp_log_add_func(int logLevel, LogFunc func, Pointer data); 

创建org.gphoto2.jna.LogFunc

public interface LogFunc extends Callback { 
    public static final int GP_LOG_ERROR = 0; 
    public static final int GP_LOG_VERBOSE = 1; 
    public static final int GP_LOG_DEBUG = 2; 
    public static final int GP_LOG_DATA = 3; 
    public static final int GP_LOG_ALL = GP_LOG_DATA; 

    //the args argument is a va_list 
    public void log(int logLevel, String domain, String format, Pointer args, Pointer data); 
} 

org.gphoto2.jna.LogFunc的实施和使用:

LogFunc callback = new LogFunc() { 
     public void log(int logLevel, String domain, String format, Pointer args, Pointer data) { 
      System.out.println("[" + domain + "] " + format); 
      System.out.println(args.toString()); 
     } 
}; 
GPhoto2Native.INSTANCE.gp_log_add_func(LogFunc.GP_LOG_ALL, callback, null); 
+1

您必须查看相关平台上的va_list的实际实现(从头文件(vararg.h)开始,并且可能需要深入研究编译器细节)。 va_list通常是一个指向堆栈的指针,各种提取参数只是简单地抵消将指针移动一个适当大小偏移量的操作。你必须使用指针手动提取东西,使用指针等各种数据提取方法。getInt(int offset),取决于您尝试访问的类型。 – technomage

+0

这就是我所害怕的。从纯粹主义的角度来看,JNA/Java业务应该与任何本地业务分开。这是特别令人担忧的,因为我目前正在使用openSuse进行开发,并可能稍后想要在OS X上进行部署。不幸的是,我不太了解C/C++,但是有没有办法使用第二个本地库来转换va_list更多JNA可管理? – derabbink

+0

JNA应该为va_list处理提供映射当然是一个有争议的案例。随意在上面打开一个问题。 – technomage

回答

1

下面是一个例子可变参数执行情况,关于什么的可变参数宏正在做一些提示:

int printf(const char* fmt, ...) { 
    va_list argp; 

    va_start(argp, fmt); // usually something like: argp = (char *)&fmt - sizeof(void *); 

    int arg1 = va_arg(argp, int); // *(int *)argp; argp += sizeof(int); 
    void *arg2 = va_arg(argp, void*); // *(void **)argp; argp += sizeof(void *); 
    float arg3 = va_arg(argp, float); // *(float *)argp; argp += sizeof(float); 

    va_end(argp); // no-op 

} 

所以基本上堆栈指针工作一堆指针运算的。有问题的一部分w/r/t JNA是你没有访问堆栈指针,你可能需要扩展JNA的本地回调机制来专门处理可变回调以提供堆栈指针。

即使这是潜在的问题。从上面的例子可以看出,实际上你需要variadic函数签名的最后一个命名参数的地址,以访问可变参数。一般来说这将非常棘手。

+0

我想我会恢复到只调用gphoto2 cli与匹配stdout和stderr的“聪明的正则表达式”相结合。它可能不是最简单的解决方案,但至少它更有可能工作。 – derabbink

+1

@derabbink仅供参考,使用JavaCPP更容易解决这个问题:它已经具有自动构建,捆绑和加载JNI库的基础结构。我最近不得不为FFmpeg这么做:https://github.com/bytedeco/javacpp-presets/commit/4ed81465898e0ab27b48f3eb232c26d92ffb69d7 –