C++ 라이브러리에서 Swift 패키지를 만드는 방법

24375 단어 tutorialcppswiftios
표지 사진은 Kira auf der HeideUnsplash에서 찍었다.
이 강좌는 당신이 자신Swift package을 만드는 과정을 지도할 것입니다.이것은 유행하는 C++ 선형 대수 라이브러리 Eigen 에 귀속을 제공합니다.간단히 보기 위해 일부 Eigen 기능만 Swift로 마이그레이션됩니다.

C++ 및 Swift 상호 운용의 당면 과제


C++에서 Swift로 연결하는 것은 복잡한 이야기입니다. 이것은 어느 C++ 코드를 이식하고 싶은지에 따라 결정됩니다.이 두 언어는 일대일 API 매핑이 명확하지 않습니다.자동 귀속 솔루션(예를 들어 Scapix, Gluecodium이 존재하지만 C++ 언어의 하위 집합만 성공적으로 비추는 데 성공할 수 있습니다.다른 언어로 변환하기 쉬운 코드를 작성하고 싶은 라이브러리 개발자라면, 이러한 귀속 해결 방안에 도움이 될 수 있습니다.그러나 만약에 제3자 도서관을 사용하고 싶다면 보통 어려움을 겪게 될 것이다.이 경우, 당신은 단지 하나의 선택만 있을 수 있습니다 - 수동으로 바인딩을 작성합니다.
스웨프트 팀은 도구 체인CObjective-C의 상호작용성을 제공했다.동시에 C++ interop 명세서가 하나 있는데 명확한 실시 시간표가 없다.이 목록에서는 C++ 및 Swift의 상호 운용성에 대한 설계 및 조정 가능성에 대해 설명합니다.가장 도전적인 문제 중 하나는 템플릿 가져오기입니다.C++ 템플릿은 Swift 범주와 비슷합니다.그러나 그것들은 중요한 차이가 있다.이 문서를 작성할 때 Swift는 비유형 템플릿 매개 변수, 템플릿 매개 변수 및 매개 변수 패키지를 지원하지 않습니다.또한 Swift 범주는 구속에 기반합니다C++20 concepts.또한 C++ 템플릿은 호출 사이트에서 특정 형식으로 템플릿을 대체하고 이 형식이 템플릿에서 호출된 문법을 지원하는지 확인하는 문법 교체를 실행합니다.요컨대, Swift에서 고도로 템플릿화된 C++ 라이브러리를 호출하고 싶다면, 정말 재수없다.

문제 설정


대량의 템플릿화된 C++ 라이브러리 Eigen 를 수동으로 연결해 봅시다.이것은 유행하는 선형 대수 라이브러리: 행렬, 벡터, 수치 해산기와 관련 알고리즘이다.패키지의 기본 전략은 다음과 같습니다. 구체적인 유형을 선택하여 Objective-C 클래스에 포장하면, 이 클래스는 Swift로 가져옵니다.
Objective-C API를 Swift로 가져오는 방법 중 하나는 C++ 라이브러리를 Xcode 프로젝트에 직접 추가하고 제공하는 것이다bridging header.그러나, 포장기를 단독 모듈로 컴파일하기를 원합니다.이 경우 패키지 관리자의 도움이 필요합니다.스웨프트의 팀은 대대적으로 홍보한다Swift Package Manager (SPM).역사적으로 SPM은 일부 기능이 부족해서 많은 개발자들이 그것을 이전할 수 없다.그러나 최근 몇 년 동안 그의 발전은 매우 빠르다.Xcode 12에서 최종적으로 자원과 가방을 묶을 수 있고 Swift 놀이공원에서 가방을 사용할 수 있습니다.
이 자습서에서는 SPM 패키지 SwiftyEigen을 만듭니다.구체적인 유형으로 우리는 임의의 행수와 열수를 가진 부동 행렬을 채택할 것이다.Matrix 클래스에는 행렬의 역을 계산하는 데 사용되는 초기 값 설정 항목, 아래 첨자 및 역 구사 방법이 있습니다.전체 항목은 https://github.com/ksemianov/SwiftyEigen에 있습니다.

프로젝트 구조


SPM에는 새 라이브러리를 만드는 데 유용한 템플릿이 있습니다.
foo@bar:~$ mkdir SwiftyEigen && cd SwiftyEigen
foo@bar:~/SwiftyEigen$ swift package init
foo@bar:~/SwiftyEigen$ git init && git add . && git commit -m 'Initial commit'
그런 다음 타사 라이브러리 Eigen을 서브 모듈로 추가합니다.
foo@bar:~/SwiftyEigen$ git submodule add https://gitlab.com/libeigen/eigen Sources/CPP
foo@bar:~/SwiftyEigen$ cd Sources/CPP && git checkout 3.3.9
그리고 우리는 우리의 가방 명세서 가방을 편집할 것이다.스위프트:
// swift-tools-version:5.3

import PackageDescription

let package = Package(
    name: "SwiftyEigen",
    products: [
        .library(
            name: "SwiftyEigen",
            targets: ["ObjCEigen", "SwiftyEigen"]
        )
    ],
    dependencies: [],
    targets: [
        .target(
            name: "ObjCEigen",
            path: "Sources/ObjC",
            cxxSettings: [
                .headerSearchPath("../CPP/"),
                .define("EIGEN_MPL2_ONLY")
            ]
        ),
        .target(
            name: "SwiftyEigen",
            dependencies: ["ObjCEigen"],
            path: "Sources/Swift"
        )
    ]
)
이 패키지 목록은 패키지를 컴파일하는 방법입니다.Swift 구축 시스템은 Objective-C와 Swift를 위한 별도의 목표를 작성합니다.SPM에서는 두 언어를 동일한 대상에서 혼합할 수 없습니다.ObjCEigen 대상은sources/ObjC 폴더의 원본을 사용하여sources/CPP 폴더를 헤더 검색 경로에 추가하고 EIGEN MPL2 에만 적용을 정의하여 EIGEN 사용 시 MPL2 허가를 보장합니다.SwiftyEigen 대상은 ObjCEigen에 의존하고 sources/Swift 폴더의 원본을 사용합니다.

수동 바인딩


아니요, Objective-C 클래스에 제목을 지정하여 Sources/objcegen/include 폴더에 배치합니다.
#pragma once

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface EIGMatrix: NSObject

@property (readonly) ptrdiff_t rows;
@property (readonly) ptrdiff_t cols;

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)matrixWithZeros:(ptrdiff_t)rows cols:(ptrdiff_t)cols
NS_SWIFT_NAME(zeros(rows:cols:));
+ (instancetype)matrixWithIdentity:(ptrdiff_t)rows cols:(ptrdiff_t)cols
NS_SWIFT_NAME(identity(rows:cols:));

- (float)valueAtRow:(ptrdiff_t)row col:(ptrdiff_t)col
NS_SWIFT_NAME(value(row:col:));
- (void)setValue:(float)value row:(ptrdiff_t)row col:(ptrdiff_t)col
NS_SWIFT_NAME(setValue(_:row:col:));

- (EIGMatrix*)inverse;

@end

NS_ASSUME_NONNULL_END
우리는 행,col 속성, 0과 항등식의 초기 값 설정 항목, 단일 값을 가져오고 설정하는 방법, 역방법이 있습니다.
Sources/objcegen 폴더에서 다음을 수행합니다.
#import "EIGMatrix.h"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"
#import <Eigen/Dense>
#pragma clang diagnostic pop

#import <iostream>

using Matrix = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>;
using Map = Eigen::Map<Matrix>;

@interface EIGMatrix ()

@property (readonly) Matrix matrix;

- (instancetype)initWithMatrix:(Matrix)matrix;

@end

@implementation EIGMatrix

- (instancetype)initWithMatrix:(Matrix)matrix {
    self = [super init];
    _matrix = matrix;
    return self;
}

- (ptrdiff_t)rows {
    return _matrix.rows();
}

- (ptrdiff_t)cols {
    return _matrix.cols();
}

+ (instancetype)matrixWithZeros:(ptrdiff_t)rows cols:(ptrdiff_t)cols {
    return [[EIGMatrix alloc] initWithMatrix:Matrix::Zero(rows, cols)];
}

+ (instancetype)matrixWithIdentity:(ptrdiff_t)rows cols:(ptrdiff_t)cols {
    return [[EIGMatrix alloc] initWithMatrix:Matrix::Identity(rows, cols)];
}

- (float)valueAtRow:(ptrdiff_t)row col:(ptrdiff_t)col {
    return _matrix(row, col);
}

- (void)setValue:(float)value row:(ptrdiff_t)row col:(ptrdiff_t)col {
    _matrix(row, col) = value;
}

- (instancetype)inverse {
    const Matrix result = _matrix.inverse();
    return [[EIGMatrix alloc] initWithMatrix:result];
}

- (NSString*)description {
    std::stringstream buffer;
    buffer << _matrix;
    const std::string string = buffer.str();
    return [NSString stringWithUTF8String:string.c_str()];
}

@end
Sources/Swift에서 Objective-C 코드를 Swift에 공개합니다(자세한 내용은 Swift Forums.
@_exported import ObjCEigen
또한 cleaner API의 아래 첨자를 제공합니다.
extension EIGMatrix {
    public subscript(row: Int, col: Int) -> Float {
        get { return value(row: row, col: col) }
        set { setValue(newValue, row: row, col: col) }
    }
}

사용 예


이제 다음과 같은 클래스를 사용할 수 있습니다.
import SwiftyEigen

// Create a new 3x3 identity matrix
let matrix = EIGMatrix.identity(rows: 3, cols: 3)

// Change a specific value
let row = 0
let col = 1
matrix[row, col] = -2

// Calculate the inverse of a matrix
let inverseMatrix = matrix.inverse()
마지막으로, 우리는 간단한 예시 프로젝트를 만들어서 패키지 라이브러리 Swifty Eigen의 기능을 보여줄 수 있다.2x2 행렬의 값을 입력한 다음 역 행렬을 계산할 수 있습니다.이를 위해 Xcode에 새 iOS 프로젝트를 만들고 패키지 폴더를finder에서 프로젝트 나비게이트로 드래그하여 로컬 의존 항목을 추가하고 Example target의 일반 설정에 SwiftyGen 프레임워크를 추가할 수 있습니다.그런 다음 UI를 작성하고 프로젝트를 완료했습니다.

전체 코드on GitHub를 참조하십시오.

도구책

  • SwiftyEigen Project
  • Eigen Linear Algebra Library
  • Swift Package Manager
  • C/Swift Interop
  • Objective-C/Swift Interop
  • Objective-C Bridging Header
  • C++/Swift Interop Manifest
  • C++20 Concepts
  • Automatic Bridging Solutions
  • 읽어주셔서 감사합니다!

    좋은 웹페이지 즐겨찾기