ฉันสามารถผสม Swift กับ C ++ ได้หรือไม่ เช่นเดียวกับไฟล์ Objective-C .mm


131

ฉันเพิ่งเปลี่ยนไฟล์. m เป็น. mm และใช้ C ++ มีวิธีทำเช่นเดียวกันกับ Swift หรือไม่?

คำตอบ:


111

ไม่เมื่อคุณเปลี่ยนจาก. m เป็น. mm คุณกำลังเปลี่ยนจาก Objective-C เป็นภาษาอื่น (ซึ่งมีความแตกต่างเล็กน้อย) ที่เรียกว่า Objective-C ++ ดังนั้นคุณจึงไม่ได้ใช้ C ++ จริงๆ คุณกำลังใช้ Objective-C ++ ซึ่งยอมรับ C ++ ส่วนใหญ่เป็นอินพุต (เช่นเดียวกับที่ C ++ ยอมรับ C ส่วนใหญ่ แต่ไม่ใช่ทั้งหมดเป็นอินพุต) เมื่อฉันบอกว่ามันไม่ใช่ C ++ ให้พิจารณาไฟล์ C ++ ที่มีตัวแปรชื่อnil(ซึ่งเป็น C ++ ตามกฎหมาย) แล้วพยายามรวบรวมเป็น Objective-C ++

Swift ไม่มีความสัมพันธ์แบบเดียวกัน ไม่ใช่ชุดพิเศษของ C หรือ C ++ และคุณไม่สามารถใช้โดยตรงใน.swiftไฟล์ได้

"การใช้ Swift กับ Cocoa และ Objective-C"ยังบอกเรา:

คุณไม่สามารถนำเข้ารหัส C ++ ไปยัง Swift ได้โดยตรง ให้สร้าง Objective-C หรือ C wrapper สำหรับโค้ด C ++ แทน


93
ค่อนข้างน่ารำคาญจริง ๆ - พวกเราทุกคนที่มีแอป Cocoa ซึ่งรวมฐานรหัส C / C ++ ตอนนี้ต้องดูแลโครงการที่เขียนใน3ภาษา ....
Jay

6
ฉันขอแนะนำว่า Objective-c ++ ไม่ใช่ภาษาอื่น แต่การผสมผสานของ c ++ และความแตกต่างของ Objective-c นั้นละเอียดอ่อน แต่ก็ยังคงอยู่ที่นั่น
amar

1
"ศูนย์" C ++ ถูกกฎหมายอย่างไร? ไม่มีสิ่งนั้น (อย่างน้อยใน c ++ มาตรฐาน) โปรดยกตัวอย่างอื่น
rewolf

3
แต่ด้วย ObjC คุณสามารถไปที่.mmไฟล์ของคุณและทำ (เกือบ) เสร็จ ไม่เป็นเช่นนั้นกับ Swift
chakrit

6
@rewolf ฉันคิดว่าเขาหมายถึงตัวแปรชื่อnilเช่นint nil
ลุค

165

ความสับสนที่อาจมาจากสมมติฐานที่ว่าเพียงแค่เปลี่ยนนามสกุลไฟล์จาก.mที่จะ.mmเป็นสิ่งที่คุณจำเป็นต้องสร้างสะพานเชื่อมภาษาเมื่อในความเป็นจริงมันไม่ทำอะไรเลยของการจัดเรียงที่ มันไม่ได้เป็น.mmที่ทำให้เกิดแรงเสียดทานกับ.cppมันเป็น.hส่วนหัวซึ่งจะต้องบวกไม่เป็นC++ส่วนหัว


โครงการเดียวกัน: ใช่

ในโครงการเดียวกันคุณสามารถผสมC , C ++ , Objective-C , Objective C ++ , Swiftและแม้แต่Assemblyได้อย่างมีความสุข

  1. ...Bridging-Header.h: คุณเปิดเผยC , Objective-CและObjective-C ++เป็นSwiftโดยใช้บริดจ์นี้
  2. <ProductModuleName>-Swift.h: exposes โดยอัตโนมัติของสวิฟท์ชั้นเรียนที่มีเครื่องหมาย@objcเพื่อวัตถุประสงค์ -C
  3. .h: นี่เป็นส่วนที่ยุ่งยากเนื่องจากมีการใช้อย่างคลุมเครือสำหรับทุกรสชาติของC , ++หรือไม่ก็ตามวัตถุประสงค์หรือไม่ เมื่อ a .hไม่มีคำหลักC ++คำเดียวเช่นclassสามารถเพิ่มลงในคำหลัก...Bridging-Header.hและจะแสดงฟังก์ชันที่เกี่ยวข้อง.c หรือ .cppฟังก์ชันที่ประกาศ มิฉะนั้นส่วนหัวดังกล่าวจะต้องถูกรวมไว้ในCบริสุทธิ์หรือObjective-C API

ไฟล์เดียวกัน: ไม่

ในไฟล์เดียวกันคุณไม่สามารถผสมทั้ง 5 ในไฟล์ต้นฉบับเดียวกันได้:

  1. .swift: คุณไม่สามารถผสมSwift ได้กับอะไรก็ได้
  2. .m: คุณสามารถผสมObjective-CกับC ( @Vinzzz )
  3. .mm: คุณสามารถผสมObjective-CกับC ++ได้ สะพานนี้เป็นวัตถุประสงค์-C ++ ( @Vinzzz ).
  4. .c: บริสุทธิ์C
  5. .cpp: คุณสามารถผสมC ++ & Assembly ( @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 ++ เป็นสองสิ่งที่แตกต่างกันแม้จะมีชื่อ )
Vinzzz

1
ฉันจะพูดถึงว่าไม่เหมือน C วัตถุประสงค์ C ++ ไม่ใช่ C superset ดังนั้นคุณจึงไม่สามารถผสมทั้ง 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 code:

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

โดยเฉพาะอย่างยิ่งตัวอย่างเรียกใช้ Objective C และฟังก์ชัน C ++ จาก Swift

กุญแจสำคัญคือการสร้างส่วนหัวที่ใช้ร่วมกัน Project-Bridging-Header.h และใส่ส่วนหัว Objective C ไว้ที่นั่น

โปรดดาวน์โหลดโครงการเป็นตัวอย่างที่สมบูรณ์


ขอบคุณสำหรับ Gian คนนี้!
Jason Elwood

ขอบคุณสำหรับตัวอย่างนี้ แต่ถ้าฉันต้องการย้าย #import "CPlusPlus.h" จาก ObjCtoCPlusPlus.mm ไปยังไฟล์ ObjCtoCPlusPlus.h คอมไพเลอร์จะส่งคืนข้อผิดพลาดนี้: <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 'เป็นไปได้ไหมที่จะย้ายการนำเข้านี้ไปไว้ในไฟล์ส่วนหัว
Alessandro Pirovano

@API: ไม่คุณพลาดประเด็นนี้ ObjCtoCPlusPlus.h/“ .mm` มีขึ้นเพื่อจุดประสงค์เดียวในการจัดหาอินเทอร์เฟซ Ob-C ให้กับโค้ด C ++ นั่นคือบริดจ์ซึ่งเป็นส่วนประกอบที่จำเป็นที่นี่ ปล่อยให้รวมไว้ที่ที่พวกเขาอยู่และเพิ่มวิธีการในObjCtoCPlusPlus.…ไฟล์สำหรับแต่ละวิธี C ++ ที่คุณต้องการเข้าถึง คุณควรอ่านsourcemaking.com/design_patterns/adapter
Slipp

ฉันดาวน์โหลดตัวอย่างของคุณและมันยอดเยี่ยมมากสำหรับฟังก์ชัน STATIC ที่คลาสเสนอเป็นตัวอย่าง แต่ฉันไม่สามารถจัดการปรับตัวอย่างสำหรับฟังก์ชันที่ไม่คงที่เพิ่มเติมที่จะใช้จากภายนอกได้ ... เป็นไปได้หรือไม่? (ฉันทำการบ้าน C ++ เมื่อหลายสิบปีก่อน ... ตอนนี้ฉันไม่ใช่ดินสอที่คมที่สุดในกล่อง)
Isaac

31

คุณสามารถข้าม Objective-C ได้ด้วยระหว่างไฟล์. เพียงเพิ่มไฟล์ส่วนหัว C ด้วยซอร์สไฟล์. cpp มีเฉพาะการประกาศ C ในไฟล์ส่วนหัวและรวมรหัส C ++ ใด ๆ ในไฟล์ต้นฉบับ จากนั้นรวมไฟล์ส่วนหัว C ไว้ใน ** - Bridging-Header.h

ตัวอย่างต่อไปนี้ส่งคืนตัวชี้ไปยังวัตถุ C ++ (โครงสร้าง Foo) เพื่อให้ Swift สามารถจัดเก็บใน COpaquePointer แทนที่จะมีการกำหนด struct Foo ในพื้นที่ส่วนกลาง

ไฟล์ Foo.h (เห็นโดย Swift - รวมอยู่ในไฟล์ bridging)

#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 (ไม่เห็นโดย 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
คำตอบนี้สมควรได้รับวิธี upvotes เพิ่มเติม มีประโยชน์จริงๆ.
rsp1984

นี่เป็นครั้งแรกที่ฉันเห็นการextern "C"ตัดส่วนหัวในตำแหน่งที่รวมไว้แทนที่จะเป็น#ifdef'ed ในไฟล์ส่วนหัว ยอดเยี่ยม!
dcow

27

ฉันเพิ่งทำโปรเจ็กต์ตัวอย่างเล็กน้อยโดยใช้ Swift, Objective-C และ C ++ เป็นการสาธิตวิธีใช้ OpenCV เย็บใน iOS OpenCV API คือ C ++ ดังนั้นเราจึงไม่สามารถพูดคุยโดยตรงจาก Swift ได้ ฉันใช้คลาส Wrapper ขนาดเล็กซึ่งไฟล์การใช้งานคือ Objective-C ++ หัวไฟล์สะอาด Objective-C เพื่อให้สวิฟท์สามารถพูดคุยกับนี้โดยตรง คุณต้องระวังอย่านำเข้าไฟล์ C ++ - ish โดยอ้อมไปยังส่วนหัวที่ Swift โต้ตอบด้วย

โครงการอยู่ที่นี่: https://github.com/foundry/OpenCVSwiftStitch


ฉันคิดว่านี่ตอบปัญหาที่ฉันพบในวันนี้โดยพยายามใช้ KudanCV ไลบรารีของบุคคลที่สามกับ Swift ส่วนหัวเชื่อมต่อของฉันนำเข้าไฟล์. h ซึ่งมีการนำเข้าไปยัง <memory> <strings> และ <vectors> ซึ่งฉันเดาว่ามาจากไลบรารีหรือไฟล์ C ++ ดังนั้นโค้ด Swift ของฉันจะไม่คอมไพล์ ฉันจะขอบคุณถ้ามีคนบอกฉันได้ว่ามีวิธีแก้ปัญหานี้หรือไม่ ... หรือถ้าฉันต้องเขียนโค้ดใหม่ทั้งหมดใน Objective-C
Rocket Garden

1
@RocketGarden - ไฟล์ส่วนหัว KudanCV คือ C ++ คุณสามารถเขียน wrapper ที่ทำงานในลักษณะเดียวกับคลาส Wrapper ตัวอย่างของฉัน หรือคุณเขียนส่วนเหล่านั้นของแอปของคุณใหม่ที่จำเป็นต้องพูดคุยกับ KudanCV ใน Objective-C ++ (ด้วยส่วนหัว Objective-C) คุณไม่จำเป็นต้องเขียนส่วนเหล่านั้นในโครงการของคุณใหม่ที่ไม่เกี่ยวข้องกับ KudanCV
โรงหล่อ

ขอบคุณสำหรับสิ่งนั้นฉันรู้ประมาณ 5 นาทีต่อมา แต่มันมีประโยชน์ที่จะบันทึกไว้สำหรับคนอื่น ๆ
Rocket Garden

13

นี่คือความพยายามของฉันที่ใช้เครื่องมือส่งเสียงดังเพื่อทำให้การสื่อสาร C ++ / รวดเร็วโดยอัตโนมัติ คุณสามารถติดตั้งคลาส C ++ ได้อย่างรวดเร็วสืบทอดจากคลาส C ++ และแม้แต่ลบล้างเมธอดเสมือนได้อย่างรวดเร็ว
มันจะแยกวิเคราะห์คลาส C ++ ที่คุณต้องการส่งออกให้รวดเร็วและสร้างสะพาน Objective-C / Objective-C ++ โดยอัตโนมัติ

https://github.com/sandym/swiftpp


8

Swift ไม่สามารถทำงานร่วมกับ C ++ ได้โดยตรง คุณสามารถแก้ไขปัญหาได้โดยการห่อรหัส C ++ ด้วย Objective-C และใช้ Objective C wrapper ใน Swift


นี่คือสิ่งที่ฉันทำ ฉันเขียนบทความเกี่ยวกับวิธีการรวม c ++ กับโครงการที่รวดเร็ว: learningswift.brightdigit.com/integrating-c-plus-plus-swift
leogdion

4

ฉันยังมีโปรแกรมสาธิตสำหรับการรวม opencv ที่รวดเร็ว

คุณสามารถดาวน์โหลดได้จากhttps://github.com/russj/swift_opencv3_demo

ข้อมูลเพิ่มเติมเกี่ยวกับการสาธิตhttp://flopalm.com/opencv-with-swift/


4

ไม่ใช่ไม่ใช่ในไฟล์เดียว

อย่างไรก็ตามคุณสามารถใช้ C ++ ใน Swift Projects ได้โดยไม่จำเป็นต้องมีไลบรารีแบบคงที่หรือเฟรมเวิร์ก เช่นเดียวกับคนอื่น ๆ กล่าวไว้กุญแจสำคัญคือการสร้างส่วนหัวของ Objective-C bridging ที่ # รวมส่วนหัว 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

เคล็ดลับอย่างหนึ่ง (จากหลาย ๆ คน) ก็คือ

คุณต้องมีส่วนหัวแยกต่างหากสำหรับไฟล์ bridging obj-c ++ ...

คุณไม่สามารถโยน @interface และ @implementation ในไฟล์. mm เดียวกันได้เหมือนที่ปกติ

ดังนั้นในไฟล์ส่วนหัวเชื่อมต่อของคุณคุณมี

#import "Linkage.hpp"

Linkage.hpp มี @interface สำหรับ Linkage และ Linkage.mm มี @implementation สำหรับ. mm

แล้ว

... จริงๆแล้วคุณไม่ # รวม "yourCpp.hpp" ใน Linkage.hpp

คุณใส่เฉพาะ#include "yourCpp.hpp"ไฟล์ Linkage.mm ไม่ใช่ในไฟล์ Linkage.hpp

ในตัวอย่าง / บทช่วยสอนออนไลน์จำนวนมากผู้เขียนเพียงแค่ใส่ @interface และ @implementation ไว้ในไฟล์. mm เดียวกันอย่างที่มักทำกัน

ซึ่งจะทำงานในตัวอย่างการเชื่อมต่อ cpp ที่เรียบง่าย แต่

ปัญหาคือ:

หาก yourCpp.hpp ของคุณมีคุณสมบัติ c ++ ใด ๆ ซึ่งมันจะแน่นอน (เช่นบรรทัดแรก#include <something>) กระบวนการจะล้มเหลว

แต่ถ้าคุณเพียงแค่ไม่ได้มี#include "yourCpp.hpp"ในการเชื่อมโยงส่วนหัวของไฟล์ (มันปรับให้มีไว้ในไฟล์ .mm อย่างเห็นได้ชัดคุณจะต้อง) - มันทำงาน

อีกครั้งนี่เป็นเพียงเคล็ดลับเดียวในกระบวนการทั้งหมด


1

ในกรณีนี้เป็นประโยชน์สำหรับทุกคนฉันยังมีบทช่วยสอนสั้น ๆ เกี่ยวกับการเรียกไลบรารีแบบคงที่ C ++ อย่างง่ายจากยูทิลิตี้บรรทัดคำสั่ง Swift เล็กน้อย นี่เป็นการพิสูจน์ชิ้นส่วนแนวคิดของรหัส

ไม่มี Objective-C เกี่ยวข้องเพียงแค่ Swift และ C ++ โค้ดในไลบรารี C ++ ถูกเรียกโดย C ++ wrapper ที่ใช้ฟังก์ชันที่มีลิงก์ "C" ภายนอก จากนั้นฟังก์ชันนั้นจะถูกอ้างอิงในส่วนหัวของการเชื่อมต่อและเรียกใช้จาก Swift

ดูhttp://www.swiftprogrammer.info/swift_call_cpp.html


1

ฉันกำลังให้ลิงก์ไปยังSE-0038ในแหล่งข้อมูลอย่างเป็นทางการโดยอธิบายว่า นี่จะเก็บรักษาข้อเสนอสำหรับการเปลี่ยนแปลงและการปรับปรุงที่ผู้ใช้มองเห็นได้สำหรับภาษาการเขียนโปรแกรม Swift

สถานะ ณ วันนี้คือนี่คือคำขอคุณลักษณะที่ได้รับการยอมรับ แต่ยังไม่มีกำหนดเวลา

ลิงก์นี้มีไว้เพื่อคัดท้ายทุกคนที่กำลังมองหาคุณลักษณะนี้ในทิศทางที่ถูกต้อง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.