Привязки Flutter C++ к dart::ffi

Я пытаюсь написать пакет-оболочку для кода C++, который выполняет анализ данных в нашем приложении. Я нашел этот отличный пост, который показывает основные шаги, необходимые для этого. Я больше кодер C/C++, поэтому я немного застрял и не понимаю, что я делаю неправильно?

Это анализы.hpp

#include <memory>
#include <vector>

struct int_array
{
    int* array;
    int len;
};

class cpp_analyses
{
public:
        cpp_analyses();
        ~cpp_analyses() = default;

        int_array get_header_index(int_array idx_list_input);
};

Соответствующий анализ.cpp

#include "analyses.hpp"

cpp_analyses::cpp_analyses()
{

}

int_array cpp_analyses::get_header_index(int_array idx_list_input)
{
        int_array out;

        out.array = new int[2];
        out.len = 2;

        out.array[0] = 1;
        out.array[1] = 2;

        return out;
}

Это анализ-адаптер.hpp

#include "analyses.hpp"

#define EXPORT extern "C" __attribute__((visibility("default"))) __attribute__((used))

EXPORT void* initialize_analyses();
EXPORT int_array  analyses_get_index(void *ptr, struct int_array in_idx);

Соответствующий исходный файл analysis_adapter.cpp

#include "analyses-adapter.hpp"

void* initialize_analyses()
{
        return new cpp_analyses;
}

int_array  analyses_get_index(void *ptr, struct int_array in_idx)
{
        auto typed_ptr = static_cast<cpp_analyses*>(ptr);
        return typed_ptr->get_header_index(in_idx);
}

Со стороны дротика:

import 'dart:ffi';
import 'package:ffi/ffi.dart';

class IntArray extends Struct {
  external Pointer<Int32> data;

  @Int32()
  external int length;
}

typedef _example_init_analyses = Pointer<Void> Function();
typedef _analyses_get_saccades_index = IntArray Function(Pointer<Void> obj, IntArray input);

class Analyses {
        static late DynamicLibrary nativeApiLib;
        external Pointer<Void> _nativeInstance;
        external _analyses_index analyses_index;

        Analyses() {
                nativeApiLib =(DynamicLibrary.open('libanalyses.so')); // android and linux
                _example_init_analyses _initialize_analyses = nativeApiLib.lookup<NativeFunction<_example_init_analyses>>('initialize_analyses').asFunction();
                _nativeInstance = _initialize_analyses();

                analyses_index = nativeApiLib.lookup<NativeFunction<_analyses_index>>('analyses_get_index').asFunction();
        }

        IntArray get_saccades_index(IntArray input)
        {
                return analyses_index(_nativeInstance, input);
        }
}

Наконец в main.dart

import 'package:flutter/material.dart';
import 'package:analyses/analyses.dart';

void main() {
        Analyses OBJ = new Analyses();
        IntArray TMP = IntArray();

        TMP.length = 2;

        IntArray TMP_second = OBJ.get_index(TMP);
}

Весь код строится без проблем. Однако, когда я пытаюсь запустить код, он жалуется на то, что

#0 NoSuchMethodError._throwNew (dart:core-patch/errors_patch.dart:222:5)

#1 Analyses._nativeInstance= (package:analyses/ncapp_analyses.dart)

Я не вижу, где я делаю что-то не так. Любая помощь будет принята с благодарностью! Спасибо

РЕДАКТИРОВАТЬ В соответствии с запросом, пожалуйста, посмотрите полную трассировку стека

Tried calling: _nativeInstance=
#0      NoSuchMethodError._throwNew (dart:core-patch/errors_patch.dart:222:5)
#1      Analyses._nativeInstance= (package:analyses/analyses.dart)
#2      new Analyses (package:analyses/analyses.dart:22:17)
#3      main (package:analyses/main.dart:5:33)
#4      _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:130:25)
#5      _rootRun (dart:async/zone.dart:1426:13)
#6      _CustomZone.run (dart:async/zone.dart:1328:19)
#7      _runZoned (dart:async/zone.dart:1861:10)
#8      runZonedGuarded (dart:async/zone.dart:1849:12)
#9      _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:126:5)
#10     _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#11     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)

Если я уберу внешний перед Указатель _nativeInstance;, я не смогу скомпилировать:

ERROR: lib/ncapp_analyses.dart:16:23: Error: Field '_nativeInstance' should be initialized because its type 'Pointer<Void>' doesn't allow null.
ERROR:  - 'Pointer' is from 'dart:ffi'.
ERROR:  - 'Void' is from 'dart:ffi'.
ERROR:         Pointer<Void> _nativeInstance;
ERROR:                       ^^^^^^^^^^^^^^^

Можете ли вы включить больше трассировки стека? Какая линия бросает?

Richard Heap 29.03.2022 17:23

@RichardHeap -> Я обновил вопрос. Извините за отсутствие информации.

PMDP3 29.03.2022 18:56

Почему вы добавляете ключевое слово external в нескольких местах? Кроме того, ознакомьтесь с новым методом lookupFunction, который может улучшить читабельность.

Richard Heap 29.03.2022 19:13

Например, если я не поставлю «внешний» перед Pointer<Void> _nativeInstance;, код не скомпилируется, потому что _nativeInstance должен быть инициализирован и не может быть нулевым. Я поищу lookupFunction

PMDP3 29.03.2022 20:14

Сделай это late.

Richard Heap 29.03.2022 20:26

Это работает! Большой!! Большое тебе спасибо. Пожалуйста, запишите свой ответ, чтобы я мог подтвердить правильность вашего решения.

PMDP3 29.03.2022 20:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Во-первых, вам нужно отделить класс Dart, представляющий общий объект, от класса Dart, который представляет ваш класс C++.

Первый будет содержать ссылку на разделяемую библиотеку и искомые функции; второй будет просто содержать Pointer<Void> для экземпляра вашего класса C++ - будет по одному экземпляру (и это будет то, что вы освобождаете, когда закончите с этим конкретным экземпляром класса C++).

Например:

import 'dart:ffi';

class SoLibrary {
  factory SoLibrary() {
    // make it a singleton
    _instance ??= SoLibrary._();
    return _instance!;
  }

  SoLibrary._() {
    // todo - check for Platform type
    _nativeApiLib = DynamicLibrary.open('libanalyses.so');
  }

  static SoLibrary? _instance;

  late DynamicLibrary _nativeApiLib;
  late final Pointer<Void> Function() _newAnalyses = _nativeApiLib
      .lookupFunction<Pointer<Void> Function(), Pointer<Void> Function()>(
          'initialize_analyses');
}

class Analyses {
  Pointer<Void> _nativeInstance;

  Analyses() : _nativeInstance = SoLibrary()._newAnalyses();
}

Спасибо за отличный ответ. Это явно намного читабельнее! И это работает :)

PMDP3 29.03.2022 21:29

Другие вопросы по теме