Can we still not mix Swift and C in a module?

Unfortunately, the libraries I want to use (libtiff and libgeotiff) have a lot of variadic function calls like this:

extern int TIFFGetField(TIFF *tif, uint32_t tag, ...);

As I understand it, this can't be imported into Swift, and I need to write a C wrapper for it. So in my systemLibrary module I tried adding a shim.c file, with a wrapper and corresponding declaration in the umbrella header, but it seems to be ignored, and the link stage fails to find the symbol.

So, how can I write these C wrappers? It seems there’s still no direct support for it.

My attempt is here.

You can do what we do regularly in the Swift on Server world: create a separate C target for shims. See swift-nio-ssl's CNIOBoringSSLShims, swift-crypto's CCryptoBoringSSLShims, and I'm sure several others. I can explain how these work if you have trouble.

1 Like

You can also sometimes short-circuit the process by putting static inline functions in the header you’re using for the systemLibrary, but then you are sort of mixing your code with the library’s actual contents. Which might be fine for an executable, but gets more questionable for a public library.

(I’m not sure why the separate C target is so bad though, if you need a proper .c file. It’s not like there’ll be layering issues.)

1 Like

I kinda see the argument for keeping it a separate module, but it does bring up something. Many of the shims I’m writing are tiny, like this:

uint32_t GTIFGetWidth(const TIFF* inTIFF) {
	uint32_t width = 0;
	TIFFGetField(const_cast<TIFF*>(inTIFF), TIFFTAG_IMAGEWIDTH, &width);
	return width;
}

Will the build system inline this, in the end? I tried sticking this code into the header file, but it complained about the C++ isms. Is there a way to tell it to compile the header as C++?

I don’t need to do that if it’ll eventually get inlined.

I think your only options for inlining here would be to either compile the Swift module with C++ interop enabled (which is still somewhat unstable) so that you can put the definition in the header and ClangImporter can see it, or to leave it in the source file and rely on LTO (which I don't think is very well supported yet either) for the linker to optimize it.

2 Likes