ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Swift と C++ の同時利用
C++とObjective-C++のプロジェクトでSwiftを使用して、コードをより安全に、より速く、より簡単に開発する方法を学びます。C++とSwiftのAPIを使用して、あなたのアプリにSwiftを段階的に組み込む方法を紹介します。
関連する章
- 0:40 - Basics of interoperability
- 2:22 - Adding Swift to a C++ codebase
- 4:10 - Calling a C++ method in Swift
- 4:50 - Calling a Swift method in C++
- 7:32 - Improving how C++ APIs are imported
- 12:15 - Foreign reference types
リソース
関連ビデオ
WWDC23
-
ダウンロード
♪ ♪
こんにちはコンパイラチーム エンジニアのZoeです 今日はSwiftとC++を一緒に 使えるXcode15の新機能 相互運用性について ご紹介します このセッションは2つのパートに 分けて行われます まず相互運用性の基本について 説明します 次にSwiftでC++ APIを 自然で安全なものにする 方法を説明します 大規模なアプリが多くあり またObjective-Cで書かれたコードベースが 存在する世界に Swiftは乗り出しました Swiftがこの既存のコードを活用し コードベースに段階的に採用できる ことが不可欠でした 現在Swiftはこの相互運用性を 次のレベルに引き上げ さらに多くの場面でSwiftを 採用できるようにしています
大規模なC++コードベースの場合は 双方向の相互運用性を利用して Swiftを段階的に導入できる ようになりました アプリのC++ライブラリに アクセスする必要がある場合は Objective-Cブリッジ層を作成する必要が なくなりました サンプルアプリを参考にC++コードベースに Swiftを導入するのが いかに簡単かを見てみましょう
写真編集アプリを開発中です カメラロールから写真を選択したり 色の反転や明るさの変更も できます
コードを見ていく前に アプリの構造を見てみましょう
アプリは2つの部分に 分割できます 画像処理フレームワークと ユーザーインターフェイスコードです このアプリのベースとなっている 画像処理フレームワークは C++で書かれています ユーザーインターフェイス層が C++フレームワークと通信できるように したいと考えていましたので Objective-C++を使用して ViewControllerなどの ほとんどのユーザーインターフェイスを 実装しました 次にアプリのユーザーが 写真を数枚選択して カメラロールから 編集できるようにしたいと考えました SwiftUIにはこれを簡単にする新しい PhotoPickerビューがあるそうなので このアプリにSwiftを 導入したいと思います Xcode15以降ではSwiftを 簡単に採用できるようになり Objective-C++コードベースでは すべてのC++APIに引き続き アクセスできます それではSwiftファイルをプロジェクトに 追加することから始めましょう C++フレームワークを 使用しており XcodeはC++ APIを自動的に インポートするので ブリッジングヘッダは必要ありません
次にプロジェクトのビルド設定で C++の相互運用性を 有効にする必要があります ご存知のとおりSwiftはCおよび Objective-C APIを呼び出せるので ビルド設定は現在CとObjective-C モードに設定されています これをC++に変更できます
設定にC++とObjective-C++が 表示されるようになり C++ ImageKit frameworkから APIを直接呼び出せるようになりました Swiftファイルに戻ると 他のSwiftモジュールと同様 フレームワークをインポート できますので モジュール名をクリックして その内容を確認することも可能 これはSwift APIのように 見えるかもしれませんが 実際にはC++ ImageKit frameworkからの ものです これはSwiftコンパイラが それらをどのように認識するかです 今日使用するAPIを いくつか見てみましょう
一番下からCxxImageEngine型の 静的メンバーが表示されます これは安全でないポインタとして 現在インポートされていますが これについては 後ほど詳しく説明します CxxImageEngineには他にも loadImageとgetImagesという メンバーがありますのでこれらを すぐに使用しようと思います ここでPhotoPickerのすべてのUIを ドロップして C++と通信する2つのメソッドに 集中できるようにします
共有されているCxxImageEngineを取得し 選択した各画像に対して loadImageを呼び出して エンジンにロードできます SwiftでC++メソッドを呼び出すのは とても簡単です これでSwiftUIビューが 完成したので Objective-C++ ViewControllerで 利用したいです
それには構造体を公開する 必要がありますが それによりObjective-C++コードから アクセスできるようになります
素晴らしい! Swiftコードはすべてビルドされました これでViewControllerファイルに 移動して Swiftが生成したヘッダを インポートできるようになりました
このヘッダにはすべてのパブリック Swift APIが含まれています 生成されたヘッダを インポートしたので C++でSwiftコードの呼び出しを 開始できます まずSwiftUIビューを構築します そうすれば現在のメソッドを 呼び出せます
またXcodeは コード補完に役立ちます
これをデバイス上でテストしてみましょう
アプリをビルドして実行すると 新しいSwiftUIビューが表示 Objective-C++ アプリに 直接インポートされました
これは真の双方向の相互運用性の 一例です C++の型と関数をSwiftで シームレスに使用でき その逆も可能です C++ではSwiftUIビューを構築して 使用することができ ビューの本体がC++フレームワークに コールバックされました すべての統合はSwiftコンパイラによって 自動的に行われたため ブリッジ層を作成する必要は ありませんでした またすべてのAPIは 直接かつネイティブであるため 相互運用する他の言語の ほとんどとは異なり SwiftでC++ API を呼び出すときも その逆のときもオーバーヘッドはありません 今日はかなり小さなアプリを 扱っていますが Swiftコンパイラは 大規模で複雑なコードベースでも 相互運用性をサポートできます
Swiftは標準ライブラリと 他のソースの両方から ほとんどのC++コレクションを インポートできます Swiftは関数テンプレートと クラステンプレートの特殊化を処理 共有ポインタを使用したメモリ管理も 同様のユーザー定義型も サポートしています SwiftはこれらのインポートされたAPIを 高レベルで理解できます たとえば共有ポインタの保持と 解放操作について この高度な知識を活用して 一連の強力な最適化を適用可能です
逆に構造体やクラスと そのメソッド その他のメンバーなど ほとんどのSwift APIを C++に公開できます Arrayのようなジェネリック型や 毎年進化するresilient 型を 公開することもできます またC++の相互運用性は Xcodeにより完全サポート コード補完や定義へのジャンプに加え 両方の言語にわたるデバッガーサポートなどが 得られます これらはC++の相互運用性によって サポートされるAPIのほんの一部です Swiftコンパイラは各言語間で 使用されるAPIなどを 大規模なコードベース間の相互運用性をサポートし 一貫した体験を促進することで さらに多くの場面でSwiftを 採用できるようになります 相互運用性の基本が ご紹介できたところで この機能をさらに深く掘り下げて SwiftでC++ APIが自然で安全に 感じられるようにするための 他の方法を探ってみることにします Swiftコンパイラはほとんどの C++ APIを自動的にインポート 安全なSwift APIとして表示できます たとえばデフォルトでは C++型はSwift構造体として インポートされ C++演算子は該当する Swift演算子にマップされ コンテナはコレクションとして 自動的にインポートされます コンパイラを使用すると APIのインポート法の微調整も可能で さらに自然に感じられる APIを公開します これを行うにはアノテーションを使用して APIに関する詳細情報を コンパイラに提供します
たとえば関数またはメソッドで Swiftでは不自然なC++命名規則が 使用されている場合があります このような場合はアノテーションで インポートされた関数名を変更し 引数ラベルを追加するか ゲッタとセッタを計算された プロパティとしてインポートします
アノテーションは参照セマンティクスなど 高レベルのパターンの説明や ある種の型を Swiftクラスとしてインポートする際にも 有用です
またはAPIが安全であるにもかかわらず 安全でないと判断された場合は Swiftを修正します
これらのアノテーションは Swiftがインポートしている APIに関する情報を伝達する 強力な方法です サンプルアプリで使用する 異なるAPIを特定し Swiftが安全かつ直感的な方法で APIをインポートできるよう アノテーションをどう活用するかを 検討してみましょう
PhotoPickerが完成しましたので 編集した写真を ライブラリに保存するための 保存ボタンも追加したいと 思います
Swiftに戻ってインポートした すべてのAPIをもう一度見てみましょう
まず保存する写真を 収集する必要があります getImages関数を使用して この作業を処理します
getImages関数は C++ vectorを返します このメソッドを呼び出す前に Swiftでのvector動作の詳細を 理解しましょう Swift型は値型と参照型の2つの カテゴリに分類されます Swiftでは構造体は値型を表し クラスは参照型を表します
デフォルトではC++型は Swiftの値型としてインポートされます
したがってSwiftはSwift構造体 のように動作する値型として vectorをインポートします vectorと他のSwift構造体の 唯一の違いは Swiftが型の特別なメンバーを 使用することで コピーコンストラクタなどの 有効期間を管理します これらのコピーコンストラクタは 多くの場合ディープコピーを実行 変更された場合にのみ コピーされるSwift配列とは異なり Swiftがvectorをコピーすると すべての要素がコピーされます
画像のvectorを取得したので 反復処理も可能になり forループでの各画像を取得や 画像をuiImageに変換し直し 写真ライブラリに保存できるように なりました
このforループが機能するのはvectorに beginメソッドとendメソッドがあるためで Swiftはそれをコレクションとして 自動的にインポートします このコレクションへの自動適合により vectorをSwift配列に簡単変換 マップやフィルタなどのメソッドに アクセスできるようになります 安全のためSwiftの安全モデルに 当てはまらない C++イテレータAPIではなく SwiftコレクションAPIを 使用することが重要です
C++イテレータを使用すると 有効期間の問題や 無効なメモリアクセスなどのバグが 発生しやすくなります 一方SwiftコレクションAPIは C++コレクション上で 動作する場合でも 安全性が確保されています
Swiftコンパイラは安全でない C++ APIを使用不可として マークすることにより安全なAPIへの ガイドを支援し より安全な代替案を提案します Swiftアプリに戻りましょう C++ ImageEngineを使用するたびに 安全でないポインタであることが 気になっています 実際この型はSwiftとC++の両方で 常にポインタとして使用されます これは型に「参照セマンティクス」が あるためです これは型がオブジェクトの アイデンティティを意図しており 同じメモリへの参照を共有するため コピーは個別の値ではないことを 意味します 前に述べたようにSwiftタイプは 2つのカテゴリに分類されます 値型と参照型です Objective-Cでは値型と参照型も 明確に区別されているため これによりObjective-Cの型を 構造体やクラスにマッピング可能です C++の場合どの型がどのカテゴリに 分類されるかはあまり明確でないのは SwiftやObjective-Cとは異なり C++には値型と参照型の間に 明確な区別がないためです
したがってデフォルトではコンパイラは すべてを値型としてインポートします ただしSwiftではC++コードに アノテーションを追加することで 一部を参照型またはクラス型として インポートするオプションもあります
SWIFT_SHARED_REFERENCE 属性を使用することで CxxImageEngineを Swiftクラスにマップできます この属性はSwiftでは安全でないと されるポインタではなく Swiftが型が常にポインタまたは 参照として渡されることを強制し 型が間接化されることを意味します
コードを安全にするために Swiftは必要に応じて参照を保持したり 解放したりすることで参照の 有効期間を自動的に管理します この種の参照カウントを有効にするには 保持関数と解放関数の 両方をSwiftに提供する必要が あります C++ ImageKitヘッダについて 詳しく見てみましょう
swift/bridgingをインポートして SWIFT_SHARED_REFERENCEなどの アノテーションにアクセスできます これでこのアノテーションを型に 適用して Swiftが呼び出せる保持関数と解放関数の 両方を指定できるようになりました いいですね! ここではポインタを逆参照する 必要がなくなったことを示す Swiftコンパイラエラーが いくつか発生しています
このC++ APIを Swiftに認識させるために できることがもう1つあります ここのforループでgetImagesを 呼び出しています このようにゲッタとセッタを定義するのは C++では一般的ですが Swiftではあまり自然ではありません これをSwiftでよりネイティブに 感じさせるために Swift/bridgegingの 別のアノテーションを使用します SWIFT_COMPUTED_PROPERTY 属性をゲッタとセッタに適用して ペアをSwiftの計算プロパティに マップします もう一度C++ヘッダに移動して このアノテーションを適用しましょう
これで定義を2回クリックして getImagesメソッドの呼び出し元を 検索できるようになりました Swift呼び出し元を選択して 名称を「images」に変更します
きれいですね 最後にもう一度アプリを テストしてみましょう
写真を数枚選択してカメラロールに 保存し直せるようになりました
いいですね! このセッションではAPIの インポート方法を改善するために 使ったのは2つのアノテーションのみです C++ヘッダで利用可能はアノテーションは 他にもたくさんあります アクセスに必要なのは swift/bridgingのインポートだけ
Xcode15でC++の相互運用性を 有効にするには C++とObjective-Cの相互運用モードを 変更することだけです CとObjective-Cから C++とObjective-C++へ またSwiftとC++の相互運用性は すべてのApple Platformだけでなく LinuxやWindowsでもサポートします C++は大規模で複雑な言語なので フィードバックを もとにC++のAPIをインポートし Swift API公開方法を改善したいと考えています C++ APIのインポート方法を 変更すると 新しいバージョンの相互運用性が 作成されます それは新機能をいつ採用するかを 選択できることを意味し 今日から快適に開発時にC++ APIを 使用できるようになります
問題にお気づきの点やご提案など ありましたら Feedback Assistantから お知らせください C++の相互運用性は Swiftコンパイラワークグループで オープンソース設計されました このワークグループは 数十の企業エンジニアや学生で 構成されています ワークグループは SwiftとC++の相互運用性の 将来のビジョンを定義する 2つの文書を作成しており 時間の経過とともに この機能の進化を促進します
ワークグループのフォーラムに 参加することも可能です swift.orgにアクセスしてください
Swift 5.9では オーバーヘッドなしで C++ APIを自動的かつ安全に 使用できます 新しいSwiftコードをC++に公開することで Swiftを段階的に導入できます コンパイラに詳細な情報を 提供することにより インポート済APIの改善や 微調整も可能です
C++コードベースへのSwift導入を お楽しみください
-
-
4:10 - Calling a C++ method from Swift
func loadImage(_ image: UIImage) { // Load an image into the shared C++ class. CxxImageEngine.shared.pointee.loadImage(image) }
-
4:20 - Import a C++ framework
import CxxImageKit
-
4:45 - Import the Generated Header
#import "SampleApp-Swift.h"
-
4:57 - Calling a Swift method in C++
- (IBAction)openPhotoLibrary:(UIButton *)sender { // Construct SwiftUI view SampleApp::ImagePicker::init().present(self); }
-
8:22 - Using the SWIFT_COMPUTED_PROPERTY attribute
int getValue() const SWIFT_COMPUTED_PROPERTY; void setValue(int newValue);
-
8:42 - Using the SWIFT_SHARED_REFERENCE attribute
struct SWIFT_SHARED_REFERENCE(retain, release) CxxReferenceType;
-
8:52 - Using the SWIFT_RETURNS_INDEPENDENT_VALUE attribute
SWIFT_RETURNS_INDEPENDENT_VALUE std::string_view networkName() const;
-
10:45 - Using a for-loop to iterate over a C++ std::vector in Swift
// Get every image out of the shared C++ class. for image in CxxImageEngine.shared.pointee.getImages() { let uiImage = CxxImageEngine.shared.pointee.uiImageFrom(image) UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil) }
-
13:54 - Import swift/bridging
#import <swift/bridging>
-
14:01 - Applying the SWIFT_SHARED_REFERENCE attribute to CxxImageEngine
struct SWIFT_SHARED_REFERENCE(IKRetain, IKRelease) CxxImageEngine { // ... };
-
14:53 - Applying the SWIFT_COMPUTED_PROPERTY attribute to getImages
/// \returns all images that have been loaded into the engine. Includes any modifications that were /// applied to the images. SWIFT_COMPUTED_PROPERTY inline std::vector<Image *_Nonnull> getImages() const;
-
15:06 - Updated for-loop using the "images" computed property
// Get every image out of the shared C++ class. for image in CxxImageEngine.shared.pointee.images { let uiImage = CxxImageEngine.shared.pointee.uiImageFrom(image) UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil) }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。