'Can I mix C++ and Swift in the same swift package, using the Swift Package Manager?

I would like to write one module in C++, and make some functions in C that are accessible in Swift.

I am somewhat baffled, because no matter what I do, the SPM insists on attempting to compile the C++ code as if it were Objective-C, and of course it cannot find the right headers.

Here is my attempt.

Source directory structure:

Sources
|
+-CxxModule
| |
| +-include
| | |
| | +-CxxModule.hpp
| |
| +-CxxModule.cpp
|
+-SwiftModule
  |
  +-SwiftModule.swift

The manifest Package.swift is as follows:

// 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 is as follows:

#ifndef CxxModule_hpp
#define CxxModule_hpp

#include <iostream>

extern "C" void printHello();

#endif /* CxxModule_hpp */

CxxModule.cpp is as follows:

#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;
}

Finally, SwiftModule.swift:

import CxxModule

What am I missing? Is there a way to tell SPM that the module is supposed to be in C++? Or is this simply unsupported at the moment?

Note: the C++ compiles just fine if I eliminate the Swift target.



Solution 1:[1]

I was able to resolve the problem. The answer is “Yes” so long as the header exposed to Swift is readable in pure C.

First, I moved all C++-specific code (especially headers referencing the standard library) into the source cpp file:

#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;
}

Second, I added macros to make the header readable in both C and C++:

#ifndef CxxModule_hpp
#define CxxModule_hpp

#ifdef __cplusplus
extern "C" {
#endif

void printHello();

#ifdef __cplusplus
}
#endif

#endif /* CxxModule_hpp */

The file structure and Swift module remain unchanged.

The lesson: any header that is going to be read by both C++ and Swift must be readable by both. (Swift is able to understand C headers, but not C++ headers, at least in its present state.)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 AthanasiusOfAlex