JNA передает структуру, содержащую указатель на структуру (структуры) и указатель на примитив

#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;
}

  

Итак:

  1. Сопоставления JAN (намеренно) наивны, но действительно ли они глупы? Если да, то каковы логические типы?

  2. Если у меня уже есть массив QueueInfo , могу ли я просто установить указатель на первый элемент этого массива? Или мне нужно выделить массив с помощью Structure::toArray ? Структуры не имеют конструктора, отличного от конструктора по умолчанию, должны ли они иметь?

  3. У меня есть массив с плавающими приоритетами очереди, но как мне установить указатель? Действительно ли это должен быть указатель или что-то еще? Значение с плавающей точкой[]?

Я могу найти множество вопросов по 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, которые содержат указатель на массив примитивов в другом месте памяти, в то время как другой имеет дело с непрерывным буфером структур по ссылке. И ответы немного отличаются.