#java #jna
#java #jna
Вопрос:
Я использую JNA и нахожу ее очень простой для извлечения данных из собственной библиотеки, но изо всех сил пытаюсь понять, как сделать это наоборот, т. Е. передать структурированные данные в собственный метод.
Я использую небольшой пример из части библиотеки, которую я пытаюсь вызвать.
Определения типов в собственной библиотеке следующие:
typedef struct CreateInfo {
int count; // Number of queue infos
const QueueInfo* queues; // Zero-or-more queue info structures
} CreateInfo;
typedef struct QueueInfo {
int count; // Number of queue priorities
const float* priorities; // 'array' of queue priorities
} QueueInfo;
Итак, у нас есть a, CreateInfo
которое ссылается на ряд QueueInfo
, каждое из которых содержит список значений с плавающей запятой.
Наивная реализация этих структур на JNA может быть следующей (порядок полей, конструкторы и т.д. опущены для краткости):
public class CreateInfo extends Structure {
public int count;
public QueueInfo.ByReference queues;
}
public QueueInfo extends Structure {
int count;
public Pointer priorities;
}
Итак:
-
Сопоставления JAN (намеренно) наивны, но действительно ли они глупы? Если да, то каковы логические типы?
-
Если у меня уже есть массив
QueueInfo
, могу ли я просто установить указатель на первый элемент этого массива? Или мне нужно выделить массив с помощьюStructure::toArray
? Структуры не имеют конструктора, отличного от конструктора по умолчанию, должны ли они иметь? -
У меня есть массив с плавающими приоритетами очереди, но как мне установить указатель? Действительно ли это должен быть указатель или что-то еще? Значение с плавающей точкой[]?
Я могу найти множество вопросов по SO и веб-интерфейсам в целом для получения структур из собственной библиотеки, но относительно немного для передачи структурированных данных. И во всех примерах, которые я нашел, используются разные подходы к одной и той же проблеме, которые кажутся очень сложными для того, что должно быть довольно простым (?), Поэтому я теряюсь в поисках «правильного» подхода.
Я подозреваю, что я не задаю правильные вопросы, что, вероятно, означает, что я упускаю что-то фундаментальное в JNA.
Надеюсь, какая-нибудь добрая душа сможет указать, что не так с приведенным выше наивным кодом JNA и как его можно заполнить данными на стороне Java.
Комментарии:
1. Таким образом, похоже, что «стандартный» ответ на вопрос № 2 заключается в том, что необходимо создать экземпляр массива структур с помощью
Structure::toArray
, напримерfinal QueueInfo[] queues = (QueueInfo[]) new QueueInfo().toArray(size); for(int n = 0; n < size; n) { ... }
2. Частичный ответ на # 1 заключается в том, что поле ‘указатель на структуру массива’ должно быть
Structure.ByReference
. Соответствующим образом модифицировал JNA.3. # 1: Зависит от вашего определения глупости. # 2: Использование
Structure.toArray()
метода более удобно, потому что вам не нужноread()
извлекать каждую структуру из собственной памяти. Вы также можете использоватьPointer
и создать экземпляр структуры из соответствующего смещения с помощью конструктора указателя. # 3:Pointer
в целом это хорошее отображение. В итоге вы получите буфер, выделенный самим пользователем, который вы можете использоватьPointer.getFloatArray()
для извлечения значений.4. @DanielWiddis Не уверен, что ты подразумеваешь под «извлечением значений»? Мне не нужно ничего извлекать, у меня уже есть данные в коде Java, мне просто нужно передать их в родную библиотеку в виде поля в структуре. Или я не понимаю вашу точку зрения? Возможно, вы могли бы предоставить краткий фрагмент псевдокода, объясняющий, что вы имеете в виду?
5. Извините, я думал, вы читаете выделенную C память. Обычно используется противоположный шаблон,
Memory buffer = new Memory(bytes)
гдеbytes
достаточно места для ваших данных.Memory
является подклассомPointer
, поэтому у вас есть доступ кwrite()
методам, в данном случаеbuffer.write(long offset, float[] buf, int index, int length)
. УстановитеPointer
в вашей структуре значениеbuffer
, и это должно сработать.
Ответ №1:
1 — Сопоставления JNA
Сопоставления предназначены для прямой привязки типов на стороне Java к соответствующим типам собственной стороны. Когда память, необходимая для этих сопоставлений, хорошо известна, JNA работает очень хорошо. К сожалению, когда объем встроенной памяти, подлежащей отображению, является переменным, это требует некоторой работы по выделению и отображению требуемой встроенной памяти. Есть несколько способов сделать это с различными уровнями абстракции / контроля.
2 — уже есть QueueInfo[] (часть 1)
С тем, как вы определили QueueInfo
в своем вопросе, это бесполезно. Вы определили только класс на стороне Java, но Pointer
класс подразумевает указатель на собственную память. Вам следует изменить свой класс, чтобы он расширялся Structure
и использовался public
в вашем count
поле. Обратите внимание, что при создании экземпляра этой структуры будет выделена только собственная память для int
и Pointer
. Память для самого массива необходимо будет выделить отдельно.
3 — выделить плавающий массив
Как я упоминал в комментариях, один из способов сделать это — выделить собственную память для массива с плавающей точкой:
Memory buffer = new Memory(count * Native.getNativeSize(Float.TYPE));
Затем, предполагая, что вы float[] buf
определили, вы можете скопировать это в собственную память, используя
buffer.write(0L, buf, 0, count);
Затем вы можете просто использовать buffer
в качестве priorities
поля вашего QueueInfo
экземпляра.
2 — уже есть QueueInfo[] (часть 2)
Теперь к вопросу, вы не можете просто установить указатель на первый элемент, если не знаете, что у вас есть непрерывный массив на стороне C. Вы можете использовать Structure::toArray
для выделения памяти (с последующим заполнением каждого элемента) или отдельно создать массив (смежных) указателей и скопировать Pointer
значение из ваших отдельно выделенных структур. Для toArray
варианта вам не нужен конструктор указателей, если вы напрямую задаете значения в сгенерированном массиве, но конструктор указателей может упростить копирование (из одного блока встроенной памяти в другой).
Краткие сведения
Вариант 1: создание экземпляров отдельных QueueInfo
структур с использованием Pointer.write()
метода для массива float. Может быть полезно создать конструктор, который принимает float[]
в качестве аргумента и устанавливает count
, а также выделяет и устанавливает priorities
переменную, как описано выше. Затем создайте массив Pointer
s для CreateInfo
структуры и скопируйте указатель ссылки на каждый элемент.
Вариант 2: создайте массив структур, использующих Structure::toArray
для выделения собственной памяти; затем выполните итерацию по этой структуре и непосредственно создайте QueueInfo
структуры с соответствующим индексом.
Комментарии:
1. Спасибо за объяснение (и ваши другие ответы на соответствующий вопрос), я думаю, что теперь я понимаю это! Я отвечу на оба вопроса, предполагая, что я разберусь с этим. Кстати, ваш ответ на # 2 был вызван моей ошибкой, конечно
QueueInfo
, это расширение структуры JNA, я просто перепутал вырезание и вставку.2. В качестве отступления: есть ли способ в SO связать два опубликованных мной вопроса, которые, как я теперь знаю, по сути одинаковы?
3. Если вы отредактируете свой исходный вопрос, чтобы включить ссылку на другой пост SO, он должен появиться на правом поле в разделе «Связанный». Однако, ответив на ваш другой вопрос, они не совсем совпадают. Этот имеет дело в первую очередь с массивом структур ByValue, которые содержат указатель на массив примитивов в другом месте памяти, в то время как другой имеет дело с непрерывным буфером структур по ссылке. И ответы немного отличаются.