Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bridging C++ code into my swift code. What file extensions go to which c based language in XCode?

As a Swift coder I am so confused by the apparent fragmentation in the c side of things. Somehow there is Objective-C, Objective-C++, c, and c++ and they all have there .h, .hpp, .m, .cpp, etc file extensions however I cannot find great documentation of which extension goes to which file type in XCode (although I am fairly sure for c++ it should be .hpp and .cpp).

This confusion has lead me state where I have no real idea of what I am doing when trying to expose a c++ class to my swift code. Let me explain my strategy.

I have a bridging file that was autogenerated by XCode which I have modified

Bridging-Header.h

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

#include <stdio.h>
#include <stdlib.h>

int* main1() {
    int *num;

    num = (int *)malloc(1000 * sizeof(int));
    for (int i = 0; i < 10; i++) {
        num[i] = i;
    }
    num[10] = 10;
    free(num);
    return num;
}

If I am interpreting things right XCode considers this to be the header file of Objective-C code. Indeed this function is exposed to swift and works as intended.

I then created another file because if I try to import my c++ code in that file it tries to compile it like objective-c which does not go well. This file I believe would be the implementation of this header and I have been told it would be a safe place to import my c++ code.

There is also Bridging-Header.m

#ifndef Bridging_Header_h
#define Bridging_Header_h
#import "Bridging-Header.h"
#import "rectangle.hpp"

int main2() {
    Rectangle* r = new Rectangle();
    return 2;
}

#endif /* TryPost_Bridging_Header_h */

This code I was expecting XCode to treat like Objective-c++ code thus allowing my to safely import my c++ code additionally I thought it would be automatically linked to its corresponding header since if I tried to import it from my other file it would cause an import cycle leading to tons of issues.

However those assumptions must be wrong as this code doesnt appear to even be compiled. Swift does not see the function main2 does not show up in swift.

There is also Rectangle.hpp

#ifndef rectangle_hpp
#define rectangle_hpp

#include <stdio.h>

int main3() {
    return 3;
}
class Rectangle {
    int main();
};

#endif /* fancy_hpp */

I believe XCode should interpret this as C++ code. There is also

Rectangle.cpp

#include "rectangle.hpp"

int Rectangle::main() {
    std::cout << "Hello world!";
    return 99;
}

int main4() {
    return 99;
}

Neither of these seem to be getting compiled either.

I am very confused because I don't know how to even begin dealing with imports between all of these languages nor what I should name the files if I want the compiler to automatically treat them like a certain language. How can I get my Rectangle class to be exposed to swift and maybe even print out "Hello World" when called (even though I am doubtful that printing from c++ is possible.

To be honest I dont really care about the main2 function it is just there for testing purposes and because I think I need that file to get my c++ in. If it is not included in the final project I would not mind. main1, main2, main4 and Rectangle are all important however.

like image 865
J.Doe Avatar asked Jan 02 '23 18:01

J.Doe


1 Answers

You cannot directly use c/c++ code in swift because the type systems are different. Objective c/c++ is mixed meaning it understand both c/c++ types and swift types. To consume say Rectangle c++ class, you would wrap this in a objective c/c++ class and import the objective c/c++ header in <Project>-Bridging-Header.hpp file to expose it to Swift.

Swift by default treats all files with extension -

*.m - as objective c files
*.mm - as objective c++ files
*.hpp - as (objective) c/c++ header files
*.cpp - as c++ files
*.c - as c files

Now coming to your example, If i understand correctly you want to use c++ class (say Rectangle) in Swift. Here's the layered view of the solution.

+----------------------------------+
|  Rectangle (c++)                 |
+----------------------------------+
|  RectangleWrapper (objc++)       |
+----------------------------------+
|  Bridging header                 |
+----------------------------------+
|  Swift                           |
+----------------------------------+

Native class

// Rectangle.h
class Rectangle {
  public:
   void draw();
}

// Rectangle.cpp
void Rectangle::draw() {
  cout << "draw rectangle" << endl;
}

objc++ Wrapper

// RectangleWrapper.hpp
@interface RectangleWrapper : NSObject {
@private
  void* rect;
}
- (RectangleWrapper*) init;
- (void) draw;
@end

// RectangleWrapper.mm
#import "Rectangle.h"
@implementation RectangleWrapper

- (RectangleWrapper*) init {
  rect = (void*)new Rectangle;  // create c++ class instance
  return self;           // return objc++ instance
}

- (void) draw {
  Rectangle* r = (Rectangle*)rect;
  r->draw();  // call c++ method
}

@end

Bridging-Header

Expose your objective c++ class to swift world by simply importing the objc++ header file into Bridging-Header.hpp.

// Bridging-Header.hpp
#import "RectangleWrapper.hpp"

Swift

Now simply use the objective c++ RectangleWrapper class as any other swift class.

// use-rectangle.swift
let rect = RectangleWrapper()
rect.draw()

code

See complete code on github

like image 185
Sunil Avatar answered Jan 05 '23 17:01

Sunil