我能否在同一个Swift包中使用C++和Swift混编,使用Swift包管理器?

3
我希望写一个C++模块,并在Swift中使用一些C函数。
我有些困惑,因为无论我做什么,SPM都会尝试将C++代码编译为Objective-C,当然它找不到正确的头文件。
这是我的尝试。
源代码目录结构:
Sources
|
+-CxxModule
| |
| +-include
| | |
| | +-CxxModule.hpp
| |
| +-CxxModule.cpp
|
+-SwiftModule
  |
  +-SwiftModule.swift

清单Package.swift如下:
// swift-tools-version: 5.6

import PackageDescription

let package = Package(
    name: "CxxLibrary",
    products: [
        .library(
            name: "CxxLibrary",
            targets: ["SwiftModule"]),
    ],
    dependencies: [
    ],
    targets: [
        .target(
            name: "CxxModule",
            dependencies: []),
        .target(
            name: "SwiftModule",
            dependencies: ["CxxModule"],
            path: "Sources/SwiftModule"
        ),
    ]
)

CxxModule.hpp 的内容如下:

#ifndef CxxModule_hpp
#define CxxModule_hpp

#include <iostream>

extern "C" void printHello();

#endif /* CxxModule_hpp */

CxxModule.cpp 的内容如下:

#include "CxxModule.hpp"

void printHello() {
    // use something from the standard library to make sure
    // c++ is really being used
    std::cout << "Hello, world!" << std::endl;
}

最后,SwiftModule.swift文件:
import CxxModule

我有什么遗漏吗?有没有办法告诉SPM该模块是用C++编写的?还是说目前不支持这种方式?
注意:如果我删除Swift目标,C++可以正常编译。
2个回答

2

我已经解决了这个问题。答案是“是”,只要Swift所暴露的头文件在纯C中可读。

首先,我将所有特定于C ++的代码(尤其是引用标准库的头文件)移入源cpp文件中:

#include <iostream>
#include "CxxModule.hpp"

void printHello() {
    // use something from the standard library to make sure
    // c++ is really being used
    std::cout << "Hello, world!" << std::endl;
}

第二步,我添加了宏,使得头文件在C和C++中都可以读取:
#ifndef CxxModule_hpp
#define CxxModule_hpp

#ifdef __cplusplus
extern "C" {
#endif

void printHello();

#ifdef __cplusplus
}
#endif

#endif /* CxxModule_hpp */

文件结构和Swift模块保持不变。
教训是:任何既要被C++读取又要被Swift读取的头文件都必须可被两者读取。(Swift可以理解C头文件,但至少在目前的状态下无法理解C++头文件。)

0

对于其他正在苦苦挣扎于C和Swift的人们的一些注意事项(对于C++来说,“只是”一个命名问题..我知道,但没有一个可用的C代码,C++就是不可能完成的任务..)

总结一下:

  1. 假设您有“C”代码,让我们通称为libC(一个文件夹),其中包含*.c和*.h文件。

  2. 您想要构建一个包装器,比如libCWrapper

  3. 在Xcode中构建一个名为libCWrapper的包

  4. Xcode应该构建一个类似于以下内容的Package.swift:(已删除注释)

    import PackageDescription

    let package = Package( name: "libCWrapper", products: [ .library( name: "libCWrapper", targets: ["libCWrapper"]), ], targets: [ .target( name: "libCWrapper", dependencies: []), .testTarget( name: "libCWrapperTests", dependencies: ["libCWrapper"]), ] )

  5. 现在第一个技巧:我们需要两个目标和两个产品..所以添加:

...

 products: [
        .library( name: "libCWrapper", targets: ["libCWrapper"]),
        .library( name: "libC", targets: ["libC"]),
    ],

..

  .target(
        name: "libCWrapper",
        dependencies: ["libC"]),
    
    .target(
            name: "libC",
            dependencies: []),

注意: Swift libCWrapper 依赖于 libC。

  1. 我们还必须添加/更改 "C sources" 的布局:

所有的 "C" 代码必须放在一个名为 "C" 目标的文件夹中,但是包含一级上层目录。所以:

Sources --->[libC] sampleCall.c [include] ---> [sampleCall.h]

这样:

enter image description here

  1. 请添加目标,如果您需要进行一些测试:

    平台:[ .MacOS(.v10_12),.iOS(.v9)],

  2. 在测试中:

@testable import libCWrapper @testable import libC

也要添加 C 产品。 这样测试就可以是:

import XCTest
@testable import libCWrapper
@testable import libC

final class libCWrapperTests: XCTestCase {
    
    func testExample() throws {
        // This is an example of a functional test case.
        // Use XCTAssert and related functions to verify your tests produce the correct
        // results.
        XCTAssertEqual(libCWrapper().text, "Hello, World!")
    }
    
    func testGimmeFive(){
        let n = gimmeFive()
        XCTAssertEqual(n, 5)

    }
}

在C语言中,我们有:

//
//  sampleCall.c.c
//  
//
//  Created by ing.conti on 24/08/22.
//

#include "sampleCall.h"


int gimmeFive(void){
    return 5;
}

现在,在测试中您可以从Swift调用C,只是一个“开放”/“公共”的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接