jayrulez
(Robert Campbell)
1
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?
ole
(Ole Begemann)
2
@_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
ole
(Ole Begemann)
3
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
jayrulez
(Robert Campbell)
4
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.
Joe_Groff
(Joe Groff)
5
I would not recommend relying on "unofficial" features. The only supported way to interface with C functions is to import a header.
7 Likes
jayrulez
(Robert Campbell)
6
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?
compnerd
(Saleem Abdulrasool)
7
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
ole
(Ole Begemann)
8
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