Как вернуть по значению из нативной функции? - PullRequest
10 голосов
/ 14 мая 2019

У меня есть следующий метод C ++, скомпилированный с использованием Visual Studio 2017:

extern "C" __declspec( dllexport )
Info* __stdcall GetInfo(InfoProvider* infoProvider)
{
   static_assert(std::is_pod<Info>::value, "Must be Plain Old Data in order to be safely copied between DLL boundaries");

   Info info = new Info();
   Info->data1 = infoProvider->data1;
   Info->data2 = infoProvider->data2;

   return info;
}

В коде Java он отображается в среде выполнения Java с использованием метода интерфейса со следующей сигнатурой:

Info GetInfo(Pointer infoProvider);

final class Info extends Struct {

    public final Signed32 data1;

    public final Signed32 data2;

    public R2VInfo(final Runtime runtime) {
        super(runtime);

        data1 = new Signed32();
        data2 = new Signed32();
    }
}

Это работает.

Вышеупомянутый метод C ++ вызывает утечку памяти, поэтому я хотел бы изменить его, чтобы он возвращал результат по значению:

extern "C" __declspec( dllexport )
Info __stdcall GetInfo(InfoProvider* infoProvider)
{
   static_assert(std::is_pod<Info>::value, "Must be Plain Old Data in order to be safely copied between DLL boundaries");

   Info info{};
   Info.data1 = infoProvider->data1;
   Info.data2 = infoProvider->data2;

   return info;
}

Я использую то же отображение Java JNR:

Info GetInfo(Pointer infoProvider);

Но это не работает - нарушение прав доступа.Вызывается собственный метод, но с некоторым изменяющимся значением указателя.

Как вернуть по значению в JNR?

1 Ответ

5 голосов
/ 22 мая 2019

JNI построен на старом чистом K & R C, чтобы быть совместимым со всеми доступными компиляторами. Возвращение структуры из функции было введено в C89 и было реализовано значительно позже вместе со стандартом C ++. Сегодня все еще можно найти такой старый компилятор C во многих дружественных к Java средах, таких как небольшие устройства или сим-карты. Поэтому я не думаю, что JNI будет обновлен до C89 или даже C99.

В вашем случае я бы рекомендовал написать дополнительный C-код, который обрабатывает вызов библиотечной функции. Код может быть реализован двумя способами:

  1. Для Info* __stdcall GetInfo(InfoProvider* infoProvider) вы должны написать свободную функцию, например:
extern "C" __declspec( dllexport )
void __stdcall FreeInfo(Info* info)
{
   static_assert(std::is_pod<Info>::value, "Must be Plain Old Data in order to be safely copied between DLL boundaries");

   delete info;
}
  1. Для Info __stdcall GetInfo(InfoProvider* infoProvider) вы должны написать оболочку:
extern "C" __declspec( dllexport )
void __stdcall GetInfo(InfoProvider* infoProvider, Info* info)
{
   static_assert(std::is_pod<Info>::value, "Must be Plain Old Data in order to be safely copied between DLL boundaries");

   Info infoProvider = GetInfo(infoProvider);
   info->data1 = infoProvider.data1;
   info->data2 = infoProvider.data2;
}
...