Objective-C wrapper for C++ function call from Swift

Hello, my fellows Swift developers.

I have to deal with a closed source C++ library for which I decided to create an Obj-C class just to wrap the functions call to the C++.

How bad it would be to just return the C++ pointers from Obj-C to deal with it on Swift (I really dislike Obj-C)? For example, some of the C++ functions return an Array (on the C++ side it’s a (Struct *)malloc...).

My question is:

  • Should I loop through this array and create a new Struct for each element and add them to a new Array on Obj-C and return this to be used in Swift (my guts says this is the right way)?
  • Or can I just return this pointer and call it from Swift and create an UnsafeBufferPointer, and then an Array(unsafeBufferPtr) and deal with it straight from Swift (this is what the lazy me is doing right now)?

BTW, I “forward declare” the C++ Struct on Obj-C header – so it can also be visible for Swift – and return the pointer as

- (const MyStruct *)myObjCFunction {
MyStruct *cppPtr = (MyStruct*)::cppFunctionCall();
return cppPtr;
}

On Swift this is imported as func myObjCFunction() -> UnsafePointer<MyStruct>. Then, on Swift I do:

let objcPtr = objcClassInstance.myObjCFunction()
let buffer = UnsafeBufferPointer(
            start: objcPtr,
            count: size // There is a function on C++ which returns the size of the array
        )
var myArray = Array(buffer)
defer {
    buffer.deallocate()
}

It works, but it feels wrong.

You didn't show MyStruct, I'll assume it is a POD structure.

Note that once you have UnsafePointer<MyStruct> plus a number of elements you can use it directly in a way that resembles array subscripting without the need of an array:

    for i in 0 ..< count {
        let x: S = pointer[i]
        do_something_with_it(x)
    }

In many instances this is just enough.

Yes, MyStruct is just a POD. It's an image representation with the following structure:

typedef struct MyStruct {
    Byte * data;
    int32_t width;
    int32_t height;
    int32_t stride;
    int32_t channels;
} MyStruct;

On the C++ side, data is allocated with malloc... The lib gives me a function to deallocate this destroyImagePointer(unsigned char* myStructPtr);. On Swift, I will get these images and create a [CGImage]() array. So, I loop through the Array<MyStruct> from the previous code snippet:

for image in myArray:
    let data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, image.data, image.stride * image.height, kCFAllocatorNull)
    var dataProvider = CGDataProvider(
                dataInfo: nil,
                data: CFDataGetBytePtr(data),
                size: CFDataGetLength(data),
                releaseData: { _, data, _ in 
                    objcClassInstance.destroyImagePointer(UnsafeMutablePointer(mutating: data.assumingMemoryBound(to: UInt8.self)))
                }
            )
    let cgImage = CGImage(
                ...,
                provider: dataProvider!,
               ...,
            )

The question remains, can I just forward the C++ pointer to Swift through Obj-C? The problem is I'm dealing with memory leaks and I don't know if I'm the one doing something wrong writing this integration between this C++ library and Swift to be used on iOS applications or if the leak is on the C++ lib itself.

I see no problem with that. Moreover you can even avoid obj-c and use C++ allocated pointer directly in Swift. AFAIU your problem you have one malloced block where MyStruct reside back to back, and each of those has a malloced allocated pointer for the "data" bytes. I further assume that "destroyImagePointer" while taking a pointer to MyStruct as a parameter doesn't deallocate that pointer, only the underlying "data" myStruct points to. And perhaps you have another call in C++ that deallocates the outer malloced pointer with all myStructs once you done dealing with them.

I'd minimise extra allocations on the swift side. A sketch implementation:

func makeImages(structs: UnsafePointer<MyStruct>, count: Int) -> [CGImage] {
    (0 ..< count).map { i in
        let s = structs[i]
        let context = CGContext(data: UnsafeMutableRawPointer(mutating: s.data), width: Int(s.width), height: Int(s.height), bitsPerComponent: 8, bytesPerRow: Int(s.stride), space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: 0, releaseCallback: { a, b in
               // ...
            },
            releaseInfo: nil
        )!
        return context.makeImage()!
    }
}

let structs = getStructs()
let count = ...
let images = makeImages(structs: structs, count: count)
deallocateStructs(structs)

@tera Thank you so much for your kind reply. I will try to implement your suggestion.
I'm not used to C/C++ so I have no idea what I'm doing :rofl:. You know when you say yes for a Freelancing project and then you pray for it to end asap?