将2D阵列的向量转换为dart/flutter以用于dart-ffi消耗

ojsjcaue  于 2023-03-31  发布在  Flutter
关注(0)|答案(1)|浏览(96)

我的c++函数定义如下:

std::vector<std::array<std::array<float, 160>, 160>> get_masks(float *img_pixels) {...}

我请求社区帮助将此函数Map到dart,以便可以使用它。
我甚至尝试了 ffigen & Chatgpt,但结果并不令人满意。
有没有什么好的资源可以让我学习这个概念?

wztqucjr

wztqucjr1#

溶液:

您可以使用此代码作为开始,但不要忘记释放分配的内存(甚至根据您的特定情况对其进行更多优化,因为它可能不会被优化,它只是为了演示)

  • 在本机一侧:
FFI_PLUGIN_EXPORT float **createDoubleArrayFromVectorOfDoubleArrays(void *vector) {
    std::vector<std::array<std::array<float, 160>, 160>> *vectorOfDoubleArrays = (std::vector<std::array<std::array<float, 160>, 160>> *) vector;
    float **array = new float *[vectorOfDoubleArrays->size()];
    for (int i = 0; i < vectorOfDoubleArrays->size(); i++) {
        array[i] = new float[160 * 160];
        for (int j = 0; j < 160; j++) {
            for (int k = 0; k < 160; k++) {
                array[i][j * 160 + k] = (*vectorOfDoubleArrays)[i][j][k];
            }
        }
    }
    return array;
}
  • 在省道侧(在生成绑定之后):
List<List<Float64List>> generateRandomArrayOfDoubleArrays(int count){
  Pointer<Void> nativeVector = _bindings.createRandomVectorOfDoubleArrays(count); // create a vector of double arrays in native code and return a pointer to it (casted to void*)
  final Pointer<Pointer<Float>> nativeGeneratedArray = _bindings.createDoubleArrayFromVectorOfDoubleArrays(nativeVector);  // generate a list of list of double arrays from nativeVector, each double array is a Float64List of length 160
  // now we convert them to dart lists by iterating and copying the data
  final List<List<Float64List>> generatedArray = [];
  for (int i = 0; i < count; i++) {
    final List<Float64List> generatedArrayRow = [];
    for (int j = 0; j < 160; j++) {
      final Pointer<Float> nativeGeneratedArrayRow = nativeGeneratedArray.elementAt(i).value.elementAt(j);
      final Float64List generatedArrayRowElement = Float64List.fromList(nativeGeneratedArrayRow.asTypedList(160));
      generatedArrayRow.add(generatedArrayRowElement);
    }
    generatedArray.add(generatedArrayRow);
  }
  return generatedArray;
}

结果:

示例应用:完整的工作示例步骤:

  • 运行flutter create --template=plugin_ffi --platforms=android,ios,windows,macos,linux sample_ffi_plugin创建dart ffi项目
  • 转到生成的项目
  • src/中的sample_ffi_plugin.c重命名为sample_ffi_plugin.cpp(在CMakeLists.txt中也重命名
  • sample_ffi_plugin.cpp的内容替换为:
#include "sample_ffi_plugin.h"

FFI_PLUGIN_EXPORT float **createDoubleArrayFromVectorOfDoubleArrays(void *vector) {
    std::vector<std::array<std::array<float, 160>, 160>> *vectorOfDoubleArrays = (std::vector<std::array<std::array<float, 160>, 160>> *) vector;
    float **array = new float *[vectorOfDoubleArrays->size()];
    for (int i = 0; i < vectorOfDoubleArrays->size(); i++) {
        array[i] = new float[160 * 160];
        for (int j = 0; j < 160; j++) {
            for (int k = 0; k < 160; k++) {
                array[i][j * 160 + k] = (*vectorOfDoubleArrays)[i][j][k];
            }
        }
    }
    return array;
}

FFI_PLUGIN_EXPORT void* createRandomVectorOfDoubleArrays(int count){
    std::vector<std::array<std::array<float, 160>, 160>> *vector = new std::vector<std::array<std::array<float, 160>, 160>>;
    for (int i = 0; i < count; i++) {
        std::array<std::array<float, 160>, 160> array;
        for (int j = 0; j < 160; j++) {
            for (int k = 0; k < 160; k++) {
                array[j][k] = j * 160 + k;
            }
        }
        vector->push_back(array);
    }
    return vector;
}
  • sample_ffi_plugin.h的内容替换为:
#if _WIN32
#define FFI_PLUGIN_EXPORT __declspec(dllexport)
#else
#define FFI_PLUGIN_EXPORT
#endif

#ifdef __cplusplus
#include <vector>
#include <array>
extern "C" {
#endif

FFI_PLUGIN_EXPORT void* createRandomVectorOfDoubleArrays(int count);

FFI_PLUGIN_EXPORT float** createDoubleArrayFromVectorOfDoubleArrays(void* vector);

#ifdef __cplusplus
}
#endif
  • 生成绑定:
flutter pub run ffigen --config ffigen.yaml
  • /lib/中的sample_ffi_plugin.dart中的代码替换为:
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
import 'dart:math';
import 'dart:typed_data';

import 'sample_ffi_plugin_bindings_generated.dart';

const String _libName = 'sample_ffi_plugin';

/// The dynamic library in which the symbols for [SampleFfiPluginBindings] can be found.
final DynamicLibrary _dylib = () {
  if (Platform.isMacOS || Platform.isIOS) {
    return DynamicLibrary.open('$_libName.framework/$_libName');
  }
  if (Platform.isAndroid || Platform.isLinux) {
    return DynamicLibrary.open('lib$_libName.so');
  }
  if (Platform.isWindows) {
    return DynamicLibrary.open('$_libName.dll');
  }
  throw UnsupportedError('Unknown platform: ${Platform.operatingSystem}');
}();

/// The bindings to the native functions in [_dylib].
final SampleFfiPluginBindings _bindings = SampleFfiPluginBindings(_dylib);

extension Float64ListExtension on Float64List {
  /// Creates a `double` array from this list by copying the list's
  /// data, and returns a pointer to it.
  ///
  /// `nullptr` is returned if the list is empty.
  Pointer<Double> toDoubleArrayPointer({required Allocator allocator}) {
    if (isEmpty) {
      return nullptr;
    }
    final Pointer<Double> ptr = allocator(sizeOf<Double>() * length);
    for (int i = 0; i < length; i++) {
      ptr[i] = this[i];
    }
    return ptr;
  }
}

List<List<Float64List>> generateRandomArrayOfDoubleArrays(int count){
  Pointer<Void> nativeVector = _bindings.createRandomVectorOfDoubleArrays(count); // create a vector of double arrays in native code and return a pointer to it (casted to void*)
  final Pointer<Pointer<Float>> nativeGeneratedArray = _bindings.createDoubleArrayFromVectorOfDoubleArrays(nativeVector);  // generate a list of list of double arrays from nativeVector, each double array is a Float64List of length 160
  // now we convert them to dart lists by iterating and copying the data
  final List<List<Float64List>> generatedArray = [];
  for (int i = 0; i < count; i++) {
    final List<Float64List> generatedArrayRow = [];
    for (int j = 0; j < 160; j++) {
      final Pointer<Float> nativeGeneratedArrayRow = nativeGeneratedArray.elementAt(i).value.elementAt(j);
      final Float64List generatedArrayRowElement = Float64List.fromList(nativeGeneratedArrayRow.asTypedList(160));
      generatedArrayRow.add(generatedArrayRowElement);
    }
    generatedArray.add(generatedArrayRow);
  }
  return generatedArray;
}
  • 最后转到/example并将main.dart替换为:
import 'package:flutter/material.dart';
import 'dart:async';

import 'package:sample_ffi_plugin/sample_ffi_plugin.dart' as sample_ffi_plugin;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    const textStyle = TextStyle(fontSize: 25);
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Native Packages'),
        ),
        body: SingleChildScrollView(
          child: Container(
            padding: const EdgeInsets.all(10),
            alignment: Alignment.center,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text(
                  'First 100 elements of each of first 10 generated arrays:',
                  style: textStyle,
                ),
                for(int i=0;i<10;i++)
                Text(
                    '${sample_ffi_plugin.generateRandomArrayOfDoubleArrays(1)[0][i].take(100)}',
                    style: textStyle),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

相关问题