Pitch: The CStdlib module

I would like to pitch a new module offered by Swift by default. The module would re-export the correct module that contains the POSIX or POSIX-like C standard library for the current platform, if any; it would not be imported by default, but would allow "reasonably cross-platform" code to avoid using lengthy #if canImport(…) chains to gain access to all possible stdlibs, given that they have different names on different OSs.

For example, the module may be named CStdlib. It would behave identically to a module whose entire source was:

#if canImport(Darwin)
@_exported import Darwin
#endif

#if canImport(Glibc)
@_exported import Glibc
#endif

#if canImport(CRT) // Win32
@_exported import CRT
#endif

#if canImport(WASILibc)
@_exported import WASILibc
#endif

This is not meant to overlap with Swift System; it provides no idiomatic types or harmonization between API, but it would help implementors of Swift System, Foundation and similar libraries reduce code complexity and automatically respond to platform additions as those are upstreamed.

Code that requires work on differences that are OS-specific, such as non-portable API, can still use the OS-specific module names; this does not obviate those.

24 Likes

cc @Max_Desiatov.

So to clarify, this won't re-order arguments to handle the potential collisions of those nor will it offer any implementations or shims. Would this have an issue with windows systems that expose their linux layer?

Overall this would be a great quality of life improvement to importing things.

Correct.

WSL is not like MingW/Cygwin, in the sense that POSIX API are not exposed to Windows processes — instead, a virtualized container directly executes Linux executables. Binaries built with Swift for Windows can execute outside of WSL and import the CRT module; binaries built with Swift for Linux can execute in the WSL environment and can import the specific distro's C stdlib as Glibc. While CF has some support for Cygwin, the official Swift for Windows does not build with it.

4 Likes

Nice. +1.

The only downside I can see is that it would look weird if you import CStdlib but use canImport(Darwin) to adjust your call-site for quirks of each implementation. For example, accessing the uint8_t members of an in6_addr:

import CStdlib

#if canImport(Darwin)
  let in6_addr_octets = \in6_addr.__u6_addr.__u6_addr8
#elseif canImport(Glibc)
  let in6_addr_octets = \in6_addr.__in6_u.__u6_addr8
#endif

In C, these sometimes get papered-over using macros that the clang importer doesn't support. For example, see the Darwin header:

typedef struct in6_addr {
	union {
		__uint8_t   __u6_addr8[16];
		__uint16_t  __u6_addr16[8];
		__uint32_t  __u6_addr32[4];
	} __u6_addr;                  
} in6_addr_t;

#define s6_addr   __u6_addr.__u6_addr8 /* <--- clang importer says no */

The other variants are OS-specific; so you can use #if os(Windows) and #if canImport(CRT) interchangeably. For Darwin-family OSes, it's a pain in the neck to list all of the marketing names, so folks tend to prefer canImport(Darwin).

We could perhaps overcome this with an #if os(Darwin) or #if os(Apple), or perhaps if a compile-time define could be set on each branch.

1 Like

My hope is that the usefulness of this module is there for libraries that 'know what they're doing' — that is, are writing bindings for that behavior and thus know to account for the weirdness of it, rather than as a general tool. I would regard Swift System and Foundation to be the libraries that add idiomatic types, and the ones that I would want people to import rather than CStdlib specifically for this use.

3 Likes

Initially I thought I'd stay neutral, as one argument against it is that all these libraries are sufficiently different from each other. Having an explicitly different import is a good reminder to the user that "here be dragons".

OTOH, the precedent of having different API for different platforms but the same import was already established by frameworks like SwiftUI. And I myself have written a few modules like this, for example our TokamakShim module re-exports SwiftUI on Apple platforms, and platform-appropriate renderer elsewhere. And our CombineShim conditionally re-exports Combine or OpenCombine with the same logic.

So I understand the need for it. And I agree with the point that users importing CStdlib already should know why, how, and what exactly they're doing. Overall it's a +1 from me.

4 Likes

Yes, it isn't a deal-breaker. But it means the utility is truly limited to removing long canImport chains and automatically attempting to compile on new platforms. The code will still look a bit weird as you use canImport to check module availability, without actually importing that module directly.

Still, I think it has value. I would use it, for sure.

1 Like

Would this be something built into the Swift toolchain?
Or could it be part of the Apple package collection?
Perhaps as a new module of the apple/swift-system package?

Should it exclude platform-specific submodules (such as hfs and sys.qos)?

#if canImport(Darwin)
@_exported import Darwin.C
@_exported import Darwin.POSIX
#endif

Otherwise, another module name may be more appropriate than CStdlib.

2 Likes

Since the hope is that this be used in the implementation of both Swift System and similar low-level packages, and also of packages that are already part of the Swift build like Foundation, it would be built into the distributable toolchain and built out of the Swift repository.

This is interesting; I think you have a good idea here, which would solve some of the weirdness above. I'm not sure if all modulemaps we ship correctly segregate submodules in this way, but I would be happy with this suggestion for much the same reason you propose it.

2 Likes

I've done the Darwin/Glibc dance quite a few times, and this would definitely have come in handy, so +1 to the idea from me :ok_hand:

To put a little paint on the shed, maybe going for something like SystemLibC or PlatformCLib or similar could help make it more obvious that things might be different on different platforms?

3 Likes

It'd be great to formalize and standardize this. +1!

4 Likes

I think this would be an immediate improvement to Swift when using any posix api's. Ifdeffing or maintaining this in some private repository all the time is annoying. Shipping this with Swift would also add the benefit when adding support for a new platform would not break on any existing packages that simply want posix. +1 from me

I'm all for this. +1

Terms of Service

Privacy Policy

Cookie Policy