Чи можна змішувати Swift із C ++? Як і файли Objective-C .mm


131

Я просто змінив свої .m файли на .mm і використовую C ++. Чи є спосіб зробити те ж саме зі Свіфтом?

Відповіді:


111

Ні. Коли ви переходите з .m на .mm, ви фактично переходите з Objective-C на іншу мову (яка має багато тонких відмінностей) під назвою Objective-C ++. Таким чином, ви не використовуєте C ++; ви використовуєте Objective-C ++, який приймає більшість C ++ як вхідний (таким же чином, як C ++ приймає більшість, але не всі C як вхідні). Коли я кажу, що це не зовсім C ++, розгляньте файл C ++, який включає змінну з назвоюnil (яка є законною C ++), а потім спробуйте скомпілювати це як Objective-C ++.

Свіфт не має однакових відносин. Це не набір C або C ++, і ви не можете безпосередньо використовувати жоден .swiftфайл.

"Використання Swift з какао та Objective-C" також говорить нам:

Ви не можете імпортувати код C ++ безпосередньо в Swift. Натомість створіть обгортку Objective-C або C для коду C ++.


93
Насправді це дуже дратує - всім нам із програмами какао, що містять кодові бази C / C ++, тепер доводиться підтримувати проекти, написані трьома мовами ....
Jay

6
Я припускаю, що Objective-c ++ не є іншою мовою, але поєднання різниці c ++ та Objective-c є тонким, але його все-таки є
amar

1
Як "нульовий" законний C ++? Немає такого (принаймні, у стандартному режимі c ++). Надайте інший приклад
перемогти

3
Але за допомогою ObjC ви можете просто перейти до .mmсвоїх файлів і бути (майже) готовим . Не так зі Свіфтом.
чакрит

6
@rewolf, я думаю, він має на увазі змінну на ім'я nil, наприкладint nil
Лука

165

Плутанина може виникнути з припущення, що лише зміна розширення файлу з .mна .mmце все, що вам потрібно, щоб перевести мови, коли насправді він нічого подібного не робить. Не те, .mmщо викликає тертя з .cpp, саме .hзаголовок повинен бути позитивно не C++заголовком.


Той же проект: Так.

У цьому ж проекті ви можете із задоволенням змішати C , C ++ , Objective-C , Objective C ++ , Swift і навіть збірку .

  1. ...Bridging-Header.h: Ви піддаєте C , Objective-C і Objective-C ++ для Swift , використовуючи цей міст
  2. <ProductModuleName>-Swift.h: Виставляє автоматично ваші Swift класи , відмічені @objcв Objective-C
  3. .h: це складна частина, оскільки вони неоднозначно використовуються для всіх ароматів С , ++ чи ні, Об'єктивна чи ні. Якщо a .hне містить жодного ключового слова C ++ , як-от class, воно може бути додане до ...Bridging-Header.hта викриє будь-яку функцію відповідної .c або .cpp функціональної функції, яку вона оголошує. В іншому випадку, що заголовок повинен бути обгорнуті або в чистому C або Objective-C API.

Той самий файл: Ні.

У тому самому файлі ви не можете змішати всі 5. У цьому ж вихідному файлі :

  1. .swift: не можна змішувати Свіфт ні з чим
  2. .m: Ви можете змішати Objective-C з C . ( @Vinzzz )
  3. .mm: ви можете змішати Objective-C з C ++ . Цей міст об'єктивний-C ++ . ( @Vinzzz ).
  4. .c: чистий С
  5. .cpp: ви можете змішати C ++ та збірку ( @Vality )
  6. .h: всюдисущий і неоднозначний C , C ++ , Objective-C або Objective-C ++ , тому відповідь - це залежить.

Список літератури


1
Виправлення: У тому ж вихідному файлі .m , що Objective-C є суворим набором C, ви МОЖЕТЕ змішати Objective-C і C ...
Vinzzz

1
Без проблем, я навіть не хвилююсь кредиту, якщо відповідь правильна;) BTW, .mm - це змішування Objective-C і C ++ (C і C ++ - це дві різні речі, незважаючи на назву )
Вінцз

1
Я хотів би зазначити, що на відміну від об'єкта C, C ++ не є супернабір С, тому ви не можете змішувати і C, і C ++ у файлі .cpp. Тільки C ++ і збірка.
Vality

1
Якщо хтось виявить, що ця детальна відповідь не зовсім допомагає при спробі викрити файли Swift у їх файл Objective-C / C ++ (Xcode скаржиться, що файл заголовка Swift не знайдено). Я виявив, що мені потрібно імпортувати "ProductName / ProductModuleName-Swift.h" у вихідний файл Objective-C / C ++. Я знайшов це дещо похованим на сайті: developer.apple.com/library/ios/documentation/Swift/Conceptual/…
AeroBuffalo

Гарна думка. Щоб ознайомитись з робочим проектом зі змішування цих мов, подивіться на stackoverflow.com/a/32546879/218152 .
SwiftArchitect

72

Я написав простий проект Xcode 6, який показує, як поєднувати код C ++, Objective C і Swift:

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

Зокрема, у прикладі називаємо об'єктив C та функцію C ++ від Swift.

Ключовим моментом є створення спільного заголовка Project-Bridging-Header.h та розміщення туди заголовків Objective C.

Завантажте проект як повний приклад.


Дякую за це Джан!
Джейсон Елвуд

Дякую за цей приклад, але якщо я хочу перенести #import "CPlusPlus.h" з ObjCtoCPlusPlus.mm у файл ObjCtoCPlusPlus.h, компілятор поверне цю помилку: <unknown>: 0: error: не вдалося імпортувати мостиковий заголовок ' /Users/Ale/Downloads/shared-master/C-ObjC-Swift/Performance_Console/Performance_Console/Performance_Console-Bridging-Header.h 'можливо перемістити цей імпорт у файл заголовка?
Алессандро Піровано

@API: Ні, ви не пропустите суть. ObjCtoCPlusPlus.h/ `` .mm` існує лише з метою надання інтерфейсу Ob-C коду C ++ - це міст, який є необхідним компонентом тут. Залиште включення, де вони є, і додайте метод у ObjCtoCPlusPlus.…файли для кожного методу C ++, до якого потрібно отримати доступ. Ви б добре , щоб читати над sourcemaking.com/design_patterns/adapter
Slipp Д. Томпсон

Я завантажив ваш приклад, і це фантастично для функції STATIC, який клас пропонує як приклад, але я не встигаю адаптувати приклад для додаткової нестатичної функції, яка використовується ззовні ... Чи це також можливо? (Я робив домашнє завдання C ++ десятиліття тому ... я зараз не найгостріший олівець коробки)
Ісаак

31

Ви також можете пропустити Objective-C файл між ними. Просто додайте файл заголовка C із вихідним файлом .cpp. У файлі заголовка є лише декларації C, а будь-який код C ++ у вихідному файлі. Потім додайте файл заголовка C у ** - Bridging-Header.h.

Наступний приклад повертає вказівник на об’єкт C ++ (struct Foo), тому Swift може зберігати в COpaquePointer замість того, щоб у глобальному просторі визначався struct Foo.

Файл Foo.h (бачив Swift - включений до мостового файлу)

#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

Всередині вихідного файлу Foo.cpp (не бачив Свіфт):

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
Ця відповідь заслуговує спосіб більше upvotes. Дійсно корисна.
rsp1984

Це перший раз, коли я бачив extern "C"загортати заголовок на місце, яке воно включене, а не #ifdef'ed у самому файлі заголовка. Блискуче!
цві

27

Я щойно зробив невеликий приклад проекту, використовуючи Swift, Objective-C і C ++. Це демонстрація того, як використовувати зшивання OpenCV в iOS. API OpenCV - це C ++, тому ми не можемо говорити з ним безпосередньо від Swift. Я використовую невеликий клас обгортки, файлом реалізації якого є Objective-C ++. Тема чистий Objective-C, тому Swift може говорити з цим безпосередньо. Ви повинні стежити за тим, щоб не опосередковано імпортувати будь-які файли C ++ в заголовки, з якими взаємодіє Swift.

Проект тут: https://github.com/foundry/OpenCVSwiftStitch


Я думаю, це відповідає на проблему, з якою я потрапив сьогодні, намагаючись використати сторонню бібліотеку KudanCV зі Swift. Мій заголовок мосту імпортує їх .h-файл, який містить імпорт до <memory> <strings> і <vectors>, які, напевно, з бібліотек C ++ або файлів, в результаті чого мій код Swift не буде компілюватися. Буду вдячний, якби хтось міг сказати мені, чи є спосіб подолати це ... або якщо мені доведеться переписати весь свій код у Objective-C
Rocket Garden

1
@RocketGarden - Заголовок KudanCV - це C ++. Ви можете написати обгортку, яка працює аналогічно моєму прикладу обгорткового класу. АБО ви перезаписуєте ті частини свого додатка, які потрібно поговорити з KudanCV у target-C ++ (із ​​заголовками Objective-C). Вам не потрібно буде переписувати ті частини вашого проекту, які не стосуються KudanCV.
ливарний

Дякую за це, я зрозумів це приблизно через 5 хвилин, але корисно мати це для запису для інших.
Ракетний сад

13

Ось моя спроба кланг-інструменту для автоматизації C ++ / швидкої комунікації. Ви можете інстанціювати класи C ++ з швидкої, успадковувати від класу C ++ і навіть швидко змінювати віртуальні методи.
Він буде розбирати клас C ++, який ви хочете експортувати, для швидкого створення та автоматичного генерування мосту Objective-C / Objective-C ++.

https://github.com/sandym/swiftpp


8

Swift безпосередньо не сумісний із C ++. Ви можете подолати проблему, загорнувши свій код C ++ за допомогою Objective-C та використовуючи обгортку Objective C у Swift.


Це я і зробив. Я написав статтю про те, як інтегрувати c ++ у швидкий проект: learningswift.brightdigit.com/integrating-c-plus-plus-swift
leogdion


4

Ні, не в одному файлі.

Однак ви можете використовувати C ++ у проектах Swift, не потребуючи статичної бібліотеки чи рамок. Як уже говорили інші, головним є створення мостового заголовка Objective-C, який #include C-сумісні заголовки C ++, які позначені як C, сумісні із зовнішнім трюком "C" {} .

Відеоурок: https://www.youtube.com/watch?v=0x6JbiphNS4


2

Інші відповіді трохи неточні. Ви можете фактично змішати і Swift, і [Objective-] C [++] в одному файлі, хоча і не зовсім так, як ви очікували.

Цей файл (c.swift) компілюється у дійсний виконуваний файл з обома swiftc c.swiftта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;
}
// */

Ваш зразок коду в C, а не C ++. Краще піти з <iostream> прикладу IMHO.
kakyo

2

Один трюк (з багатьох) - це

Вам потрібен окремий заголовок для вашого мостового файлу obj-c ++ ...

Ви не можете просто кинути @interface та @implementation в один і той же .mm-файл, як це часто робиться зазвичай.

Отже, у своєму мостовому файлі заголовок у вас є

#import "Linkage.hpp"

Linkage.hpp має @interface для Linkkage, а Linkage.mm має @implementation for .mm

І потім

... ви насправді не #include "yourCpp.hpp" у Linkage.hpp.

Ви ставите лише #include "yourCpp.hpp"у файл Linkage.mm, а не у файл Linkage.hpp.

У багатьох онлайн-прикладах / навчальних посібниках автор просто поміщає @interface та @implementation у той самий файл .mm, як це часто робиться.

Це буде працювати в дуже простих мостових прикладах cpp, але,

Проблема полягає в наступному:

якщо ваш yourCpp.hpp має будь-які функції c ++, які, безумовно, буде (як, наприклад, перший рядок #include <something>), тоді процес вийде з ладу.

Але якщо ви просто НЕ мають #include "yourCpp.hpp"в Linkage заголовка файлу (це нормально , щоб мати його в файлі .mm, очевидно , вам потрібно) - це працює.

На жаль, це, на жаль, лише одна порада у всьому процесі.


1

Якщо це корисно для когось, я також маю короткий підручник із виклику простої статичної бібліотеки C ++ з тривіальної утиліти командного рядка Swift. Це дійсно голий доказ концептуального коду.

Ніякого об'єктивного C не задіяно, просто Swift та C ++. Код у бібліотеці C ++ викликається обгорткою C ++, яка реалізує функцію із зовнішнім зв'язком "C". Потім ця функція посилається в мостовому заголовку і викликається від Swift.

Див. Http://www.swiftprogrammer.info/swift_call_cpp.html


1

Я надаю посилання на SE-0038 в офіційному ресурсі, описаному як Це підтримує пропозиції щодо змін та видимих ​​для користувача удосконалень мови програмування Swift.

Станом на сьогодні, це запит на функцію, який був прийнятий, але ще не запланований.

Це посилання призначене для того, щоб спрямовувати тих, хто шукає цю функцію, у правильному напрямку

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.