Create Swift Package based on C ++ library

Photo by Kira auf der Heide on Unsplash
Photo by Kira auf der Heide on Unsplash

This article will help you create your first Swift Package. We'll use the popular C ++ linear algebra library, Eigen, to demonstrate how you can access it from Swift. For simplicity, we are only porting a subset of Eigen's capabilities.






Difficulties of interaction between C ++ and Swift

C++ Swift . , . 2 API --. C++ Swift (, ScapixGluecodium). , , , . , , , , . : .





Swift interop  C  Objective-C  . , C++ interop  . C++ – . , C++ Swift . , . , Swift , template template variadic . , Swift , ( C++20 concepts). , C++ .





, C++ , !






++ Eigen, . , . : , Objective-C , Swift.





Objective-C API Swift – C++ Xcode  bridging header. , , . , . Swift  Swift Package Manager (SPM). , SPM , - . , SPM . Xcode 12, Swift playground.





SPM  SwiftyEigen. float .  Matrix  , .  GitHub.






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
      
      



Package.swift:





// 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 header search paths,  EIGEN_MPL2_ONLY, MPL2 Eigen.  SwiftyEigen  ObjCEigen  Sources/Swift.






Objective-C  Sources/ObjCEigen/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
      
      



readonly rows cols, , , .





 Sources/ObjCEigen:





#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
      
      



Objective-C Swift  Sources/Swift ( Swift Forums):





@_exported import ObjCEigen
      
      



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()
      
      



, , , SwiftyEigen. 2x2 . , iOS Xcode, Finder project navigator, , SwiftyEigen . UI :





 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





!








All Articles