c++ - 如何将 Swift 包中的 C++ 模块包含在另一个模块或 Xcode 项目中
问题描述
摘要:在创建包含 C++ 模块和 Obj-C 模块的 Swift 包时,C++ 标头导入出现问题。
这里的模块或“客户端”不涉及 Swift 代码 - 这只是关于使用 Swift 包管理器来创建和使用模块。过去,我经常使用框架使用来自 Objective-C 客户端(以及来自 Swift 代码使用的 Objective-C 包装器)的 C++ 代码。
我正在尝试使用 Swift 包管理器来创建一个包含 C++ 和 Objective-C API 的包。我有一个 Swift 包,带有工作单元测试,结构如下:MyPackageCPP
是 C++ 实现;MyPackage
是围绕它的objective-C 包装器。
Package.swift 文件是
import PackageDescription
let package = Package(
name: "MyPackage",
platforms: [.iOS(.v13)],
products: [
.library(name: "MyPackageCPP", targets: ["MyPackageCPP"]),
.library(name: "MyPackage", targets: ["MyPackage"]),
],
targets: [
.target(
name: "MyPackageCPP",
path: "Sources/MyPackageCPP",
publicHeadersPath: "include"
),
.target(
name: "MyPackage",
dependencies: ["MyPackageCPP"],
path: "Sources/MyPackage",
exclude: [
"Info.plist",
],
publicHeadersPath: "include"
),
.testTarget( // C++ tests
name: "MyPackageCPPTests",
dependencies: ["MyPackageCPP"]
),
.testTarget( // Obj-C wrapper tests
name: "MyPackageTests",
dependencies: ["MyPackage"]
)
],
cxxLanguageStandard: .gnucxx1z
)
目录结构是
MyPackage
|
.- Sources
| |
| .- MyPackage
| | |
| . .- Classes (implementation files for Objective-C wrappers)
| | |
| | .- include
| | MyClass.h (Obj-C header)
| |
| .- MyPackageCPP
| |
| .- Classes (C++ Implementation files)
| |
| .- include
| MyClassCPP.h (C++ header)
| MyPointClass.h (Simple C-header defining a custom struct)
|
.- Tests
|
.- MyPackageCPPTests
| MyPackageCPPTests.mm (XCTestCase-based unit tests for C++ API)
|
.- MyPackageTests
MyPackageTests.mm (XCTestCase-based unit tests for pure Obj-C API)
关键头文件:
MyPointClass.h(旨在定义一个可由 C++ 类及其 Obj-C 包装器使用的简单 C 类型):
struct MyPoint {
double x;
double y;
};
MyClassCPP.h:
#pragma once
#include "MyPointClass.h"
#include <string>
#include <memory>
class MyClassCPP {
public:
MyClassCPP() = delete;
MyClassCPP(const std::string &info);
~MyClassCPP();
bool initialized();
MyPoint modifyPoint(MyPoint point);
private:
struct Impl;
std::unique_ptr <struct Impl> _pImpl;
};
MyClass.h(Obj-C 包装类)
#import <UIKit/UIKit.h>
#import "MyPointClass.h"
NS_ASSUME_NONNULL_BEGIN
// iOS Platform specific implementation
@interface MyClass : NSObject
-(instancetype)initWithInfoInAString:(NSString*)someInfoInAString;
-(MyPoint)modifyPoint:(MyPoint)point;
@property (atomic, readonly) BOOL initialized;
@end
NS_ASSUME_NONNULL_END
问题
我一直遇到包含问题。单元测试像现在一样工作,但是如果我将 Obj-C 测试文件从 更改MyPackageTests.mm
为MyPackageTests.m
,我会遇到构建失败'string' not found.
我知道需要 Objective-C++ (*.mm) 才能正确构建使用 C++ 的 Objective-C 文件,但 MyPackageTests 的直接包含链不包含任何 C++。相反,从错误中可以看出,在构建模块 Obj-CMyPackage
时,包含 C++ 包中的所有头文件MyPackageCPP
,即使MyPointClass.h
Obj-C MyPackage 仅显式包含 C 头文件(通过#import "MyClass.h"
)(线索是“ In file included from <module-includes>
”)
当我尝试将 Obj-C 模块包含到MyApp
导入 Obj-C 包装器的简单 Objective-C 测试应用程序中时,我得到了相同的构建错误MyPackage
:
While building module 'MyPackage' imported from /Users/{user}/TestCode/MyPackage/Tests/MyPackageTests/MyPackageTests.m:3:
While building module 'MyPackageCPP' imported from /Users/{user}/TestCode/MyPackage/Sources/MyPackage/include/MyClass.h:3:
In file included from <module-includes>:1: /Users/{user}/TestCode/MyPackage/Sources/MyPackageCPP/include/MyClassCPP.h:5:10: fatal error: 'string' file not found
#include <string>
问题
C++ 包中的所有头文件是否都必须包含在 Obj-C 包装器构建中?(我尝试添加MyClassCPP.h
到目标 MyPackage 的排除项,但无济于事。)或者有没有办法解决这个问题,允许我在构建 Obj-C 包装器时只包含 C++ 包中的纯 C 头文件MyPackage
(同时保持 C++ 头文件可用于 C++ 客户端)?
解决方案
在构建 Obj-C 包装器 MyPackage 时(同时保持 C++ 头文件可用于 C++ 客户端),有没有办法解决这个问题?
在尝试了多种方法后,我找到了一种可行的方法。这些变化涉及:
修改结构文件 MyPointClass.h 如下(我在这里重命名为 MyPackageCommonHeaders.h):
typedef struct MyPoint { double x; double y; } MyPoint;
添加文件 MyPackageCommonHeaders.c 仅包含以下内容:
#include "../include/MyPackageCommonHeaders.h"
仅为共享标头创建一个新模块
MyPackage | .- Sources | | | .- MyPackage | | | | | .- Classes (implementation files for Objective-C wrappers) | | | | | .- include | | MyClass.h (Obj-C header) | | | .- MyPackageCommonHeaders // NEW MODULE | | | | | .- Classes (C++ Implementation files) | | MyPackageCommonHeaders.c // NEW FILE | | | | | .- include | | MyPackageCommonHeaders.h (Simple C-header) | | | .- MyPackageCPP | | | .- Classes (C++ Implementation files) | | | .- include | MyClassCPP.h (C++ header) | .- Tests | .- MyPackageCPPTests | MyPackageCPPTests.mm (XCTestCase-based unit tests for C++ API) | .- MyPackageTests MyPackageTests.m (XCTestCase-based unit tests for pure Obj-C API)
(注意 MyPackageTests.m 不再是 Obj-C++,这是我最初的目标)
修改 Package.swift 以包含新模块,将其列为 C++ 和 Obj-C API mmodules 的依赖项:
import PackageDescription let package = Package( name: "MyPackage", platforms: [.iOS(.v13)], products: [ .library(name: "MyPackageCommonHeaders", targets: ["MyPackageCommonHeaders"]), .library(name: "MyPackageCPP", targets: ["MyPackageCPP"]), .library(name: "MyPackage", targets: ["MyPackage"]), ], targets: [ .target( name: "MyPackageCPP", dependencies: ["MyPackageCommonHeaders"], path: "Sources/MyPackageCPP", publicHeadersPath: "include" ), .target( name: "MyPackage", dependencies: ["MyPackageCPP", "MyPackageCommonHeaders"], path: "Sources/MyPackage", exclude: [ "Info.plist", ], publicHeadersPath: "include" ), .testTarget( // C++ tests name: "MyPackageCPPTests", dependencies: ["MyPackageCPP"] ), .testTarget( // Obj-C wrapper tests name: "MyPackageTests", dependencies: ["MyPackage"] ) ], cxxLanguageStandard: .gnucxx1z )
有了这个,我可以创建一个支持 C++ API 的封装,以及一个使用 C++ 代码的纯 Obj-C 包装 API。请注意,Obj-C 包装器不会强制将 Obj-C 调用者提升为 Obj-C++。其中一个关键部分是两个API 都可以使用一个简单的 C-struct (MyPoint),因此 Obj-C 包装器不必翻译;即,Obj-C 包装器可以返回从其包装的 C++ 方法获得的相同对象。
推荐阅读
- pandas - 熊猫 groupby 与 None
- python - Python - 在某个值之后使用列块进行数组递增
- python - 在 python 中使用 .apply 查找四分位数
- r - 警告消息:通过将日期转换为数值
- url-rewriting - 您如何协调移动网站和完整网站之间的重定向?
- typescript - Module augmentation / Defining types over non-typed files
- sql - Using a trigger to update a specific value in one table based on a specific value of another table
- python - Python pandas 数据框/字典到 csv
- java - 如何在基于 xml 的 spring 配置中为 hibernate.javax.cache.uri 属性指定相对路径
- php - 带变量的 PHP7 UTF-8 代码点转义序列