¿Puedo mezclar Swift con C ++? Al igual que los archivos Objective-C .mm

131

Acabo de cambiar mis archivos .m a .mm y uso C ++. ¿Hay alguna manera de hacer lo mismo con Swift?

EhTd
fuente

Respuestas:

111

No. Cuando cambia de .m a .mm, en realidad está cambiando de Objective-C a un lenguaje diferente (que tiene muchas diferencias sutiles) llamado Objective-C ++. Entonces realmente no estás usando C ++; está utilizando Objective-C ++ que acepta la mayoría de C ++ como entrada (de la misma manera que C ++ acepta la mayoría pero no toda C como entrada). Cuando digo que no es C ++, considere un archivo C ++ que incluya una variable llamada nil(que es legal C ++) y luego intente compilarlo como Objective-C ++.

Swift no tiene la misma relación. No es un superconjunto de C o C ++, y no se puede usar directamente en un .swiftarchivo.

"Usar Swift con cacao y Objective-C" también nos dice:

No puede importar código C ++ directamente en Swift. En su lugar, cree un contenedor Objective-C o C para el código C ++.

Rob Napier
fuente
93
Bastante molesto en realidad: todos nosotros con aplicaciones Cocoa que incorporan bases de código C / C ++ ahora tenemos que mantener proyectos escritos en 3 idiomas ...
Jay
66
Sugiero que Objective-c ++ no es un lenguaje diferente, pero una combinación de c ++ y Objective-c diferencia es sutil pero todavía está allí
amar
1
¿Cómo es "nulo" legal C ++? No existe tal cosa (al menos en c ++ estándar) Proporcione otro ejemplo
rewolf
3
Pero con ObjC puedes simplemente ir a .mmtus archivos y estar (casi) listo. No es así con Swift.
chakrit
66
@rewolf, creo que se refiere a una variable llamada nil, comoint nil
Luke
165

La confusión puede venir de la suposición de que simplemente cambiar una extensión de archivo de .ma .mmes todo lo que necesita para unir los idiomas, cuando, en realidad, no hace nada de eso. No es lo .mmque causa fricción .cpp, es el .hencabezado el que no debe ser un C++encabezado.


Mismo proyecto: sí.

En el mismo proyecto, puede mezclar felizmente C , C ++ , Objective-C , Objective C ++ , Swift e incluso Assembly .

  1. ...Bridging-Header.h: expones C , Objective-C y Objective-C ++ a Swift usando este puente
  2. <ProductModuleName>-Swift.h: Expone de forma automática sus Swift clases marcados con @objca Objective-C
  3. .h: esta es la parte difícil, ya que se usan de manera ambigua para todos los sabores de C , ++ o no, objetivo o no. Cuando a .hno contiene una sola palabra clave de C ++ , como class, se puede agregar a ...Bridging-Header.h, y expondrá cualquier función que corresponda .c o .cpp funcionalidades que declara. De lo contrario, ese encabezado debe estar envuelto en una API pura de C o Objective-C .

Mismo archivo: No.

En el mismo archivo, no puede mezclar los 5. En el mismo archivo fuente :

  1. .swift: no puedes mezclar Swift con nada
  2. .m: Usted puede mezclar Objective-C con C . ( @Vinzzz )
  3. .mm: puedes mezclar Objective-C con C ++ . Este puente es Objective-C ++ . ( @Vinzzz ).
  4. .c: puro C
  5. .cpp: puedes mezclar C ++ y ensamblaje ( @Vality )
  6. .h: ubicuo y ambiguo C , C ++ , Objective-C u Objective-C ++ , por lo que la respuesta es que depende.

Referencias

Arquitecto Swift
fuente
1
Corrección: en el mismo archivo fuente .m , Objective-C es un estricto superconjunto de C, PUEDES mezclar Objective-C y C ...
Vinzzz
1
No hay problema, ni siquiera me importa el crédito, siempre que la respuesta sea la correcta;) Por cierto, .mm es para mezclar Objective-C y C ++ (C y C ++ son dos cosas diferentes, a pesar del nombre )
Vinzzz
1
Mencionaría que, a diferencia del objetivo C, C ++ no es un superconjunto de C, por lo que no puede mezclar C y C ++ en un archivo .cpp. Solo C ++ y ensamblaje.
Vality
1
En caso de que alguien encuentre que esta respuesta bien detallada no ayuda cuando se intenta exponer archivos Swift en su archivo Objective-C / C ++ (Xcode se queja de que no se encuentra el archivo de encabezado Swift). Descubrí que tenía que importar "ProductName / ProductModuleName-Swift.h" en mi archivo fuente Objective-C / C ++. Encontré esto algo enterrado en: developer.apple.com/library/ios/documentation/Swift/Conceptual/…
AeroBuffalo
Buen punto. Para un proyecto de trabajo que combine estos idiomas, eche un vistazo a stackoverflow.com/a/32546879/218152 .
SwiftArchitect el
72

Escribí un proyecto simple de Xcode 6 que muestra cómo mezclar C ++, Objective C y código Swift:

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

En particular, el ejemplo llama a un objetivo C y una función C ++ de Swift.

La clave es crear un encabezado compartido Project-Bridging-Header.h y colocar los encabezados de Objective C allí.

Descargue el proyecto como un ejemplo completo.

Gian Luigi Romita
fuente
Gracias por esto Gian!
Jason Elwood
Gracias por este ejemplo, pero si quiero mover el #import "CPlusPlus.h" de ObjCtoCPlusPlus.mm al archivo ObjCtoCPlusPlus.h, el compilador devuelve este error: <desconocido>: 0: error: no se pudo importar el encabezado de puente ' /Users/Ale/Downloads/shared-master/C-ObjC-Swift/Performance_Console/Performance_Console/Performance_Console-Bridging-Header.h '¿es posible mover esta importación al archivo de encabezado?
Alessandro Pirovano
@API: No, te estás perdiendo el punto. ObjCtoCPlusPlus.h/ `` .mm` existe con el único propósito de proporcionar una interfaz Ob-C al código C ++; es el puente, que es un componente necesario aquí. Deje las inclusiones donde están y agregue un método en los ObjCtoCPlusPlus.…archivos para cada método C ++ al que necesite acceder. Haría
Slipp D. Thompson el
Descargué tu ejemplo y es fantástico para la función ESTÁTICA que ofrece la clase como ejemplo, pero no puedo adaptar el ejemplo para que una función no estática adicional se use desde afuera ... ¿También es posible? (Hice mi tarea de C ++ hace décadas ... no soy el lápiz más afilado de la caja en este momento)
Isaac
31

También puede omitir el archivo Objective-C en el medio. Simplemente agregue un archivo de encabezado C con un archivo fuente .cpp. Tenga solo declaraciones C en el archivo de encabezado e incluya cualquier código C ++ en el archivo fuente. Luego incluya el archivo de encabezado C en ** - Bridging-Header.h.

El siguiente ejemplo devuelve un puntero a un objeto C ++ (struct Foo) para que Swift pueda almacenar en un COpaquePointer en lugar de tener struct Foo definido en el espacio global.

Archivo Foo.h (visto por Swift - incluido en el archivo puente)

#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

Archivo fuente interno Foo.cpp (no visto por 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;
}
Dan G
fuente
1
Esta respuesta merece mucho más votos a favor. Realmente util.
rsp1984
Esta es la primera vez que veo extern "C"envolver el encabezado en el lugar donde está incluido en lugar de #ifdef'editado en el archivo de encabezado. ¡Brillante!
dcow
27

Acabo de hacer un pequeño proyecto de ejemplo usando Swift, Objective-C y C ++. Es una demostración de cómo usar la costura de OpenCV en iOS. La API de OpenCV es C ++, por lo que no podemos hablar con ella directamente desde Swift. Utilizo una clase de contenedor pequeña cuyo archivo de implementación es Objective-C ++. El archivo de encabezado está limpio Objective-C, por lo que Swift puede hablar con esto directamente. Debe tener cuidado de no importar indirectamente ningún archivo C ++ - ish en los encabezados con los que Swift interactúa.

El proyecto está aquí: https://github.com/foundry/OpenCVSwiftStitch

fundición
fuente
Creo que esto responde a un problema que encontré hoy tratando de usar una biblioteca de terceros KudanCV con Swift. Mi encabezado de puente importa su archivo .h que contiene importaciones a <memory> <strings> y <vectors> que supongo que son de bibliotecas o archivos de C ++, como resultado mi código Swift no se compilará. Estaría agradecido si alguien pudiera decirme si hay una forma de evitar esto ... o si tengo que reescribir todo mi código en Objective-C
Rocket Garden el
1
@RocketGarden: el archivo de encabezado KudanCV es C ++. Podría escribir un contenedor que funcione de la misma manera que mi clase de contenedor de ejemplo. O reescribe aquellas partes de tu aplicación que necesitan hablar con KudanCV en Objective-C ++ (con encabezados Objective-C). No necesitaría reescribir aquellas partes de su proyecto que no están relacionadas con KudanCV.
fundición
Gracias por eso, me di cuenta unos 5 minutos más tarde, pero es útil tenerlo registrado para otros.
Rocket Garden el
13

Aquí está mi intento de una herramienta de clang para automatizar la comunicación C ++ / swift. Puede instanciar clases de C ++ de swift, heredar de la clase de C ++ e incluso anular métodos virtuales en swift.
Analizará la clase de C ++ que desea exportar a swift y generará el puente Objective-C / Objective-C ++ automáticamente.

https://github.com/sandym/swiftpp

Arenoso
fuente
8

Swift no es directamente compatible con C ++. Puede solucionar el problema envolviendo su código C ++ con Objective-C y utilizando el contenedor Objective C en Swift.

Austin
fuente
Esto es lo que hice. Escribí un artículo sobre cómo integrar c ++ con un proyecto rápido: learningswift.brightdigit.com/integrating-c-plus-plus-swift
leogdion
4

No, no en un solo archivo.

Sin embargo, puede usar C ++ en proyectos Swift sin necesidad de una biblioteca o marco estático. Como otros han dicho, la clave es hacer un encabezado de puente Objective-C que # incluya encabezados C ++ compatibles con C que estén marcados como C compatibles con el truco externo "C" {} .

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

James Mart
fuente
2

Otras respuestas son ligeramente inexactas. En realidad, puede mezclar Swift y [Objective-] C [++] en el mismo archivo, aunque no del modo esperado.

Este archivo (c.swift) se compila en un ejecutable válido con ambos swiftc c.swiftyclang -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;
}
// */
serpiente5
fuente
Su código de muestra está en C, no en C ++. Mejor ve con un ejemplo <iostream> en mi humilde opinión.
kakyo
2

Un truco (de muchos) es que

Necesita un encabezado separado para su archivo obj-c ++ puente ...

No puede simplemente lanzar @interface y @implementation en el mismo archivo .mm como se suele hacer normalmente.

Entonces, en su archivo de encabezado puente tiene

#import "Linkage.hpp"

Linkage.hpp tiene la @interface para Linkage y Linkage.mm tiene la @implementation para .mm

Y entonces

... en realidad no #incluye "yourCpp.hpp" en Linkage.hpp.

Solo coloca #include "yourCpp.hpp"el archivo Linkage.mm, no el archivo Linkage.hpp.

En muchos ejemplos / tutoriales en línea, el escritor simplemente coloca la @interface y la @implementation en el mismo archivo .mm, como suele ocurrir.

Eso funcionará en ejemplos de puente cpp muy simples, pero,

El problema es:

si su yourCpp.hpp tiene alguna característica de c ++ que seguramente tendrá (como la primera línea #include <something>), entonces el proceso fallará.

Pero si simplemente no tiene el archivo de encabezado#include "yourCpp.hpp" Linkage (está bien tenerlo en el archivo .mm, obviamente necesitará): funciona.

De nuevo, esto es lamentablemente solo un consejo en todo el proceso.

Fattie
fuente
1

En caso de que esto sea útil para alguien, también tengo un breve tutorial sobre cómo llamar a una biblioteca estática simple de C ++ desde una utilidad trivial de línea de comandos de Swift. Esta es una pieza de código de prueba de concepto realmente básica.

No hay Objective-C involucrado, solo Swift y C ++. El código en una biblioteca de C ++ es llamado por un contenedor de C ++ que implementa una función con enlace externo "C". Luego se hace referencia a esa función en el encabezado de puente y se llama desde Swift.

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

Anatoli P
fuente
1

Estoy proporcionando un enlace a SE-0038 en el recurso oficial, descrito como Esto mantiene propuestas de cambios y mejoras visibles para el usuario al Lenguaje de programación Swift.

El estado actual es que esta es la solicitud de función que se ha aceptado pero aún no está programada.

Este enlace está destinado a dirigir a cualquiera que busque esta función en la dirección correcta

Ryan Heitner
fuente