Can I mix Swift with C++? Like the Objective-C .mm files
Asked Answered
V

13

137

I just changed my .m files to .mm and use C++. Is there a way to do the same with Swift?

Valverde answered 4/6, 2014 at 16:28 Comment(0)
P
113

No. When you switch from .m to .mm you are actually switching from Objective-C to a different language (which has many subtle differences) called Objective-C++. So you're not really using C++; you're using Objective-C++ which accepts most C++ as input (in the same way that C++ accepts most but not all C as input). When I say it's not quite C++, consider a C++ file that includes a variable named nil (which is legal C++) and then try to compile that as Objective-C++.

Swift doesn't have the same relationship. It is not a superset of C or C++, and you can't directly use either in a .swift file.

"Using Swift with Cocoa and Objective-C" also tells us:

You cannot import C++ code directly into Swift. Instead, create an Objective-C or C wrapper for C++ code.

Phantasm answered 4/6, 2014 at 16:35 Comment(15)
An intriguing nuance.Formalize
Pretty annoying actually - all of us with Cocoa apps incorporating C/C++ code bases now have to maintain projects written in 3 languages....Icelander
I suggest that Objective-c++ is not a different language but a mix of c++ and Objective-c difference is subtle but its still thereDishonor
How is "nil" legal C++ ? There's no such thing (at least in standard c++) Please provide another exampleBrickkiln
but from your answer it sounds like you can indeed call c++ from swift, provided there's a C-interface to those methods. If the OP is thinking like me, then he's hoping to share logic code between multiple platformsBrickkiln
Yes, just as you can access C++ from ObjC if you give it a pure-C interface, you can also access C++ from Swift if you give it a pure-C interface.Phantasm
But with ObjC you can just go .mm your files and be (almost) done. Not so with Swift.Redo
@rewolf, I think he means a variable named nil, like int nilMonatomic
I did, but in fairness it isn't really a great explanation. The same is true in C.Phantasm
gotcha! misunderstood :/ sorry :)Brickkiln
Ok - so what about Swift 2 and C++? :-)Mercorr
@Jay: Not really. Wrappers can be auto-generated pretty easily, given a handful of procedures for method name/selector translation and type coversions. So after choices of Swift for your app, and C++ for the lib code you're using, that's zero additional languages you need to deal with on a day-to-basis / up to two additional (Obj-C, and some shell lang for running the auto-gens) you should know well enough to set up and maintain from time-to-time. That's assuming rolling your own though; it's possible that there's a good wrapper-auto-gen lib out there.Tipton
@SlippD.Thompson Actually it's my own application code / core business logic that's written in C++ - can't afford the overhead of maintaining wrappers every time I'm changing anything in my code, which is like all the time as part of normal development processIcelander
@Icelander Sounds like auto-gen wrappers are the way to go then, since you understand both sides well.Tipton
@SlippD.Thompson well.. still hoping for a more seamless solution somewhere down the road!Icelander
I
178

The confusion may come from the assumption that merely changing a file extension from .m to .mm is all you need to bridge the languages, when, in reality, it does nothing of that sort. It is not the .mm that causes friction with .cpp, it is the .h header which must positively not be a C++ header.


Same project: Yes.

In the same project, you can happily mix C, C++, Objective-C, Objective C++, Swift, and even Assembly.

  1. ...Bridging-Header.h: you expose C, Objective-C and Objective-C++ to Swift using this bridge
  2. <ProductModuleName>-Swift.h: exposes automatically your Swift classes marked with @objc to Objective-C
  3. .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.

Same file: No.

In the same file, you can't mix all 5. In the same source file:

  1. .swift: you can't mix Swift with anything
  2. .m: you can mix Objective-C with C. (@Vinzzz)
  3. .mm: you can mix Objective-C with C++. This bridge is Objective-C++. (@Vinzzz).
  4. .c: pure C
  5. .cpp: you can mix C++ & Assembly (@Vality)
  6. .h: ubiquitous and ambiguous C, C++, Objective-C or Objective-C++, so the answer is it depends.

References

Ibert answered 13/9, 2015 at 20:27 Comment(6)
Correction : In the same .m source file, Objective-C being a strict C superset, you CAN mix Objective-C and C ...Maillot
No problem, I don't even care for the credit, as long as the answer is the correct one ;) BTW, .mm is for mixing Objective-C and C++ (C and C++ are two different things, in spite of the name)Maillot
I would mention that unlike objective C, C++ is not a C superset so you cannot mix both C and C++ in a .cpp file. Only C++ and assembly.Cinnabar
In case anyone finds that this well detailed answer doesn't quite help when trying to expose Swift files into their Objective-C/C++ file (Xcode is complaining that the Swift header file is not found). I found that I had to import "ProductName/ProductModuleName-Swift.h" in my Objective-C/C++ source file. I found this somewhat buried at: developer.apple.com/library/ios/documentation/Swift/Conceptual/…Jeffjeffcoat
Good point. For a working project mixing these languages, have a look at https://mcmap.net/q/168372/-can-i-have-swift-objective-c-c-and-c-files-in-the-same-xcode-project.Ibert
You are utterly confused because you do not understand your own answer. When a .h does not contain a single C++ keyword, like class, it can be added to the ...Bridging-Header.h -- that means it's a C file, not a C++ header, which is what Rob Napier said who, coincidentally, is not confused and has a clue.Vinosity
P
113

No. When you switch from .m to .mm you are actually switching from Objective-C to a different language (which has many subtle differences) called Objective-C++. So you're not really using C++; you're using Objective-C++ which accepts most C++ as input (in the same way that C++ accepts most but not all C as input). When I say it's not quite C++, consider a C++ file that includes a variable named nil (which is legal C++) and then try to compile that as Objective-C++.

Swift doesn't have the same relationship. It is not a superset of C or C++, and you can't directly use either in a .swift file.

"Using Swift with Cocoa and Objective-C" also tells us:

You cannot import C++ code directly into Swift. Instead, create an Objective-C or C wrapper for C++ code.

Phantasm answered 4/6, 2014 at 16:35 Comment(15)
An intriguing nuance.Formalize
Pretty annoying actually - all of us with Cocoa apps incorporating C/C++ code bases now have to maintain projects written in 3 languages....Icelander
I suggest that Objective-c++ is not a different language but a mix of c++ and Objective-c difference is subtle but its still thereDishonor
How is "nil" legal C++ ? There's no such thing (at least in standard c++) Please provide another exampleBrickkiln
but from your answer it sounds like you can indeed call c++ from swift, provided there's a C-interface to those methods. If the OP is thinking like me, then he's hoping to share logic code between multiple platformsBrickkiln
Yes, just as you can access C++ from ObjC if you give it a pure-C interface, you can also access C++ from Swift if you give it a pure-C interface.Phantasm
But with ObjC you can just go .mm your files and be (almost) done. Not so with Swift.Redo
@rewolf, I think he means a variable named nil, like int nilMonatomic
I did, but in fairness it isn't really a great explanation. The same is true in C.Phantasm
gotcha! misunderstood :/ sorry :)Brickkiln
Ok - so what about Swift 2 and C++? :-)Mercorr
@Jay: Not really. Wrappers can be auto-generated pretty easily, given a handful of procedures for method name/selector translation and type coversions. So after choices of Swift for your app, and C++ for the lib code you're using, that's zero additional languages you need to deal with on a day-to-basis / up to two additional (Obj-C, and some shell lang for running the auto-gens) you should know well enough to set up and maintain from time-to-time. That's assuming rolling your own though; it's possible that there's a good wrapper-auto-gen lib out there.Tipton
@SlippD.Thompson Actually it's my own application code / core business logic that's written in C++ - can't afford the overhead of maintaining wrappers every time I'm changing anything in my code, which is like all the time as part of normal development processIcelander
@Icelander Sounds like auto-gen wrappers are the way to go then, since you understand both sides well.Tipton
@SlippD.Thompson well.. still hoping for a more seamless solution somewhere down the road!Icelander
S
72

I wrote a simple Xcode 6 project that show how to mix C++, Objective C and Swift code:

https://github.com/romitagl/shared/tree/master/C-ObjC-Swift/Performance_Console

In particular the example call an Objective C and a C++ function from the Swift.

The key is to create a shared header Project-Bridging-Header.h and put the Objective C headers there.

Please download the project as a complete example.

Solemnity answered 13/6, 2014 at 10:14 Comment(3)
Thanks you for this example, but if I want to move the #import "CPlusPlus.h" from ObjCtoCPlusPlus.mm to ObjCtoCPlusPlus.h file, the compiler return this error: <unknown>:0: error: failed to import bridging header '/Users/Ale/Downloads/shared-master/C-ObjC-Swift/Performance_Console/Performance_Console/Performance_Console-Bridging-Header.h' it's possible move this import into header file?Udo
@API: Nope, you're missing the point. ObjCtoCPlusPlus.h/``.mm` exists for the sole purpose of providing an Ob-C interface to C++ code— it is the bridge, which is a necessary component here. Leave the includes where they are, and add a method in the ObjCtoCPlusPlus.… files for each C++ method you need access to. You'd do well to read over sourcemaking.com/design_patterns/adapterTipton
I downloaded your example and it's fantastic for the STATIC function the class offers as an example, but I can't manage to adapt the example for an additional non-static function to be used from outside... Is it also possible? (I did my C++ homework decades ago... I'm not the sharpest pencil of the box right now)Lindo
C
35

You can also skip the Objective-C file in between. Just add a C header file with a .cpp source file. Have only C declarations in the header file and include any C++ code in the source file. Then include the C header file in the **-Bridging-Header.h.

The following example returns a pointer to a C++ object (struct Foo) so Swift can store in a COpaquePointer instead of having struct Foo defined in the global space.

Foo.h file (seen by Swift - included in the bridging file)

#ifndef FOO_H
#define FOO_H

// Strictly C code here.
// 'struct Foo' is opaque (the compiler has no info about it except that 
// it's a struct we store addresses (pointers) to it.
struct Foo* foo_create();
void foo_destroy(struct Foo* foo);

#endif

Inside source file Foo.cpp (not seen by Swift):

extern "C"
{
#include "Foo.h"
}
#include <vector>

using namespace std;

// C++ code is fine here. Can add methods, constructors, destructors, C++ data members, etc.
struct Foo
{
   vector<int> data;
};

struct Foo* foo_create()
{
   return new Foo;
}

void foo_destroy(struct Foo* foo)
{
    delete foo;
}
Cinereous answered 18/6, 2015 at 20:1 Comment(3)
This answer deserves way more upvotes. Really useful.Bluster
This is the first time I've seen extern "C" wrap the header in the place it's included rather than #ifdef'ed in the header file itself. Brilliant!Kokanee
Everyone one the internet: "you have to write objective-c wrapper for c++ classes". No you don't have to. Here is a simple trick to have one less language in you code base.Glauce
I
27

I have just made a little example project using Swift, Objective-C and C++. It's a demo of how to use OpenCV stitching in iOS. The OpenCV API is C++ so we can't talk to it directly from Swift. I use a small wrapper class who's implementation file is Objective-C++. The Header file is clean Objective-C, so Swift can talk to this directly. You have to take care not to indirectly import any C++-ish files into the the headers that Swift interacts with.

The project is here: https://github.com/foundry/OpenCVSwiftStitch

Intention answered 4/6, 2014 at 19:4 Comment(3)
I think this answers a problem I hit today trying to use a 3rd party library KudanCV with Swift. My bridging header imports their .h file which contains imports to <memory> <strings> and <vectors> which I guess are from C++ libraries or files, as a result my Swift code will not compile. I'd be grateful if someone could tell me if there is a way around this... or if I have to rewrite all my code in Objective-CDefinitive
@RocketGarden - KudanCV header file is C++. You could write a wrapper that works in the same way as my example wrapper class. OR you rewrite those parts of your app that need to talk to KudanCV in objective-C++ (with a Objective-C headers). You wouldn't need to rewrite those parts of your project that are not concerned with KudanCV.Intention
Thanks for that, I realised it about 5 minutes later, but its useful to have it on record for others.Definitive
B
13

Here's my attempt at a clang tool to automate C++/swift communication. You can instanciate C++ classes from swift, inherit from C++ class and even override virtual methods in swift.
It will parse the C++ class you want to export to swift and generate the Objective-C/Objective-C++ bridge automatically.

https://github.com/sandym/swiftpp

Bullion answered 23/9, 2014 at 0:18 Comment(0)
F
8

Swift is not directly compatible with C++. You can work around the issue by wrapping your C++ code with Objective-C, and using the Objective C wrapper in Swift.

Flanker answered 4/6, 2014 at 16:35 Comment(1)
This is what I did. I wrote an article on how to integrate c++ with a swift project: learningswift.brightdigit.com/integrating-c-plus-plus-swiftAdversity
L
4

No, not in a single file.

However, you may use C++ in Swift Projects without needing a static library or framework. Like others have said, the key is to make an Objective-C bridging header that #includes C-compatible C++ headers that are marked as C compatible with the extern "C" {} trick.

Video tutorial: https://www.youtube.com/watch?v=0x6JbiphNS4

Luckily answered 27/3, 2015 at 20:4 Comment(0)
L
3

Other answers are slightly inaccurate. You can actually mix both Swift and [Objective-]C[++] in the same file, though not quite the way you would expect.

This file (c.swift) compiles to a valid executable with both swiftc c.swift and clang -x objective-c c.swift

/* /* */
#if 0
// */
import Foundation
print("Hello from Swift!")
/* /* */
#endif
#include <stdio.h>
int main()
{
    puts("Hello from C!");
    return 0;
}
// */
Lymphocytosis answered 31/8, 2017 at 10:31 Comment(1)
Your sample code is in C not C++. Better go with an <iostream> example IMHO.Ghana
G
2

One trick (of many) is that

You need a separate header for your bridging obj-c++ file...

You can't just throw @interface and @implementation in the same .mm file as one often does normally.

So in your bridging header file you have

#import "Linkage.hpp"

Linkage.hpp has the @interface for Linkage and Linkage.mm has the @implementation for .mm

And then

...you actually don't #include "yourCpp.hpp" in Linkage.hpp.

You only put #include "yourCpp.hpp" in the Linkage.mm file, not in the Linkage.hpp file.

In many online examples/tutorials, the writer simply puts the @interface and @implementation in the same .mm file, as one often does.

That will work in very simple cpp bridging examples, but,

The problem is:

if your yourCpp.hpp has any c++ features at all which it surely will (like, the first line #include <something>), then the process will fail.

But if you just don't have the #include "yourCpp.hpp" in the Linkage header file (it's fine to have it in the .mm file, obviously you'll need to) - it works.

Again this is unfortunately just one tip in the whole process.

Glendaglenden answered 27/12, 2018 at 17:33 Comment(0)
K
1

In case this is helpful to anyone, I also have a brief tutorial on calling a simple C++ static library from a trivial Swift command line utility. This is a really bare-bones proof of concept piece of code.

No Objective-C involved, just Swift and C++. Code in a C++ library is called by a C++ wrapper that implements a function with extern "C" linkage. That function is then referenced in the bridging header and called from Swift.

See http://www.swiftprogrammer.info/swift_call_cpp.html

Kitten answered 16/9, 2015 at 3:40 Comment(0)
P
1

I am providing a link to SE-0038 in the official resource, described as This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.

The status as of today is that this is the feature request which has been accepted but not yet scheduled.

This link is intended to steer anyone looking for this feature in the right direction

Pothunter answered 20/6, 2016 at 13:24 Comment(0)
D
0

Xcode 15 includes Swift-C++ interoperability support that allows to use both languages in the same project without an intermediate bridging layer.

Import a C++ framework

import CxxImageKit

Calling a C++ method from Swift

func loadImage(_ image: UIImage) {
    // Load an image into the shared C++ class.
    CxxImageEngine.shared.pointee.loadImage(image)
}

see for more details: https://developer.apple.com/videos/play/wwdc2023/10172/

Delhi answered 18/6, 2023 at 8:42 Comment(1)
This isn't a feature of iOS 17. This is a new feature of Swift 5.9/Xcode 15. See this link in the Xcode 15 Release NotesFlan

© 2022 - 2024 — McMap. All rights reserved.