Обработка аргумента va_list в функции обратного вызова JNA
Как я могу обработать аргумент va_list с Java, получив его из нативной библиотеки?
Я использую библиотеку C, которая облегчает регистрацию через функцию обратного вызова. Библиотека libghoto2, и я использую оболочку JNA libgphoto2-java для доступа к ее функциям. Увидеть errordumper
метод в этом C-файле для примера того, как должна быть сделана регистрация.
Мне удалось добавить написанную на Java функцию обратного вызова, используя библиотеку gp_log_add_func
, Единственная проблема заключается в том, что сигнатура функции обратного вызова содержит va_list
аргумент, что я не знаю, как обрабатывать.
Как показывает пример C из более ранних va_list args
передается непосредственно в vfprintf
, Чтение vfprintf
Из руководства становится ясно, что это какая-то итеративная структура данных, которая "была инициализирована с помощью va_start
макрос "и после итерации с помощью va_arg
очистка с va_end
необходимо. Но единственный способ предотвратить сбой JVM - это args
параметр типа com.sun.jna.Pointer
в то время как String[]
или же Object[]
было бы более подходящим.
Как мне получить данные из этого va_list?
NB. Чтобы получить доступ к 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 ответ
Вот пример реализации varargs с некоторыми подсказками о том, что делают макросы varargs:
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 на нативном уровне, чтобы специально обрабатывать переменные обратные вызовы для предоставления указателя стека.
Даже это потенциально проблематично. Как видно из приведенного выше примера, вам на самом деле нужен адрес последнего именованного аргумента сигнатуры переменной, чтобы получить доступ к аргументам переменной. Это было бы очень сложно сделать в общем.
Может быть, это может быть не актуально для автора здесь, но что-то, что может быть полезно для всех, кто сталкивается с этой проблемой, как и я. Большинство этих проблем связано с обратными вызовами для ведения журнала, и в таких случаях может быть легко просто использовать C и другой вызов JNA в наших интересах.
Например, если есть функция обратного вызова, которая должна быть предоставлена с подписью C как log_write(const char* format, va_list args);
тогда у вас может быть обратный вызов JNA, который сделает еще один вызов vsprintf C для создания окончательной строки.
public interface CLib extends Library {
CLib INSTANCE = (CLib) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"), CLib.class);
int vsprintf(byte[] buffer, String format, Pointer va_list);
} // interface CLib
public static String format(final String format, final Pointer va_args) {
CLib jnaLib = CLib.INSTANCE;
byte[] buffer = new byte[2048];
jnaLib.vsprintf(buffer, format, va_args);
return new String(buffer);
}