Is there a way to declare a "extern" function implemented in a C/C++ library (C calling convention), link a .lib to the swift project and call the function in swift?

I've seen docs on C interop, some talking about bridging headers and module maps.

It would seem I could use a module map to make the functions and structs in C available to Swift. I tried to use this and, but my sample just didn't run at all (no output either).

An example of what I want to achieve here:

Is there a way to declare an external function like this in Swift and just link to a .lib where it is implemented?

@_extern(c)

The Swift 6 compiler understands the unofficial @_extern(c) attribute. Documentation:

@_extern(c, [, <"cName">])

Indicates that a particular declaration should refer to a C declaration with the given name. If the optional "cName" string is not specified, the Swift function name is used without Swift name mangling. Platform-specific mangling rules (leading underscore on Darwin) are still applied.

For example, this compiles and runs (on macOS) without having to import the C stdlib:

// App.swift
@_extern(c, "sin")
func sin(_ x: Double) -> Double

@main
struct App {
    static func main() {
        let result = sin(Double.pi / 2)
        print(result)
    }
}
$ swift --version
swift-driver version: 1.111.2 Apple Swift version 6.0 (swiftlang-6.0.0.5.15 clang-1600.0.22.6)
Target: arm64-apple-macosx14.0

# No linker flag necessary because the C stdlib is linked automatically
$ swiftc -parse-as-library App.swift && ./App
1.0

Bridging header

If you don't want to set up a C module in SwiftPM with a module map and everything, you can also write a plain bridging header in C and pass it directly to the compiler:

// header.c
extern double sin(double);
// App.swift
@main
struct App {
    static func main() {
        let result = sin(Double.pi / 2)
        print(result)
    }
}
$ swiftc -parse-as-library -import-objc-header header.h App.swift && ./App
1.0

Despite the flag name -import-objc-header, this also works for plain C headers.

Does this help?

1 Like

Here's a larger example that actually links an external library (libcmark for Markdown parsing):

// header.c
#include <stddef.h>

// From the C stdlib
extern void free(void *ptr);

// From libcmark
extern char * cmark_markdown_to_html(const char *text, size_t len, int options);
// App.swift
@main
struct App {
    static func main() {
        let markdown = """
            # Heading

            Lorem ipsum dolor sit amet.
            """
        let html = cmark_markdown_to_html(markdown, markdown.utf8.count, 0)
        print(String(cString: html!))
        free(html)
    }
}

Usage, including linking the C library:

$ swiftc -parse-as-library \
    -import-objc-header header.h \
    -L/opt/homebrew/Cellar/cmark/0.31.0/lib \
    -lcmark \
    App.swift
$ ./App
<h1>Heading</h1>
<p>Lorem ipsum dolor sit amet.</p>
1 Like

Thank you. This looks like it will help, especially @_extern (looks like what I want). I will try these later to see what works best in the end.

I would not recommend relying on "unofficial" features. The only supported way to interface with C functions is to import a header.

7 Likes

I'm willing to accept the risk of this being removed/changed.

@ole Is there a swift attribute that can tell the swift compiler to have a struct in swift match the memory layout of a C struct with the same members?

Also note that this doesn't work in the general case. Consider the following for x86 Win32:

void __stdcall add(int i, int j);

Using the @_extern(c) func add(_: CInt, _: CInt) -> Void would give you the wrong calling convention I believe.

The interop story for Swift involves a module definition for interfacing with the foreign language.

4 Likes

No. The only way to get a struct with a guaranteed C-compatible memory layout is to define it in a C header.

2 Likes