Передать указатель или ссылку на указатель в JNA для аргумента указателя в C

#java #pointers #dll #jna

#java #указатели #dll #jna

Вопрос:

В C у меня есть следующий заголовочный файл:

 /** Default constructor */
CLPLIB_EXPORT Clp_Simplex *CLP_LINKAGE Clp_newModel(void);

/** Destructor */
CLPLIB_EXPORT void CLP_LINKAGE Clp_deleteModel(Clp_Simplex *model);
  

Пытаясь импортировать это с помощью JNA, я предполагаю, что я должен указать их как таковые:

 public static native PointerByReference Clp_newModel();

public static native void Clp_deleteModel(Pointer pModel);
  

Правильно ли это, должно ли это быть Clp_deleteModel(PointerByReference pModel) ? Как ни странно, оба, похоже, работают в очень простом тесте, хотя первое имеет для меня больше смысла. Я предполагаю, что JNA выполняет часть своей магии.

 // option 1
PointerByReference a = Clp_newModel();
ChangeSomthingIntheModel(a,2);
Clp_deleteModel(a.getPointer());
ChangeSomthingIntheModel(a,2); // the JVM signals "illegal memory access here

// option 2
PointerByReference a = Clp_newModel();
ChangeSomthingIntheModel(a,2);
Clp_deleteModel(a); // passing the PointerByReference here! 
ChangeSomthingIntheModel(a,2); // the JVM signals "illegal memory access here
  

Ответ №1:

Глядя на документацию CLP, кажется, что либо Pointer или PointerByReference будет работать в этом упрощенном примере.

В JNA существует три общих представления указателей:

  • Pointer Класс, который имеет несколько методов для чтения и записи из встроенной памяти, на которую указывают.
  • PointerType Класс, который может быть расширен для представления указателя без каких-либо других функций, лучше, если вам не нужна Pointer функциональность.
  • Коллекция <something>ByReference классов, которые являются указателями на определенные типы объектов.

Поскольку все, что вы когда-либо делаете, это манипулируете значением указателя, любое из них будет работать: более важным моментом является то, что вы передаете объект-указатель идентичного класса, который вы извлекли из Clp_newModel() которого, который является указателем (на то, с чем вы никогда не сталкивались).

Примечание: эта часть вашего кода может не выполнять то, что вы ожидаете от нее:

 Clp_deleteModel(a.getPointer());
  

getPointer() Метод возвращает указатель класса, а не значение, на которое указывается. Нет принципиальной разницы (кроме типа класса) между a и a.getPointer() в вашем использовании. (Возможно, вы имеете в виду использовать a.getValue() , который возвращает указанное значение, которое будет отличаться и, вероятно, не то, что вы хотите.)

В настоящее время вы извлекаете a PointerByReference , поэтому у вас есть доступ (через .getValue() ) к тому, на что указывается, который выглядит как a CLP_LINKAGE , который, похоже, не является объектом, которым вы когда-либо будете манипулировать. Таким образом, вы могли бы получить Pointer там равнину (не зная, на что она указывает) вместо этого. И вы бы передали тот же указатель на Clp-deleteModel(a) .

Если вы никогда не обращаетесь к указанному значению, вы можете просто использовать Pointer , однако, как правило, это лучшая практика для ограничения API, безопасности типов и самодокументируемого кода для определения расширения класса PointerType . В вашем случае CLPSimplexPtr extends PointerType это был бы хороший выбор.

Если необходимо понять значение указателя (the CLP_LINKAGE ), на которое указывает возвращаемый понитер, затем используйте соответствующие <whatever>ByReference ByReference методы расширения setValue() и реализации getValue() .

Возможно, вас заинтересует, что, похоже, существует clp-java проект, который реализует CLP использование BridJ , альтернативную библиотеку Java-to-native (которая использует JNA, но имеет много оптимизаций для C ). Их определение предназначено Pointer<CLPSimplex> для обоих отображений, которые будут соответствовать CLPSimplexPtr классу, если вы напишете его в JNA — это все еще оформленный простой указатель, но типобезопасный.

Комментарии:

1. Если я правильно понимаю, JNA действительно творит какую-то магию. Указатель и PointerByrRference обрабатываются как одно и то же, если — и только если — они используются последовательно с помощью ввода / вывода, перечисленных в заголовочном файле C. Для моей реализации я должен изменить часть структуры, которую clp-java имеет на данный момент. Если мне повезет (большое, если), мне может быть разрешено протолкнуть это вверх по течению. Ради упражнения (мой первый проект с dll и Java 11) я сначала пытаюсь использовать JNA, а не bridgJ 🙂

2. Это правильно. Указатель по ссылке — это 2 указателя, сам указатель и значение, на которое он указывает, которое также является указателем. Но если вы никогда не используете этот указатель, вам не нужен указатель по ссылке. (Но он не сломается, если вы его используете, как вы продемонстрировали.)

3. Я думаю, что ваш лучший mappipng — это CLPSimplexPtr extends PointerType . Просто оформленный указатель, но сохраняет безопасность типов.

4. Я успешно реализовал последнее. Не только потому, что это типобезопасно, но и улучшает читаемость кода. Merci.