Build OpenCV libs as part of a flutter plugin fails due to an error related to pointer casting triggered in the C++ standard library

Hi,

I am trying to build a flutter plugin that uses ffi to run native C++ with the OpenCV library and I found these instructions (Implementing edge detection in Flutter 🤓) as guidance. After setting everything up and creating a sample app to use the flutter plugin I am getting the following error during the build step of the sample app

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':simple_edge_detection:buildCMakeDebug[arm64-v8a]'.
> com.android.ide.common.process.ProcessException: ninja: Entering directory `/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/android/.cxx/Debug/2xn6wz61/arm64-v8a'
  [1/3] Building CXX object CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp.o
  FAILED: CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp.o 
  /home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ --target=aarch64-none-linux-android21 --gcc-toolchain=/home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64 --sysroot=/home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64/sysroot -Dnative_edge_detection_EXPORTS -I../../../../../include -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security  -frtti -fexceptions -std=c++11 -fno-limit-debug-info  -fPIC -MD -MT CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp.o -MF CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp.o.d -o CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp.o -c /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp
  In file included from /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.cpp:1:
  In file included from /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.hpp:1:
  In file included from ../../../../../include/opencv2/opencv.hpp:52:
  In file included from ../../../../../include/opencv2/core.hpp:54:
  In file included from ../../../../../include/opencv2/core/base.hpp:58:
  In file included from ../../../../../include/opencv2/core/cvstd.hpp:81:
  In file included from ../../../../../include/opencv2/core/cvstd_wrapper.hpp:11:
  /snap/flutter/current/usr/include/c++/9/memory:121:25: error: cast from pointer to smaller type 'uintptr_t' (aka 'unsigned int') loses information
    const auto __intptr = reinterpret_cast<uintptr_t>(__ptr);
                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1 error generated.
  [2/3] Building CXX object CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp.o
  FAILED: CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp.o 
  /home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ --target=aarch64-none-linux-android21 --gcc-toolchain=/home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64 --sysroot=/home/vlad/Android/Sdk/ndk/23.1.7779620/toolchains/llvm/prebuilt/linux-x86_64/sysroot -Dnative_edge_detection_EXPORTS -I../../../../../include -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security  -frtti -fexceptions -std=c++11 -fno-limit-debug-info  -fPIC -MD -MT CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp.o -MF CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp.o.d -o CMakeFiles/native_edge_detection.dir/media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp.o -c /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp
  In file included from /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/native_edge_detection.cpp:2:
  In file included from /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/ios/Classes/edge_detector.hpp:1:
  In file included from ../../../../../include/opencv2/opencv.hpp:52:
  In file included from ../../../../../include/opencv2/core.hpp:54:
  In file included from ../../../../../include/opencv2/core/base.hpp:58:
  In file included from ../../../../../include/opencv2/core/cvstd.hpp:81:
  In file included from ../../../../../include/opencv2/core/cvstd_wrapper.hpp:11:
  /snap/flutter/current/usr/include/c++/9/memory:121:25: error: cast from pointer to smaller type 'uintptr_t' (aka 'unsigned int') loses information
    const auto __intptr = reinterpret_cast<uintptr_t>(__ptr);
                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1 error generated.
  ninja: build stopped: subcommand failed.

  C++ build system [build] failed while executing:
      /home/vlad/Android/Sdk/cmake/3.18.1/bin/ninja \
        -C \
        /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/android/.cxx/Debug/2xn6wz61/arm64-v8a \
        native_edge_detection
    from /media/vlad/FEF8B6AEF8B66495/Projects/Me/flutter_cpp_opencv_tutorial/simple_edge_detection/android

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 5s
Exception: Gradle task assembleDebug failed with exit code 1

The CMakeLists.txt has the following content

cmake_minimum_required(VERSION 3.16.3)
include_directories(../include)
add_library(lib_opencv SHARED IMPORTED)
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libopencv_java4.so)
set(EDGE_DETECTION_DIR "../ios/Classes")
set(SOURCES
    ${EDGE_DETECTION_DIR}/native_edge_detection.cpp
    ${EDGE_DETECTION_DIR}/edge_detector.cpp
)
add_library(native_edge_detection SHARED ${SOURCES})
target_link_libraries(native_edge_detection lib_opencv)

The build.gradle file has the following content

group 'dev.flutterclutter.simple_edge_detection'
version '1.0-SNAPSHOT'

buildscript {
    ext.kotlin_version = '1.7.10'
    repositories {
        google()
        mavenCentral()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:7.3.0'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion 31

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
        test.java.srcDirs += 'src/test/kotlin'
    }

    defaultConfig {
        minSdkVersion 16
    }

    dependencies {
        testImplementation 'org.jetbrains.kotlin:kotlin-test'
        testImplementation 'org.mockito:mockito-core:5.0.0'
    }

    testOptions {
        unitTests.all {
            useJUnitPlatform()

            testLogging {
               events "passed", "skipped", "failed", "standardOut", "standardError"
               outputs.upToDateWhen {false}
               showStandardStreams = true
            }
        }
    }

    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

    defaultConfig {
        externalNativeBuild {
            cmake {
                cppFlags '-frtti -fexceptions -std=c++11'
                arguments '-DANDROID_STL=c++_shared'
            }
        }
    }
}

The C++ exposed to ffi is the following

#include "native_edge_detection.hpp"
#include "edge_detector.hpp"
#include <stdlib.h>
#include <opencv2/opencv.hpp>

extern "C" __attribute__((visibility("default"))) __attribute__((used))
struct Coordinate *create_coordinate(double x, double y)
{
    struct Coordinate *coordinate = (struct Coordinate *)malloc(sizeof(struct Coordinate));
    coordinate->x = x;
    coordinate->y = y;
    return coordinate;
}

extern "C" __attribute__((visibility("default"))) __attribute__((used))
struct DetectionResult *create_detection_result(Coordinate *topLeft, Coordinate *topRight, Coordinate *bottomLeft, Coordinate *bottomRight)
{
    struct DetectionResult *detectionResult = (struct DetectionResult *)malloc(sizeof(struct DetectionResult));
    detectionResult->topLeft = topLeft;
    detectionResult->topRight = topRight;
    detectionResult->bottomLeft = bottomLeft;
    detectionResult->bottomRight = bottomRight;
    return detectionResult;
}

extern "C" __attribute__((visibility("default"))) __attribute__((used))
struct DetectionResult *detect_edges(char *str) {
    struct DetectionResult *coordinate = (struct DetectionResult *)malloc(sizeof(struct DetectionResult));
    cv::Mat mat = cv::imread(str);

    if (mat.size().width == 0 || mat.size().height == 0) {
        return create_detection_result(
            create_coordinate(0, 0),
            create_coordinate(1, 0),
            create_coordinate(0, 1),
            create_coordinate(1, 1)
        );
    }

    vector<cv::Point> points = EdgeDetector::detect_edges(mat);

    return create_detection_result(
        create_coordinate((double)points[0].x / mat.size().width, (double)points[0].y / mat.size().height),
        create_coordinate((double)points[1].x / mat.size().width, (double)points[1].y / mat.size().height),
        create_coordinate((double)points[2].x / mat.size().width, (double)points[2].y / mat.size().height),
        create_coordinate((double)points[3].x / mat.size().width, (double)points[3].y / mat.size().height)
    );
}

with the corresponding header file

struct Coordinate
{
    double x;
    double y;
};

struct DetectionResult
{
    Coordinate* topLeft;
    Coordinate* topRight;
    Coordinate* bottomLeft;
    Coordinate* bottomRight;
};

extern "C"
struct DetectionResult *detect_edges(char *str);

The ffi implementation is the following

import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:convert' show utf8;
import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';

base class Coordinate extends Struct {
  @Double()
  external double x;

  @Double()
  external double y;

  factory Coordinate.allocate(double x, double y) => calloc<Coordinate>().ref
    ..x = x
    ..y = y;
}

base class NativeDetectionResult extends Struct {
  external Pointer<Coordinate> topLeft;
  external Pointer<Coordinate> topRight;
  external Pointer<Coordinate> bottomLeft;
  external Pointer<Coordinate> bottomRight;

  factory NativeDetectionResult.allocate(
          Pointer<Coordinate> topLeft,
          Pointer<Coordinate> topRight,
          Pointer<Coordinate> bottomLeft,
          Pointer<Coordinate> bottomRight) =>
      calloc<NativeDetectionResult>().ref
        ..topLeft = topLeft
        ..topRight = topRight
        ..bottomLeft = bottomLeft
        ..bottomRight = bottomRight;
}

class EdgeDetectionResult {
  EdgeDetectionResult({
    required this.topLeft,
    required this.topRight,
    required this.bottomLeft,
    required this.bottomRight,
  });

  Offset topLeft;
  Offset topRight;
  Offset bottomLeft;
  Offset bottomRight;
}

typedef DetectEdgesFunction = Pointer<NativeDetectionResult> Function(
    Pointer<Utf8> x);

class EdgeDetection {
  static Future<EdgeDetectionResult> detectEdges(String path) async {
    DynamicLibrary nativeEdgeDetection = _getDynamicLibrary();

    final detectEdges = nativeEdgeDetection
        .lookup<NativeFunction<DetectEdgesFunction>>("detect_edges")
        .asFunction<DetectEdgesFunction>();

    NativeDetectionResult detectionResult = detectEdges(utf8.encode(path) as Pointer<Utf8>).ref;

    return EdgeDetectionResult(
        topLeft: Offset(
            detectionResult.topLeft.ref.x, detectionResult.topLeft.ref.y),
        topRight: Offset(
            detectionResult.topRight.ref.x, detectionResult.topRight.ref.y),
        bottomLeft: Offset(
            detectionResult.bottomLeft.ref.x, detectionResult.bottomLeft.ref.y),
        bottomRight: Offset(detectionResult.bottomRight.ref.x,
            detectionResult.bottomRight.ref.y));
  }

  static DynamicLibrary _getDynamicLibrary() {
    final DynamicLibrary nativeEdgeDetection = Platform.isAndroid
        ? DynamicLibrary.open("libnative_edge_detection.so")
        : DynamicLibrary.process();
    return nativeEdgeDetection;
  }
}

I am not sure how to interpret this error and I could really use your help to get it working. Thanks in advance!

please also show ffi / c++ code, ty.

I updated the post with the requested info

1 Like