Swift에서 C++ 호출하기

shintwl·2024년 4월 22일
2
post-thumbnail

openCV 같은 이미지, 영상처리 라이브러리는 C++로 작성되어 있는 경우가 많습니다.
영상, 이미지처리에 AI를 사용하는 경우에는 openCV를 정말 많이 사용하죠.
iOS 앱에서 혹은 프레임워크에서 C++ 라이브러리를 사용하려면 어떻게 했어야 했는지 그리고 어떻게 바뀌었는지 알아보겠습니다.

Objective C++ Wrapper

저는 C++를 하나도 모르기 때문에 챗GPT에게 Hello world를 출력하는 메서드를 가진 클래스를 만들어달라고 요청하겠습니다

Please create a c++ class that have a function of print "Hello world"

// HelloWorld.hpp

#ifndef HELLOWORLD_H
#define HELLOWORLD_H

class HelloWorld {
public:
    void printHelloWorld();
};

#endif

// HelloWorld.cpp

#include "HelloWorld.h"
#include <iostream>

void HelloWorld::printHelloWorld() {
    std::cout << "Hello, world!" << std::endl;
}

이 코드를 iOS 프로젝트에 추가해주겠습니다

cpp 파일을 생성하려고 시도하면 Bridging header도 같이 만들거냐고 물어봅니다.
우리는 Swift에서 사용하는 것이 목적이기 때문에 생성을 해줍니다

//xxx-Bridging-Header.h

C++ 파일을 Swift에서 사용하기 위해 HelloWorld를 감싼 Objective-C++ Wrapper 파일을 생성하겠습니다.
Cpp 파일을 사용해야 하기 때문에 .m이 아닌 .mm이 확장자입니다. (ObjC기초문법)

// HelloWorldWrapper.hpp
#ifndef HelloWorldWrapper_h
#define HelloWorldWrapper_h

#import <Foundation/Foundation.h>
@interface HelloWorldWrapper : NSObject

- (void)printHelloWorld;

@end

#endif

// HelloWorldWrapper.mm
#import <Foundation/Foundation.h>
#import "HelloWorld.hpp"
#import "HelloWorldWrapper.h"

@interface HelloWorldWrapper ()

@end

@implementation HelloWorldWrapper

- (void)printHelloWorld {
    HelloWorld obj;
    obj.printHelloWorld();
}

@end

Bridging header에 HelloWorldWrapper를 임포트해서 Swift에게 존재를 알려줍니다

// xxx-Bridging-Header.h
#import "HelloWorldWrapper.h"

드디어 우리는 Swift 파일을 사용할 수 있습니다

import Foundation

struct HelloWorldCaller {
    func printHelloWorld() {
        let wrapper = HelloWorldWrapper()
        wrapper.printHelloWorld()
    }
}

Swift로 다이렉트 호출

WWDC23에서 애플은 Mix Swift and C++라는 제목으로 Swift에서 바로 C++를 호출할 수 있고 심지어 반대의 경우도 가능하게 되었다고 밝혔습니다.

Swift에서 C++ 파일을 호출하기 위해서는 C++ 파일이 프레임워크 안에 있어야 합니다

앱 프로젝트를 만들고 그 안에 Target을 추가하는 방식으로 Framework를 만들어줍니다
Framework를 만들때 언어는 Objective-C로 해줍니다

그 안에 C++ 파일을 만들어줍니다

Objective-C 래퍼를 사용할 때와 거의 같은 코드입니다
__cplusplus만 빼고요 (C++ 환경에서 사용하겠다는 의미입니다)

// HelloWorld.hpp

#if __cplusplus // 🔥

class HelloWorld {
public:
    void printHelloWorld();
};

#endif

// HelloWorld.cpp

#include "HelloWorld.h"
#include <iostream>

void HelloWorld::printHelloWorld() {
    std::cout << "Hello, world!" << std::endl;
}

프레임워크의 umbrella header에 hpp를 적고 Build Phases에 추가해줍니다

// CPPFramework.h

#import <Foundation/Foundation.h>

//! Project version number for CPPFramework.
FOUNDATION_EXPORT double CPPFrameworkVersionNumber;

//! Project version string for CPPFramework.
FOUNDATION_EXPORT const unsigned char CPPFrameworkVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <CPPFramework/PublicHeader.h>

#import <CPPFramework/HelloWorld.hpp>

이후 앱의 빌드세팅에서 C++ and Objective-C Interoperability를 C++/Objective-C++로 변경합니다
Bridging header는 필요하지 않습니다

그 이후에 앱의 ViewController.swift에서 호출해주면 끝입니다

import UIKit
import CPPFramework // 🔥

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()

        var obj = HelloWorld() // 🔥
        obj.printHelloWorld()
    }
}

참고

https://www.swift.org/documentation/cxx-interop/
https://developer.apple.com/videos/play/wwdc2023/10172/
https://developer.apple.com/documentation/swift/mixinglanguagesinanxcodeproject
https://github.com/apple/swift/blob/main/docs/CppInteroperability/GettingStartedWithC%2B%2BInterop.md
https://github.com/apple/swift/blob/main/docs/CppInteroperability/CppInteroperabilityStatus.md
https://medium.com/@canakyildz/c-and-swift-mix-from-obj-c-bridging-layers-to-interoperability-c558238c5b52

3개의 댓글

comment-user-thumbnail
2024년 4월 23일

비슷하게 coreML에서 머신러닝 모델을 파이토치나 텐서플로우모델로 불러오는 기능도 생겼더라고요
기존에는 swift가 다른언어와 동떨어진 섬같은느낌이었는데 이제는 무역항이생긴느낌이 들긴하더라고요 ㅎㅎ

1개의 답글
comment-user-thumbnail
2024년 4월 26일

최근데 외부라이브러리 사용으로 object-c 코드를 봐야하는 일이 있었습니다 .
Bridging header, AppDelegate.mm 파일 확장자 이름 부터 import 구문까지 낯설더라구요
소개해주신 코드가 그때 봤던 코드와 비슷해서 놀랐습니다 .
이해해 보려는 생각은 못해봤는데, 포스팅해주신 덕분에 한번더 눈에 익히고 갑니다.

답글 달기