Puis-je mélanger Swift avec C ++? Comme les fichiers .mm Objective-C


131

Je viens de changer mes fichiers .m en .mm et d'utiliser C ++. Existe-t-il un moyen de faire de même avec Swift?

Réponses:


111

Non. Lorsque vous passez de .m à .mm, vous passez en fait d'Objective-C à un autre langage (qui présente de nombreuses différences subtiles) appelé Objective-C ++. Vous n'utilisez donc pas vraiment C ++; vous utilisez Objective-C ++ qui accepte la plupart des C ++ en entrée (de la même manière que C ++ accepte la plupart mais pas tous les C en entrée). Quand je dis que ce n'est pas tout à fait C ++, considérez un fichier C ++ qui comprend une variable nommée nil(qui est C ++ légal), puis essayez de compiler cela en Objective-C ++.

Swift n'a pas la même relation. Ce n'est pas un sur-ensemble de C ou C ++, et vous ne pouvez pas utiliser directement l'un ou l'autre dans un .swiftfichier.

"Utiliser Swift avec Cocoa et Objective-C" nous dit également:

Vous ne pouvez pas importer du code C ++ directement dans Swift. Au lieu de cela, créez un wrapper Objective-C ou C pour le code C ++.


93
Assez ennuyeux en fait - nous tous, avec des applications Cocoa incorporant des bases de code C / C ++, devons maintenant maintenir des projets écrits en 3 langues ....
Jay

6
Je suggère que Objective-c ++ n'est pas un langage différent, mais un mélange de différence entre C ++ et Objective-c est subtile mais il est toujours là
amar

1
Comment le C ++ légal "nil" est-il? Il n'y a rien de tel (au moins en c ++ standard) Veuillez fournir un autre exemple
rewolf

3
Mais avec ObjC, vous pouvez simplement accéder à .mmvos fichiers et être (presque) terminé. Ce n'est pas le cas avec Swift.
chakrit

6
@rewolf, je pense qu'il veut dire une variable nommée nil, commeint nil
Luke

165

La confusion peut provenir de l'hypothèse que simplement changer une extension de fichier de .mà .mmest tout ce dont vous avez besoin pour relier les langues, alors qu'en réalité, cela ne fait rien de ce genre. Ce n'est pas le .mmfrottement avec .cpp, c'est le .hcollecteur qui ne doit absolument pas être un C++collecteur.


Même projet: Oui.

Dans le même projet, vous pouvez facilement mélanger C , C ++ , Objective-C , Objective C ++ , Swift et même Assembly .

  1. ...Bridging-Header.h: vous exposez C , Objective-C et Objective-C ++ à Swift en utilisant ce pont
  2. <ProductModuleName>-Swift.h: expose automatiquement vos classes Swift marquées d'un @objcà Objective-C
  3. .h: c'est la partie la plus délicate, car ils sont utilisés de manière ambiguë pour toutes les saveurs de C , ++ ou pas, Objective ou pas. Quand a .hne contient pas un seul mot-clé C ++ , comme class, il peut être ajouté au ...Bridging-Header.h, et exposera la fonction correspondante .c ou les .cpp fonctionnalités qu'il déclare. Sinon, cet en-tête doit être encapsulé dans une API pure C ou Objective-C .

Même fichier: Non.

Dans le même fichier, vous ne pouvez pas mélanger tous les 5. Dans le même fichier source :

  1. .swift: vous ne pouvez pas mélanger Swift avec quoi que ce soit
  2. .m: Vous pouvez mélanger Objective-C avec C . ( @Vinzzz )
  3. .mm: vous pouvez mélanger Objective-C avec C ++ . Ce pont est Objective-C ++ . ( @Vinzzz ).
  4. .c: C pur
  5. .cpp: vous pouvez mélanger C ++ et Assembly ( @Vality )
  6. .h: C , C ++ , Objective-C ou Objective-C ++ omniprésent et ambigu , donc la réponse est que cela dépend.

Références


1
Correction: Dans le même fichier source .m , Objective-C étant un sur-ensemble C strict, vous POUVEZ mélanger Objective-C et C ...
Vinzzz

1
Pas de problème, je ne me soucie même pas du crédit, tant que la réponse est la bonne;) BTW, .mm est pour mélanger Objective-C et C ++ (C et C ++ sont deux choses différentes, malgré le nom )
Vinzzz

1
Je mentionnerais que contrairement à l'objectif C, C ++ n'est pas un sur-ensemble C, vous ne pouvez donc pas mélanger à la fois C et C ++ dans un fichier .cpp. Seulement C ++ et assembly.
Vality

1
Au cas où quelqu'un trouverait que cette réponse bien détaillée n'aide pas tout à fait à exposer des fichiers Swift dans leur fichier Objective-C / C ++ (Xcode se plaint que le fichier d'en-tête Swift n'est pas trouvé). J'ai trouvé que je devais importer "ProductName / ProductModuleName-Swift.h" dans mon fichier source Objective-C / C ++. J'ai trouvé cela quelque peu enterré à: developer.apple.com/library/ios/documentation/Swift/Conceptual
...

Bon point. Pour un projet de travail mélangeant ces langues, consultez stackoverflow.com/a/32546879/218152 .
SwiftArchitect

72

J'ai écrit un projet Xcode 6 simple qui montre comment mélanger du code C ++, Objective C et Swift:

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

En particulier, l'exemple appelle un Objective C et une fonction C ++ à partir du Swift.

La clé est de créer un en-tête partagé Project-Bridging-Header.h et d'y placer les en-têtes Objective C.

Veuillez télécharger le projet comme exemple complet.


Merci pour ce Gian!
Jason Elwood

Merci pour cet exemple, mais si je veux déplacer le #import "CPlusPlus.h" de ObjCtoCPlusPlus.mm vers le fichier ObjCtoCPlusPlus.h, le compilateur renvoie cette erreur: <inconnu>: 0: erreur: impossible d'importer l'en-tête de pontage ' /Users/Ale/Downloads/shared-master/C-ObjC-Swift/Performance_Console/Performance_Console/Performance_Console-Bridging-Header.h 'il est possible de déplacer cette importation dans le fichier d'en-tête?
Alessandro Pirovano

@API: Non, vous manquez le point. ObjCtoCPlusPlus.h/ `` .mm` existe dans le seul but de fournir une interface Ob-C au code C ++ - c'est le pont, qui est ici un composant nécessaire. Laissez les includes où ils se trouvent et ajoutez une méthode dans les ObjCtoCPlusPlus.…fichiers pour chaque méthode C ++ à laquelle vous devez accéder. Vous feriez bien de lire sur sourcemaking.com/design_patterns/adapter
Slipp

J'ai téléchargé votre exemple et c'est fantastique pour la fonction STATIC que la classe propose à titre d'exemple, mais je n'arrive pas à adapter l'exemple pour une fonction non statique supplémentaire à utiliser de l'extérieur ... Est-ce aussi possible? (J'ai fait mes devoirs C ++ il y a des décennies ... je ne suis pas le crayon le plus pointu de la boîte en ce moment)
Isaac

31

Vous pouvez également ignorer le fichier Objective-C entre les deux. Ajoutez simplement un fichier d'en-tête C avec un fichier source .cpp. Avoir uniquement des déclarations C dans le fichier d'en-tête et inclure tout code C ++ dans le fichier source. Incluez ensuite le fichier d'en-tête C dans le ** - Bridging-Header.h.

L'exemple suivant renvoie un pointeur vers un objet C ++ (struct Foo) afin que Swift puisse stocker dans un COpaquePointer au lieu d'avoir struct Foo défini dans l'espace global.

Fichier Foo.h (vu par Swift - inclus dans le fichier de pontage)

#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

À l'intérieur du fichier source Foo.cpp (non vu par 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;
}

1
Cette réponse mérite bien plus de votes positifs. Vraiment utile.
rsp1984

C'est la première fois que je vois extern "C"envelopper l'en-tête à l'endroit où il est inclus plutôt #ifdefqu'éd dans le fichier d'en-tête lui-même. Brillant!
dcow

27

Je viens de faire un petit exemple de projet utilisant Swift, Objective-C et C ++. C'est une démonstration de l'utilisation de l'assemblage OpenCV sous iOS. L'API OpenCV est C ++, nous ne pouvons donc pas lui parler directement depuis Swift. J'utilise une petite classe wrapper dont le fichier d'implémentation est Objective-C ++. Le fichier d'en- tête est propre Objective-C, donc Swift peut lui parler directement. Vous devez faire attention à ne pas importer indirectement de fichiers C ++ dans les en-têtes avec lesquels Swift interagit.

Le projet est ici: https://github.com/foundry/OpenCVSwiftStitch


Je pense que cela répond à un problème que j'ai rencontré aujourd'hui en essayant d'utiliser une bibliothèque tierce KudanCV avec Swift. Mon en-tête de pont importe leur fichier .h qui contient des importations vers <memory> <strings> et <vectors> qui, je suppose, proviennent de bibliothèques ou de fichiers C ++, par conséquent mon code Swift ne se compilera pas. Je serais reconnaissant si quelqu'un pouvait me dire s'il y a un moyen de contourner cela ... ou si je dois réécrire tout mon code en Objective-C
Rocket Garden

1
@RocketGarden - Le fichier d'en-tête KudanCV est C ++. Vous pouvez écrire un wrapper qui fonctionne de la même manière que mon exemple de classe wrapper. OU vous réécrivez les parties de votre application qui doivent parler à KudanCV en objective-C ++ (avec des en-têtes Objective-C). Vous n'avez pas besoin de réécrire les parties de votre projet qui ne sont pas concernées par KudanCV.
fonderie

Merci pour cela, je l'ai réalisé environ 5 minutes plus tard, mais il est utile de l'avoir enregistré pour d'autres.
Rocket Garden

13

Voici ma tentative d'un outil clang pour automatiser la communication C ++ / rapide. Vous pouvez instancier des classes C ++ à partir de swift, hériter de la classe C ++ et même remplacer des méthodes virtuelles dans swift.
Il analysera la classe C ++ que vous souhaitez exporter vers swift et générera automatiquement le pont Objective-C / Objective-C ++.

https://github.com/sandym/swiftpp




4

Non, pas dans un seul fichier.

Cependant, vous pouvez utiliser C ++ dans Swift Projects sans avoir besoin d'une bibliothèque statique ou d'un framework. Comme d'autres l'ont dit, la clé est de créer un en-tête de pontage Objective-C qui #inclut des en-têtes C ++ compatibles C qui sont marqués comme C compatibles avec l' astuce extern "C" {} .

Tutoriel vidéo: https://www.youtube.com/watch?v=0x6JbiphNS4


2

D'autres réponses sont légèrement inexactes. Vous pouvez en fait mélanger Swift et [Objective-] C [++] dans le même fichier, mais pas tout à fait comme vous le souhaiteriez.

Ce fichier (c.swift) se compile en un exécutable valide avec à la fois swiftc c.swiftetclang -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;
}
// */

Votre exemple de code est en C et non en C ++. Mieux vaut aller avec un exemple de <iostream> IMHO.
kakyo

2

Un truc (parmi tant d'autres) est que

Vous avez besoin d'un en- tête séparé pour votre fichier obj-c ++ de pontage ...

Vous ne pouvez pas simplement lancer @interface et @implementation dans le même fichier .mm comme on le fait souvent normalement.

Donc, dans votre fichier d'en-tête de pontage, vous avez

#import "Linkage.hpp"

Linkage.hpp a l '@interface pour Linkage et Linkage.mm a la @implementation pour .mm

Puis

... vous n'incluez pas en fait "yourCpp.hpp" dans Linkage.hpp.

Vous ne mettez que #include "yourCpp.hpp"dans le fichier Linkage.mm, pas dans le fichier Linkage.hpp.

Dans de nombreux exemples / tutoriels en ligne, le rédacteur place simplement @interface et @implementation dans le même fichier .mm, comme on le fait souvent.

Cela fonctionnera dans des exemples de pontage cpp très simples, mais,

Le problème est:

si votre yourCpp.hpp a des fonctionnalités C ++, ce qu'il sera sûrement (comme la première ligne #include <something>), alors le processus échouera.

Mais si vous n'avez tout simplement pas le #include "yourCpp.hpp"dans le fichier d'en- tête Linkage (c'est bien de l'avoir dans le fichier .mm, vous en aurez évidemment besoin) - cela fonctionne.

Encore une fois, ce n'est malheureusement qu'un conseil dans l'ensemble du processus.


1

Au cas où cela serait utile à quiconque, j'ai également un bref tutoriel sur l'appel d'une simple bibliothèque statique C ++ à partir d'un utilitaire de ligne de commande Swift trivial. Il s'agit d'une preuve de concept très simple.

Aucun Objective-C impliqué, juste Swift et C ++. Le code d'une bibliothèque C ++ est appelé par un wrapper C ++ qui implémente une fonction avec une liaison externe "C". Cette fonction est ensuite référencée dans l'en-tête de pontage et appelée depuis Swift.

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


1

Je propose un lien vers SE-0038 dans la ressource officielle, décrit comme Ceci maintient les propositions de changements et d'améliorations visibles par l'utilisateur du langage de programmation Swift.

Le statut actuel est qu'il s'agit de la demande de fonctionnalité qui a été acceptée mais pas encore planifiée.

Ce lien est destiné à orienter tous ceux qui recherchent cette fonctionnalité dans la bonne direction

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.