Posso misturar Swift com C ++? Como os arquivos Objective-C .mm

131

Acabei de alterar meus arquivos .m para .mm e usar C ++. Existe uma maneira de fazer o mesmo com Swift?

EhTd
fonte

Respostas:

111

Não. Quando você muda de .m para .mm, na verdade, você está mudando do Objective-C para um idioma diferente (que possui muitas diferenças sutis) chamado Objective-C ++. Então você não está realmente usando C ++; você está usando o Objective-C ++, que aceita a maior parte do C ++ como entrada (da mesma forma que o C ++ aceita a maioria, mas não todo o C como entrada). Quando digo que não é bem C ++, considere um arquivo C ++ que inclua uma variável chamada nil(que é C ++ legal) e tente compilar isso como Objective-C ++.

Swift não tem o mesmo relacionamento. Não é um superconjunto de C ou C ++ e você não pode usá-lo diretamente em um .swiftarquivo.

"Usando Swift com cacau e Objective-C" também nos diz:

Você não pode importar o código C ++ diretamente no Swift. Em vez disso, crie um wrapper Objective-C ou C para código C ++.

Rob Napier
fonte
93
Muito chato, na verdade - todos nós com Cacau os aplicativos incorporando bases de código C / C ++ agora tem que manter projetos escritos em 3 línguas ....
Jay
6
Sugiro que Objective-C ++ não é uma linguagem diferente, mas uma mistura de c ++ e diferença Objective-C é subtil mas ainda há
amar
1
Como é C ++ legal "nulo"? Não existe isso (pelo menos no c ++ padrão) Por favor, forneça outro exemplo
rewolf
3
Mas com o ObjC você pode simplesmente acessar .mmseus arquivos e (quase) pronto. Não é assim com Swift.
chakrit
6
@rewolf, acho que ele quer dizer uma variável chamada nil, comoint nil
Luke
165

A confusão pode vir da suposição de que apenas alterar uma extensão de arquivo de .mpara .mmé tudo o que você precisa para conectar os idiomas, quando, na realidade, não faz nada desse tipo. Não é o .mmque causa atrito .cpp, é o .hcabeçalho que não deve ser positivamente um C++cabeçalho.


Mesmo projeto: Sim.

No mesmo projeto, você pode combinar felizmente C , C ++ , Objective-C , Objective C ++ , Swift e até Assembly .

  1. ...Bridging-Header.h: você expõe C , Objective-C e Objective-C ++ a Swift usando esta ponte
  2. <ProductModuleName>-Swift.h: Expõe automaticamente seus Swift aulas marcados com @objca Objective-C
  3. .h: esta é a parte complicada, pois eles são usados ​​de forma ambígua para todos os tipos de C , ++ ou não, objetivo ou não. Quando a .hnão contém uma única palavra-chave C ++ , como class, ela pode ser adicionada ao ...Bridging-Header.he exporá qualquer função que as funcionalidades correspondentes .c ou .cpp declaradas. Caso contrário, esse cabeçalho deve ser agrupado em uma API C ou Objective-C pura .

Mesmo arquivo: Não.

No mesmo arquivo, você não pode misturar todos os 5. No mesmo arquivo de origem :

  1. .swift: você não pode misturar Swift com nada
  2. .m: Você pode misturar Objective-C com C . ( @Vinzzz )
  3. .mm: você pode misturar Objective-C com C ++ . Essa ponte é Objective-C ++ . ( @Vinzzz ).
  4. .c: C puro
  5. .cpp: você pode misturar C ++ e montagem ( @Vality )
  6. .h: onipresente e ambíguo C , C ++ , Objective-C ou Objective-C ++ , então a resposta é que depende.

Referências

SwiftArchitect
fonte
1
Correção: Na mesma .m arquivo de origem, sendo um rigoroso C super Objective-C, você pode misturar Objective-C e C ...
Vinzzz
1
Não tem problema, eu nem ligo para o crédito, desde que a resposta seja a correta;) BTW, .mm é para misturar Objective-C e C ++ (C e C ++ são duas coisas diferentes, apesar do nome )
Vinzzz 17/09/2015
1
Eu mencionaria que, diferentemente do objetivo C, o C ++ não é um superconjunto C, portanto você não pode misturar C e C ++ em um arquivo .cpp. Somente C ++ e montagem.
Vality 14/10
1
Caso alguém descubra que essa resposta bem detalhada não ajuda muito ao tentar expor os arquivos Swift no arquivo Objective-C / C ++ (o Xcode está reclamando que o arquivo do cabeçalho Swift não foi encontrado). Descobri que tinha que importar "ProductName / ProductModuleName-Swift.h" no meu arquivo de origem Objective-C / C ++. Achei isso um pouco enterrado em: developer.apple.com/library/ios/documentation/Swift/Conceptual/...
AeroBuffalo
Bom ponto. Para um projeto de trabalho que mistura esses idiomas, consulte stackoverflow.com/a/32546879/218152 .
SwiftArchitect
72

Eu escrevi um projeto simples do Xcode 6 que mostra como misturar códigos C ++, Objective C e Swift:

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

Em particular, o exemplo chama uma função Objective C e C ++ do Swift.

A chave é criar um cabeçalho compartilhado Project-Bridging-Header.h e colocar os cabeçalhos do Objective C lá.

Faça o download do projeto como um exemplo completo.

Gian Luigi Romita
fonte
Obrigado por este Gian!
Jason Elwood
Obrigado por este exemplo, mas se eu quiser mover o #import "CPlusPlus.h" do ObjCtoCPlusPlus.mm para o arquivo ObjCtoCPlusPlus.h, o compilador retornará este erro: <unknown>: 0: error: falha ao importar o cabeçalho da ponte ' /Users/Ale/Downloads/shared-master/C-ObjC-Swift/Performance_Console/Performance_Console/Performance_Console-Bridging-Header.h 'é possível mover essa importação para o arquivo de cabeçalho?
Alessandro Pirovano
@API: Não, você está perdendo o objetivo. ObjCtoCPlusPlus.h/ `` .mm` existe com o único objetivo de fornecer uma interface Ob-C para o código C ++ - é a ponte, que é um componente necessário aqui. Deixe as inclusões onde estão e adicione um método nos ObjCtoCPlusPlus.…arquivos para cada método C ++ ao qual você precisa acessar. Você faria bem em ler em sourcemaking.com/design_patterns/adapter
D. Thompson
Fiz o download do seu exemplo e é fantástico para a função STATIC que a classe oferece como exemplo, mas não consigo adaptar o exemplo para que uma função não estática adicional seja usada de fora ... Também é possível? (Eu fiz o meu C ++ homework décadas atrás ... Eu não sou o lápis mais afiado da caixa agora)
Isaac
31

Você também pode pular o arquivo Objective-C no meio. Basta adicionar um arquivo de cabeçalho C com um arquivo de origem .cpp. Tenha apenas declarações C no arquivo de cabeçalho e inclua qualquer código C ++ no arquivo de origem. Em seguida, inclua o arquivo de cabeçalho C no ** - Bridging-Header.h.

O exemplo a seguir retorna um ponteiro para um objeto C ++ (struct Foo) para que o Swift possa armazenar em um COpaquePointer em vez de ter struct Foo definido no espaço global.

Arquivo Foo.h (visto por Swift - incluído no arquivo de ponte)

#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

Arquivo de origem interno Foo.cpp (não visto pelo 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
fonte
1
Essa resposta merece muito mais votos. Muito útil.
rsp1984
Esta é a primeira vez que vi extern "C"envolver o cabeçalho no local em que está incluído, e não #ifdefno próprio arquivo de cabeçalho. Brilhante!
Dcow
27

Acabei de fazer um pequeno projeto de exemplo usando Swift, Objective-C e C ++. É uma demonstração de como usar a costura OpenCV no iOS. A API do OpenCV é C ++, portanto não podemos falar com ela diretamente do Swift. Eu uso uma classe de wrapper pequena cujo arquivo de implementação é Objective-C ++. O arquivo Cabeçalho está limpo como Objective-C, para que o Swift possa conversar diretamente com isso. Você deve tomar cuidado para não importar indiretamente nenhum arquivo C ++ para os cabeçalhos com os quais o Swift interage.

O projeto está aqui: https://github.com/foundry/OpenCVSwiftStitch

fundição
fonte
Acho que isso responde a um problema que encontrei hoje ao tentar usar uma biblioteca de terceiros KudanCV com Swift. Meu cabeçalho de ponte importa o arquivo .h que contém importações para <memory> <strings> e <vectors>, que eu acho que são de bibliotecas ou arquivos C ++, como resultado, meu código Swift não será compilado. Eu ficaria muito grato se alguém poderia me dizer se existe uma maneira de contornar isso ... ou se eu tenho que reescrever todo o meu código em Objective-C
Rocket Garden
1
@RocketGarden - o arquivo de cabeçalho KudanCV é C ++. Você pode escrever um wrapper que funcione da mesma maneira que minha classe de wrapper de exemplo. OU você reescreve as partes do seu aplicativo que precisam conversar com o KudanCV no objetivo-C ++ (com os cabeçalhos de Objetivo-C). Você não precisaria reescrever as partes do seu projeto que não estão relacionadas ao KudanCV.
fundição
Obrigado por isso, percebi isso cerca de 5 minutos depois, mas é útil tê-lo registrado para outras pessoas.
Rocket Garden
13

Aqui está minha tentativa de uma ferramenta clang para automatizar a comunicação rápida em C ++ /. Você pode instanciar classes C ++ a partir do swift, herdar da classe C ++ e até substituir métodos virtuais no swift.
Ele analisará a classe C ++ que você deseja exportar rapidamente e gerará a ponte Objective-C / Objective-C ++ automaticamente.

https://github.com/sandym/swiftpp

Sandy
fonte
8

Swift não é diretamente compatível com C ++. Você pode solucionar o problema agrupando seu código C ++ com Objective-C e usando o wrapper Objective C no Swift.

Austin
fonte
Foi o que eu fiz. Eu escrevi um artigo sobre como integrar c ++ com um projeto de rápida: learningswift.brightdigit.com/integrating-c-plus-plus-swift
leogdion
4

Não, não em um único arquivo.

No entanto, você pode usar o C ++ em projetos Swift sem precisar de uma biblioteca ou estrutura estática. Como outros já disseram, a chave é criar um cabeçalho de ponte Objective-C que # inclua cabeçalhos C ++ compatíveis com C e marcados como C compatíveis com o truque externo "C" {} .

Tutorial em vídeo: https://www.youtube.com/watch?v=0x6JbiphNS4

James Mart
fonte
2

Outras respostas são um pouco imprecisas. Você pode realmente misturar Swift e [Objective-] C [++] no mesmo arquivo, embora não exatamente da maneira que você esperaria.

Este arquivo (c.swift) é compilado em um executável válido com ambos swiftc c.swifteclang -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;
}
// */
snake5
fonte
Seu código de exemplo está em C e não em C ++. Melhor ir com um exemplo <iostream> IMHO.
Kakyo
2

Um truque (de muitos) é que

Você precisa de um cabeçalho separado para o arquivo de ponte obj-c ++ ...

Você não pode simplesmente jogar @interface e @implementation no mesmo arquivo .mm, como costuma fazer normalmente.

Então, no seu arquivo de cabeçalho de ponte, você tem

#import "Linkage.hpp"

Linkage.hpp possui a @interface para Linkage e Linkage.mm possui a @implementation para .mm

E depois

... você realmente não inclui "yourCpp.hpp" no Linkage.hpp.

Você só coloca #include "yourCpp.hpp"no arquivo Linkage.mm, não no arquivo Linkage.hpp.

Em muitos exemplos / tutoriais on-line, o escritor simplesmente coloca o @interface e a @implementation no mesmo arquivo .mm, como costuma fazer.

Isso funcionará em exemplos muito simples de ponte de cpp, mas,

O problema é:

se o seu yourCpp.hpp tiver algum recurso de c ++ que certamente (como a primeira linha #include <something>), o processo falhará.

Mas se você simplesmente não tiver o arquivo de cabeçalho#include "yourCpp.hpp" no Linkage (é bom tê-lo no arquivo .mm, obviamente você precisará) - ele funciona.

Mais uma vez, infelizmente, essa é apenas uma dica em todo o processo.

Fattie
fonte
1

Caso isso seja útil a alguém, também tenho um breve tutorial sobre como chamar uma biblioteca estática C ++ simples a partir de um utilitário trivial de linha de comando Swift. Esta é realmente uma prova básica do código.

Nenhum Objective-C envolvido, apenas Swift e C ++. O código em uma biblioteca C ++ é chamado por um wrapper C ++ que implementa uma função com ligação externa "C". Essa função é então referenciada no cabeçalho da ponte e chamada a partir do Swift.

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

Anatoli P
fonte
1

Estou fornecendo um link para o SE-0038 no recurso oficial, descrito como Isso mantém propostas de alterações e aprimoramentos visíveis ao usuário para o Swift Programming Language.

O status de hoje é que essa é a solicitação de recurso que foi aceita, mas ainda não agendada.

Este link tem como objetivo orientar quem procura esse recurso na direção certa

Ryan Heitner
fonte