Структура передачи JNA, содержащая указатель на структуру (и) и указатель на примитив - PullRequest
1 голос
/ 11 марта 2019

Я использую 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;

Таким образом, у нас есть 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 Ответ

1 голос
/ 16 марта 2019

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)

Теперь на вопрос, вы не можете просто установить указатель на первый элемент, если вы не знаете, что выиметь непрерывный массив С-стороны.Вы можете использовать Structure::toArray для выделения памяти (а затем заполнять каждый элемент) или отдельно создавать массив (смежных) указателей и копировать значение Pointer из ваших отдельно выделенных структур.Для варианта toArray вам не нужен конструктор указателя, если вы непосредственно устанавливаете значения в сгенерированном массиве, но конструктор указателя может упростить копирование (из одного блока памяти в другой).

Сводка

Вариант 1: создание отдельных структур QueueInfo с использованием метода Pointer.write() для массива с плавающей запятой.Может быть полезно создать конструктор, который принимает float[] в качестве аргумента и устанавливает count, а также выделяет и устанавливает переменную priorities, как описано выше.Затем создайте массив Pointer s для структуры CreateInfo и скопируйте указатель ссылки каждого элемента.

Вариант 2: создайте массив структур, используя Structure::toArray для выделения собственной памяти;затем выполните итерацию по этой структуре и непосредственно создайте структуры QueueInfo с соответствующим индексом.

...