总结:在创建包含 C++ 模块和 Obj-C 模块的 Swift 包时,C++ header 导入出现问题。
这里的模块或“客户端”中没有涉及 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-C MyPackage
, 所有 来自 C++ 包的头文件 MyPackageCPP
包括在内,即使只有 C 头文件 MyPointClass.h
明确包含在 Obj-C MyPackage 中(通过 #import "MyClass.h"
)(线索是“In file included from <module-includes>
”)
当我尝试将 Obj-C 模块包含到简单的 Objective-C 测试应用程序中时,我遇到了相同的构建错误 MyApp
导入 Obj-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++ 包中的所有 header 是否都必须包含在 Obj-C 包装器构建中? (我曾尝试将 MyClassCPP.h
添加到目标 MyPackage 的排除项中,但无济于事。)或者有没有一种方法可以让我在构建 Obj-C 时仅包含 C++ 包中的纯 C header wrapper MyPackage
(同时保持 C++ header 对 C++ 客户端可用)?
最佳答案
is there a way around this that will allow me to include only the pure C-headers from the C++ package, when building the Obj-C wrapper MyPackage (while keeping the C++header available for C++ clients)?
在尝试了多种方法之后,我找到了一种行之有效的方法。这些变化涉及:
修改结构文件 MyPointClass.h 如下(我在这里重命名为 MyPackageCommonHeaders.h):
typedef struct MyPoint { double x; double y; } MyPoint;
添加仅包含以下内容的文件 MyPackageCommonHeaders.c:
#include "../include/MyPackageCommonHeaders.h"
仅为共享 header 创建一个新模块
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++。其中的一个关键部分是一个简单的 C 结构 (MyPoint) 可用于两个 API,因此 Obj-C 包装器不必转换;即,Obj-C 包装器可以返回它从其包装的 C++ 方法中获得的相同对象。
关于c++ - 如何在另一个模块或 Xcode 项目中包含 Swift 包中的 C++ 模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67986278/