Swift의 C ++ 클래스와 상호 작용


89

C ++로 작성된 중요한 클래스 라이브러리가 있습니다. 나는 그것들을 Swift 코드로 재 작성하기보다는 Swift 내의 어떤 유형의 브릿지를 통해 사용하려고합니다. 주요 동기는 C ++ 코드가 여러 플랫폼에서 사용되는 핵심 라이브러리를 나타내는 것입니다. 사실상 저는 OS X에서 핵심 기능이 작동 할 수 있도록 Swift 기반 UI를 만들고 있습니다.

"Swift에서 C ++ 함수를 어떻게 호출합니까?"라는 다른 질문이 있습니다. 이것은 내 질문 이 아닙니다 . C ++ 함수에 연결하려면 다음이 잘 작동합니다.

"C"를 통해 브리징 헤더 정의

#ifndef ImageReader_hpp
#define ImageReader_hpp

#ifdef __cplusplus
extern "C" {
#endif

    const char *hexdump(char *filename);
    const char *imageType(char *filename);

#ifdef __cplusplus
}
#endif

#endif /* ImageReader_hpp */

Swift 코드는 이제 함수를 직접 호출 할 수 있습니다.

let type = String.fromCString(imageType(filename))
let dump = String.fromCString(hexdump(filename))

내 질문은 더 구체적입니다. Swift 내에서 C ++ 클래스 를 인스턴스화하고 조작하려면 어떻게 해야합니까? 여기에 게시 된 내용을 찾을 수없는 것 같습니다.


9
저는 개인적으로 모든 관련 C ++ 호출을 재현하고 C ++ 클래스의 보류 인스턴스로 전달하는 Objective-C 클래스를 노출하는 일반 Objective-C ++ 래퍼 파일을 작성하는 데 의지했습니다. 제 경우에는 C ++ 클래스와 호출 수가 적기 때문에 특히 노동 집약적이지 않습니다. 그러나 나는 누군가가 더 나은 것을 생각해 낼 것이라는 희망으로 이것을 대답으로 옹호하는 것을 연기 할 것입니다.
Tommy

1
글쎄, 뭔가 ... 기다려 보자 (그리고 희망).
David Hoelzer 2016

IRC를 통해 실제 C ++ 개체에 대한 void 포인터를 유지하고 실제로 C 브리지와 개체에 대한 포인터를 통해 전달되는 필수 메서드를 노출하는 Swift 래퍼 클래스를 작성하라는 제안을 받았습니다.
David Hoelzer

4
이에 대한 업데이트로 Swift 3.0은 이제 출시되었으며 이전 약속에도 불구하고 C ++ 상호 운용성은 이제 "범위 외"로 표시됩니다.
데이비드 엘저

답변:


63

완벽하게 관리 할 수있는 답변을 찾았습니다. 이것이 얼마나 깨끗하길 바라는지는 전적으로 당신이 기꺼이 할 일의 양에 달려 있습니다.

먼저 C ++ 클래스를 가져 와서 인터페이스 할 C "래퍼"함수를 만듭니다. 예를 들어,이 C ++ 클래스가있는 경우 :

class MBR {
    std::string filename;

public:
    MBR (std::string filename);
    const char *hexdump();
    const char *imageType();
    const char *bootCode();
    const char *partitions();
private:
    bool readFile(unsigned char *buffer, const unsigned int length);
};

그런 다음 다음 C ++ 함수를 구현합니다.

#include "MBR.hpp"

using namespace std;
const void * initialize(char *filename)
{
    MBR *mbr = new MBR(filename);

    return (void *)mbr;
}

const char *hexdump(const void *object)
{
    MBR *mbr;
    static char retval[2048];

    mbr = (MBR *)object;
    strcpy(retval, mbr -> hexdump());
    return retval;
}

const char *imageType(const void *object)
{
    MBR *mbr;
    static char retval[256];

    mbr = (MBR *)object;
    strcpy(retval, mbr -> imageType());
    return retval;
}

브리지 헤더에는 다음이 포함됩니다.

#ifndef ImageReader_hpp
#define ImageReader_hpp

#ifdef __cplusplus
extern "C" {
#endif

    const void *initialize(char *filename);
    const char *hexdump(const void *object);
    const char *imageType(const void *object);

#ifdef __cplusplus
}
#endif

#endif /* ImageReader_hpp */

Swift에서 이제 객체를 인스턴스화하고 다음과 같이 상호 작용할 수 있습니다.

let cppObject = UnsafeMutablePointer<Void>(initialize(filename))
let type = String.fromCString(imageType(cppObject))
let dump = String.fromCString(hexdump(cppObject))                
self.imageTypeLabel.stringValue = type!
self.dumpDisplay.stringValue = dump!

보시다시피 해결책 (실제로는 다소 간단합니다)은 개체를 인스턴스화하고 해당 개체에 대한 포인터를 반환하는 래퍼를 만드는 것입니다. 그런 다음 래퍼 함수로 다시 전달되어 해당 클래스를 준수하는 객체로 쉽게 처리하고 멤버 함수를 호출 할 수 있습니다.

더 깨끗하게 만들기

이것은 환상적인 시작이며 사소한 브리지로 기존 C ++ 클래스를 사용하는 것이 완전히 가능하다는 것을 증명하지만 더 깔끔 할 수 있습니다.

이를 정리하면 UnsafeMutablePointer<Void>Swift 코드 중간에서를 제거하고 이를 Swift 클래스로 캡슐화합니다. 기본적으로 동일한 C / C ++ 래퍼 함수를 ​​사용하지만 Swift 클래스와 인터페이스합니다. Swift 클래스는 객체 참조를 유지하고 기본적으로 모든 메서드 및 속성 참조 호출을 브리지를 통해 C ++ 객체로 전달합니다!

이렇게하면 모든 브리징 코드가 Swift 클래스에 완전히 캡슐화됩니다. 여전히 C 브리지를 사용하고 있지만 Objective-C 또는 Objective-C ++로 다시 코딩하지 않고도 C ++ 객체를 투명하게 효과적으로 사용하고 있습니다.


13
여기에 몇 가지 중요한 누락 된 문제가 있습니다. 첫 번째는 소멸자가 호출되지 않고 객체가 유출된다는 것입니다. 두 번째는 예외가 심각한 문제를 일으킬 수 있다는 것입니다.
Michael Anderson

2
이 질문에 대한 답변에서 여러 문제를 다루었습니다. stackoverflow.com/questions/2045774/…
Michael Anderson

2
@DavidHoelzer, 일부 개발자는 전체 C ++ 라이브러리를 래핑하고 Swift로 작성된대로 사용하려고하기 때문에이 아이디어를 강조하고 싶었습니다. "Swift 내에서 C ++ 클래스를 인스턴스화하고 조작"하는 방법에 대한 훌륭한 예제를 제공했습니다! 그리고 저는이 기술이 신중하게 사용되어야한다고 덧붙이고 싶었습니다! 귀하의 모범에 감사드립니다!
Nicolai Nita

1
이것이 "완벽"하다고 생각하지 마십시오. Objective-C 래퍼를 작성한 다음 Swift에서 사용하는 것과 거의 동일하다고 생각합니다.
Bagusflyer

1
@Bagusflyer 확실히 ... 객관적인 C 래퍼가 아님을 제외하고는.
David Hoelzer

11

Swift에는 현재 C ++ interop이 없습니다. 장기적인 목표이지만 가까운 장래에 일어날 가능성은 거의 없습니다.


25
"사용 C 래퍼" "C ++ 상호 운용성"의 형태를 호출하면 용어를 꽤 스트레칭
Catfish_Man

6
아마도 그것들을 사용하여 C ++ 클래스를 인스턴스화 한 다음 그에 대한 메서드를 호출하면 유용하고 질문을 만족시킬 수 있습니다.
David Hoelzer

2
사실, 더 이상 장기적인 목표는 아니지만 로드맵에서 "범위 외"로 표시됩니다.
데이비드 엘저

4
C-래퍼를 사용하는 등 거의 모든 C ++ 상호 운용성 어떤 언어, 파이썬, 자바, 표준 접근 방식이다
앤드류 폴 시몬스

8

자신의 솔루션 외에도 다른 방법이 있습니다. Objective-C ++에서 C ++ 코드를 호출하거나 직접 작성할 수 있습니다.

따라서 C ++ 코드 위에 객관적인 C ++ 래퍼를 만들고 적절한 인터페이스를 만들 수 있습니다.

그런 다음 신속한 코드에서 목표 C ++ 코드를 호출하십시오. 객관적인 C ++ 코드를 작성하려면 파일 확장자를 .m에서 .mm로 바꿔야 할 수 있습니다.

적절할 때 C ++ 객체에 의해 할당 된 메모리를 해제하는 것을 잊지 마십시오.


7

Scapix Language Bridge 를 사용 하여 C ++를 Swift에 자동으로 연결할 수 있습니다 (다른 언어 중에서). C ++ 헤더 파일에서 직접 즉석에서 자동으로 생성 된 브리지 코드. 다음은 예입니다 .

C ++ :

#include <scapix/bridge/object.h>

class contact : public scapix::bridge::object<contact>
{
public:
    std::string name();
    void send_message(const std::string& msg, std::shared_ptr<contact> from);
    void add_tags(const std::vector<std::string>& tags);
    void add_friends(std::vector<std::shared_ptr<contact>> friends);
};

빠른:

class ViewController: UIViewController {
    func send(friend: Contact) {
        let c = Contact()

        contact.sendMessage("Hello", friend)
        contact.addTags(["a","b","c"])
        contact.addFriends([friend])
    }
}

흥미 롭군. 당신처럼 보인다는 2019 년 3 월에 처음이 발표
데이비드 엘저

@ boris-rasin 예제에서 신속한 브리지에 대한 직접 C ++를 찾을 수 없습니다. 계획에이 기능이 있습니까?
sage444

2
예, 현재로서는 Swift 브리지에 대한 직접적인 C ++가 없습니다. 그러나 C ++ to ObjC 브리지는 Swift에서 완벽하게 작동합니다 (Apple의 ObjC to Swift 브리지를 통해). 저는 지금 다이렉트 브릿지에서 작업 중입니다 (경우에 따라 더 나은 성능을 제공 할 것입니다).
Boris Rasin

1
오 예, 당신은 완전히 옳지 만 Linux의 베어 신속한 프로젝트에서는 그렇지 않습니다. 귀하의 구현을 기대합니다.
sage444

6

또 다른 답변에서 언급했듯이 ObjC ++를 사용하여 상호 작용하는 것이 훨씬 쉽습니다. .m 및 xcode / clang 대신 파일 이름을 .mm로 지정하면 해당 파일의 C ++에 액세스 할 수 있습니다.

ObjC ++는 C ++ 상속을 지원하지 않습니다. ObjC ++에서 C ++ 클래스를 하위 클래스로 만들고 싶지만 할 수 없습니다. C ++로 서브 클래스를 작성하고 ObjC ++ 클래스를 감싸 야합니다.

그런 다음 일반적으로 swift에서 objc를 호출하는 데 사용하는 브리징 헤더를 사용합니다.


2
요점을 놓쳤을 수 있습니다. 인터페이스는 현재 OS X에서도 지원하는 매우 큰 다중 플랫폼 C ++ 애플리케이션을 가지고 있기 때문에 필요합니다. Objective C ++는 실제로 전체 프로젝트를위한 옵션이 아닙니다.
David Hoelzer

내가 당신을 이해하는지 잘 모르겠습니다. swift와 C ++ 또는 그 반대로 호출을 원할 때 ObjC ++가 필요합니다. 경계는 전체 프로젝트가 아닌 objc ++로 작성하면됩니다.
nnrales

1
네, 질문을 검토했습니다. 신속하게 C ++를 직접 조작하려는 것 같습니다. 어떻게하는지 모르겠어요.
nnrales

1
그것이 내 게시 된 답변이 성취 한 것입니다. C 함수를 사용하면 임의의 메모리 덩어리로 객체를 안팎으로 전달하고 양쪽에서 메서드를 호출 할 수 있습니다.
David Hoelzer

1
@DavidHoelzer 멋지다고 생각하지만이 방법으로 코드를 더 복잡하게 만들지 않습니까? 주관적인 것 같아요. ObjC ++ 래퍼는 나에게 더 깨끗해 보입니다.
nnrales
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.