我可以使用Swift包管理器在同一个Swift包中混合使用C++和Swift吗?

jdg4fx2g  于 12个月前  发布在  Swift
关注(0)|答案(2)|浏览(117)

我想用C写一个模块,用C写一些在Swift中可以访问的函数。
我有点困惑,因为无论我做什么,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
编译得很好。

jqjz2hbq

jqjz2hbq1#

我能够解决这个问题。答案是“是的”,只要暴露给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头文件,至少在目前的状态下是这样。

brccelvz

brccelvz2#

对于其他在C和Swift中挣扎的人来说,有几个注意事项(对于C来说,“简单地”是一个命名的问题。我确实知道,但是如果没有一个正常运行的C代码,C是不可能完成使命。
回顾一下:
1.假设您有“C”代码,让我们称之为libC(一个文件夹),它包含 *.c和 *.h文件。
1.如果要构建一个 Package 器,可以说libCWrapper
1.在Xcode中构建一个名为libCWrapper的包

  1. 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"]),
  ]
)

1.现在第一个技巧:我们需要两个目标和两个产品。因此,补充:

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源代码:
所有的“C”代码必须进入一个饲料名称为“C”目标,但包括一个级别。所以:
Sources ->[libC] sampleCall.c [include] -> [sampleCall.h]
所以:

  1. pls也添加目标,如果你需要做一些测试:
    平台:[ .macOS(.v10_12),.iOS(.v9)],
    1.内部测试:
    @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,这只是一个“open”/“public”和类似的问题。

相关问题