c++ - 如何在另一个模块或 Xcode 项目中包含 Swift 包中的 C++ 模块

标签 c++ objective-c module swift-package-manager

总结:在创建包含 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)?

在尝试了多种方法之后,我找到了一种行之有效的方法。这些变化涉及:

  1. 修改结构文件 MyPointClass.h 如下(我在这里重命名为 MyPackageCommonHeaders.h):

    typedef struct MyPoint {
        double x;
        double y;
    } MyPoint;
    
  2. 添加仅包含以下内容的文件 MyPackageCommonHeaders.c:

    #include "../include/MyPackageCommonHeaders.h"
    
  3. 仅为共享 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++,这是我最初的目标)

  1. 修改 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/

相关文章:

c++ - OpenGL-如何随机绘制不重叠的圆

objective-c - 主窗口关闭时关闭 Cocoa 中的子窗口

python - 捆绑多个 Python 模块

javascript - ES6+ javascript 模块导出选项

Lazarus 中的 Windows 模拟 Apache 模块

c++ - 如何让 cpack 生成正确的开始菜单快捷方式

c++ - 删除数组c++崩溃问题

c++ - 如何在C++中的不交集中合并路径压缩?

ios - 关于 "location-based APIs for emergency services"上的苹果拒绝

ios - 将实时 FFT 设置传递给执行函数