[Разработка под iOS, C++, Xcode, Swift] Создаем Swift Package на основе C++ библиотеки (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Фото Kira auf der Heide на UnsplashДанная статья поможет вам создать свой первый Swift Package. Мы воспользуемся популярной C++ библиотекой для линейной алгебры Eigen, чтобы продемонстрировать, как можно обращаться к ней из Swift. Для простоты, мы портируем только часть возможностей Eigen.Трудности взаимодействия C++ и SwiftИспользование C++ кода из Swift в общем случае достаточно трудная задача. Все сильно зависит от того, какой именно код вы хотите портировать. Данные 2 языка не имеют соответствия API один-к-одному. Для подмножества языка C++ существуют автоматические генераторы Swift интерфейса (например, Scapix, Gluecodium). Они могут помочь вам, если разрабатывая библиотеку, вы готовы следовать некоторым ограничениям, чтобы ваш код просто конвертировался в другие языки. Тем не менее, если вы хотите портировать чужую библиотеку, то, как правило, это будет не просто. В таких ситуациях зачастую ваш единственный выбор: написать обертку вручную.Команда 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
Спасибо за внимание!
===========
Источник:
habr.com
===========
===========
Автор оригинала: Константин Семьянов
===========Похожие новости:
- [Java, C++, Разработка под Android] Android interop with SWIG (a guide). From simple to weird. Part 2 — weird
- [Разработка под iOS, Разработка мобильных приложений, Разработка под Android, Kotlin] Варианты настройки iosMain sourceSet'а в Kotlin Multiplatform Mobile
- [Разработка под iOS, Разработка под Android, C#, Xamarin] Экраны отсутствующего контента в мобильном приложении на примере Xamarin
- [Java, C++, Разработка под Android] Android interop with SWIG (a guide). From simple to weird. Part 1 — simple
- [Системное администрирование, Разработка под iOS, Разработка под MacOS] Безопасный downgrade macOS Big Sur (без 1008F)
- [Программирование, Анализ и проектирование систем, C++, ООП, Программирование микроконтроллеров] Micro Property — минималистичный сериализатор двоичных данных для embedded систем. Часть 2
- [Open source, C++, 3D-принтеры, DIY или Сделай сам] Перчатка Mark gauntlet v4.2
- [Разработка под iOS, Разработка мобильных приложений, Swift, Аналитика мобильных приложений] Почему я не могу найти Яндекс.Такси через системный поиск на iPhone?
- [Python, C++, C#, Математика, Профессиональная литература] С каких книг можно начать изучать программирование (Python, C#, C++, Java, Lua, …)
- [Разработка под iOS, Objective C, Swift] Memory Management: ARC vs MRC в iOS
Теги для поиска: #_razrabotka_pod_ios (Разработка под iOS), #_c++, #_xcode, #_swift, #_swift, #_ios_development, #_ios_razrabotka (ios разработка), #_spm, #_razrabotka_mobilnyh_prilozhenij (разработка мобильных приложений), #_cpp, #_swift_package_manager, #_razrabotka_pod_ios (
Разработка под iOS
), #_c++, #_xcode, #_swift
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:56
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Фото Kira auf der Heide на UnsplashДанная статья поможет вам создать свой первый Swift Package. Мы воспользуемся популярной C++ библиотекой для линейной алгебры Eigen, чтобы продемонстрировать, как можно обращаться к ней из Swift. Для простоты, мы портируем только часть возможностей Eigen.Трудности взаимодействия C++ и SwiftИспользование C++ кода из Swift в общем случае достаточно трудная задача. Все сильно зависит от того, какой именно код вы хотите портировать. Данные 2 языка не имеют соответствия API один-к-одному. Для подмножества языка C++ существуют автоматические генераторы Swift интерфейса (например, Scapix, Gluecodium). Они могут помочь вам, если разрабатывая библиотеку, вы готовы следовать некоторым ограничениям, чтобы ваш код просто конвертировался в другие языки. Тем не менее, если вы хотите портировать чужую библиотеку, то, как правило, это будет не просто. В таких ситуациях зачастую ваш единственный выбор: написать обертку вручную.Команда 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' 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" ) ] ) #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 #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 @_exported import ObjCEigen
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() Смотрите полный проект на GitHub.Ссылки
=========== Источник: habr.com =========== =========== Автор оригинала: Константин Семьянов ===========Похожие новости:
Разработка под iOS ), #_c++, #_xcode, #_swift |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 00:56
Часовой пояс: UTC + 5