Calling C++ from Swift - What Is The Equivalent of Std::Vector<T>

wrapping std::vectordouble from C++ to C for use in Swift

You can write some bridging functions that use C compatible types to call C++ functions. This is very untested code just to give you an idea what you could do:

namespace Fft {
void transform(std::vector<double> &real, std::vector<double> &imag);
}

extern "C" {

struct complex_vector
{
void* real;
void* imag;
};

complex_vector create_complex_vector(size_t size)
{
complex_vector cv;
cv.real = new std::vector<double>(size);
cv.imag = new std::vector<double>(size);
return cv;
}

void complex_vector_push_back(complex_vector cv, double real, double imag)
{
reinterpret_cast<std::vector<double>*>(cv.real)->push_back(real);
reinterpret_cast<std::vector<double>*>(cv.imag)->push_back(imag);
}

void complex_vector_reserve(complex_vector cv, size_t size)
{
reinterpret_cast<std::vector<double>*>(cv.real)->reserve(size);
reinterpret_cast<std::vector<double>*>(cv.imag)->reserve(size);
}

void complex_vector_resize(complex_vector cv, size_t size)
{
reinterpret_cast<std::vector<double>*>(cv.real)->resize(size);
reinterpret_cast<std::vector<double>*>(cv.imag)->resize(size);
}

void fill_complex_vector(complex_vector cv, double* real, double* imag)
{
auto v_real = reinterpret_cast<std::vector<double>*>(cv.real)->data();
auto v_imag = reinterpret_cast<std::vector<double>*>(cv.imag)->data();
auto v_size = reinterpret_cast<std::vector<double>*>(cv.real)->size();

std::copy(real, real + v_size, v_real);
std::copy(imag, imag + v_size, v_imag);
}

void fft_transform(complex_vector cv)
{
Fft::transform(*reinterpret_cast<std::vector<double>*>(cv.real), *reinterpret_cast<std::vector<double>*>(cv.imag));
}

double* complex_vector_real_array(complex_vector cv)
{
return reinterpret_cast<std::vector<double>*>(cv.real)->data();
}

double* complex_vector_imag_array(complex_vector cv)
{
return reinterpret_cast<std::vector<double>*>(cv.imag)->data();
}

size_t complex_vector_size(complex_vector cv)
{
return reinterpret_cast<std::vector<double>*>(cv.imag)->size();
}

void destroy_complex_vector(complex_vector cv)
{
delete reinterpret_cast<std::vector<double>*>(cv.real);
delete reinterpret_cast<std::vector<double>*>(cv.imag);
}

} // extern "C"

If you compile that as C++ the extern "C" {} block will make it so you can call those functions from C.

You could write a C program a bit like this for example:

complex_vector cv = create_complex_vector(1024);

// fill the vector
for(int i = 0; i < complex_vector_size(cv); ++i)
complex_vector_push_back(cv, 0.2 * i, 0.4 * i);

// call the transform
fft_transform(cv);

double* real = complex_vector_real_array(cv);
double* imag = complex_vector_imag_array(cv);
size_t size = complex_vector_size(cv);

// use your transformed data here ...

destroy_complex_vector(cv);

Note: Completely untested code.

Most efficient way of getting large std::vector into Swift?

Some parts are not clear enough but I will try to show you some example.

First of all, you need to prepare a class which can access your contourVector. (I cannot see if it is an instance field or a global variable, if it is an instance field, you may use the existing class.)


Create a header for the prepared class, again you may utilize the existing header, but this header needs to be compiled both in C-context and in C++ context. So, if your existing header contains some declaration which cannot be compiled in C-context, you may need separated two headers or some #ifs.

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface YourClass : NSObject

- (NSInteger)contoursSize;
- (NSInteger)contourSizeAtIndex:(NSInteger)index;
- (CGPoint *)contourAtIndex:(NSInteger)index;

//...

@end

NS_ASSUME_NONNULL_END

Then add 3 methods to the class specified in the header:

#import "YourClass.h"
#import <vector>

typedef std::vector<CGPoint> CGContour;
typedef std::vector<CGContour> CGContours;

static CGContours contourVector;

@implementation YourClass

- (NSInteger)contoursSize {
return contourVector.size();
}
- (NSInteger)contourSizeAtIndex:(NSInteger)index {
return contourVector[index].size();
}
- (CGPoint *)contourAtIndex:(NSInteger)index {
return contourVector[index].data();
}

@end

Please do not forget to include the header inside your Project-Bridging-Header.h:

//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "YourClass.h"

You need to create a Swift side wrapper class, as you cannot create UnsafeBufferPointer in Objective-C.

class YourClassWrapper {
let yourInstance = YourClass()

var count: Int {
return yourInstance.contoursSize()
}

subscript(index: Int) -> UnsafeBufferPointer<CGPoint> {
guard 0..<count ~= index else {fatalError("Index \(index) out of bounds \(0..<count)")}
let start = yourInstance.contour(at: index)
let count = yourInstance.contourSize(at: index)
return UnsafeBufferPointer(start: start, count: count)
}
}

With these preparations above, you can access each CGPoint as:

let wrapper = YourClassWrapper()
let point = wrapper[0][1]

Or you can get the pointer to the first element in CGContour as:

let ptr = wrapper[0].baseAddress!

You may need to modify some parts to fit this into your actual code. Hope you can make it.

Can I have Swift, Objective-C, C and C++ files in the same Xcode project?

YES.

You can mix Swift, C, C++, Objective-C & Objective-C++ files in the same Xcode project.

C

// Declaration: C.h
#ifndef C_h
#define C_h
#ifdef __cplusplus
extern "C" {
#endif
void hello_c(const char * name);
#ifdef __cplusplus
}
#endif
#endif /* C_h */

// Definition: C.c
#include "C.h"
#include <stdio.h>
void hello_c(const char * name) {
printf("Hello %s in C\n", name);
}

C++

// Declaration: CPP.hpp
#pragma once
#include <string>
class CPP {
public:
void hello_cpp(const std::string& name);
};

// Definition: CPP.cpp
#include "CPP.hpp"
#include <iostream>
using namespace std;
void CPP::hello_cpp(const std::string& name) {
cout << "Hello " << name << " in C++" << endl;
}

Objective-C wrapper for C++

// Declaration: CPP-Wrapper.h
#import <Foundation/Foundation.h>
@interface CPP_Wrapper : NSObject
- (void)hello_cpp_wrapped:(NSString *)name;
@end

// Definition: CPP-Wrapper.mm
#import "CPP-Wrapper.h"
#include "CPP.hpp"
@implementation CPP_Wrapper
- (void)hello_cpp_wrapped:(NSString *)name {
CPP cpp;
cpp.hello_cpp([name cStringUsingEncoding:NSUTF8StringEncoding]);
}
@end

Objective-C

// Declaration: Objective-C.h
#import <Foundation/Foundation.h>
@interface Objective_C : NSObject
- (void)hello_objectiveC:(NSString *)name;
@end

// Definition: Objective-C.m
#import "Objective-C.h"
@implementation Objective_C
- (void)hello_objectiveC:(NSString*)name {
printf("Hello %s in Objective-C\n", [name cStringUsingEncoding:NSUTF8StringEncoding]);
}
@end

Objective-C++

// Declaration: Objective-CPP.h
#import <Foundation/Foundation.h>
@interface Objective_CPP : NSObject
- (void)hello_objectiveCpp:(NSString *)name;
@end

// Definition: Objective-CPP.mm
#include <iostream>
#import "Objective-CPP.h"
using namespace std;
@implementation Objective_CPP
- (void)hello_objectiveCpp:(NSString *)name {
cout << "Hello " << [name cStringUsingEncoding:NSUTF8StringEncoding] << " in Objective-C++\n";
}
@end

Swift

// Declaration & definition: Swift.swift
func hello_swift(_ name: String) {
print("Hello \(name) in Swift")
}

Bridging-Header.h

Cannot import CPP.hpp header file, not because of it's naming convention, but because it contains the class keyword.

#import "C.h"
#import "CPP-Wrapper.h"
#import "Objective-C.h"
#import "Objective-CPP.h"

Invocation from Swift

// Invoke C
hello_c("World".cStringUsingEncoding(NSUTF8StringEncoding))

// Can't Invoke C++ without a wrapper
// CPP().hello_cpp("World".cStringUsingEncoding(NSUTF8StringEncoding))
// Invoke C++ through Objective-C
CPP_Wrapper().hello_cpp_wrapped("World")

// Invoke Objective-C
Objective_C().hello_objectiveC("World")

// Invoke Objective-C++
Objective_CPP().hello_objectiveCpp("World")

// Invoke Swift
Swift().hello_swift("World")

.h (Headers)

(See item 3 in this Stack Overflow answer)

.h: this is the tricky part, since they are ambiguously used for all flavors of C, ++ or not, Objective or not. When a .h does not contain a single C++ keyword, like class, it can be added to the ...Bridging-Header.h, and will expose whatever function the corresponding .c or .cpp functionalities it declares. Otherwise, that header must be wrapped in either a pure C or Objective-C API.

Output

Hello World in C
Hello World in C++
Hello World in Objective-C
Hello World in Objective-C++
Hello World in Swift

Comments

Cy-4AH:

Yes. You only need wrap C++ into C or Objective-C to use in Swift.

Tommy

Indeed, I have a project that does exactly that. C++ for the thrust of the abstract cross-platform model stuff with some C parts underneath; Objective-C to wrap the C++ classes for Swift purposes, Swift to bind all that to a subclass of NSDocument, with some custom views that interrogate the C stuff.

MaddTheSane

Added the extern "C" wrapper as per your excellent suggestion. To invoke the C method void hello_c(const char * name) from C++ method hello_cpp(const std::string& name), add #include "C.h" and call hello_c(name.c_str());.

Keith Adler

The new SO-32541268: Now with parameters!


► Find this solution on GitHub and additional details on Swift Recipes.

Interacting with C++ classes from Swift

I've worked out a perfectly manageable answer. How clean you'd like this to be is entirely based upon how much work you're willing to do.

First, take your C++ class and create C "wrapper" functions to interface with it. For example, if we have this C++ class:

class MBR {
std::string filename;

public:
MBR (std::string filename);
const char *hexdump();
const char *imageType();
const char *bootCode();
const char *partitions();
private:
bool readFile(unsigned char *buffer, const unsigned int length);
};

We then implement these C++ functions:

#include "MBR.hpp"

using namespace std;
const void * initialize(char *filename)
{
MBR *mbr = new MBR(filename);

return (void *)mbr;
}

const char *hexdump(const void *object)
{
MBR *mbr;
static char retval[2048];

mbr = (MBR *)object;
strcpy(retval, mbr -> hexdump());
return retval;
}

const char *imageType(const void *object)
{
MBR *mbr;
static char retval[256];

mbr = (MBR *)object;
strcpy(retval, mbr -> imageType());
return retval;
}

The bridge header then contains:

#ifndef ImageReader_hpp
#define ImageReader_hpp

#ifdef __cplusplus
extern "C" {
#endif

const void *initialize(char *filename);
const char *hexdump(const void *object);
const char *imageType(const void *object);

#ifdef __cplusplus
}
#endif

#endif /* ImageReader_hpp */

From Swift, we can now instantiate the object and interact with it like so:

let cppObject = UnsafeMutablePointer<Void>(initialize(filename))
let type = String.fromCString(imageType(cppObject))
let dump = String.fromCString(hexdump(cppObject))
self.imageTypeLabel.stringValue = type!
self.dumpDisplay.stringValue = dump!

So, as you can see, the solution (which is actually rather simple) is to create wrappers that will instantiate an object and return a pointer to that object. This can then be passed back into the wrapper functions which can easily treat it as an object conforming to that class and call the member functions.

Making It Cleaner

While this is a fantastic start and proves that it is completely feasible to use existing C++ classes with a trivial bridge, it can be even cleaner.

Cleaning this up would simply mean that we remove the UnsafeMutablePointer<Void> from the middle of our Swift code and encapsulate it into a Swift class. Essentially, we use the same C/C++ wrapper functions but interface them with a Swift class. The Swift class maintains the object reference and essentially just passes all method and attribute reference calls through the bridge to the C++ object!

Having done this, all of the bridging code is completely encapsulated in the Swift class. Even though we are still using a C bridge, we are effectively using C++ objects transparently without having to resort to recoding them in Objective-C or Objective-C++.

How to Import C++ vector data to Swift

You can't use std::vector from Swift, so don't return a std::vector.

Since your vector contains instances of UIImage, simply return a NSArray:

+ ( NSArray< UIImage * > * )convert: ( UIImage * )image
{
Converter converter;
converter.image = image;

NSMutableArray * images = [ NSMutableArray new ];

for( auto i: converter.main() )
{
[ images addObject: i ];
}

return images;
}

Convert NSArray (Swift) to vector (C++)

Array<Int> is bridged as NSArray<NSNumber *>

Using your code, you will need to use intValue to give you an int:

int myInt = [myArray[j] intValue];



Related Topics



Leave a reply



Submit