I'm still struggling to use SDL from Swift. I'm using a Swift library I found, which was working for me a couple weeks ago, but now I'm trying to add text support to it and I can't build any of it.
SDL_PixelFormatEnum I believe is an imported-from-C enum; at least SDL_PIXELFORMAT_INDEX1LSB is imported from the C headers. I thought those always came in as Int, so I'm not sure what's going on here.
/Users/rmann/Projects/Personal/InfoDisplay/InfoDisplay/Packages/SDL/Sources/SDL/PixelFormat.swift:83:60: error: initializer 'init(_:)' requires that 'SDL_PixelFormatEnum' conform to 'BinaryInteger'
static let index1LSB = SDLPixelFormat.Format(rawValue: UInt32(SDL_PIXELFORMAT_INDEX1LSB))
^
Swift.UnsignedInteger:2:23: note: where 'T' = 'SDL_PixelFormatEnum'
@inlinable public init<T>(_ source: T) where T : BinaryInteger
^
SDLPixelFormat.Format looks like:
public extension SDLPixelFormat {
/// SDL Pixel Format Enum
struct Format: RawRepresentable, Equatable, Hashable {
public let rawValue: UInt32
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
}
}
and in an extension:
public extension SDLPixelFormat.Format {
/// SDL_PIXELFORMAT_INDEX1LSB
static let index1LSB = SDLPixelFormat.Format(rawValue: UInt32(SDL_PIXELFORMAT_INDEX1LSB))
What’s happened here is that the Swift importer has decided to import SDL_PixelFormatEnum as a struct. Thus, in Swift, SDL_PIXELFORMAT_INDEX1LSB is not an enum case but a struct constant. Thus, this line:
static let index1LSB = SDLPixelFormat.Format(rawValue: UInt32(SDL_PIXELFORMAT_INDEX1LSB))
is trying to initialise a UInt32 from an SDL_PixelFormatEnum. To make this work, do this:
static let index1LSB = SDLPixelFormat.Format(rawValue: UInt32(SDL_PIXELFORMAT_INDEX1LSB.rawValue))
which you can simplify to this:
static let index1LSB = SDLPixelFormat.Format(rawValue: SDL_PIXELFORMAT_INDEX1LSB.rawValue)
I’m not sure what the exact rules the importer uses to decide whether to create an enum or a struct, but I can see what it chose to do the latter in this case. The SDL_PixelFormatEnum is a complex beast, full of macros and potentially overlapping cases.
Thanks Quinn. I'm trying to understand why it worked a couple weeks ago. Is there a chance this import behavior changed from Swift 5.0 to 5.1?
I seem to have some trouble keeping Xcode and SPM in sync, in the sense that Xcode won't always rebuild all the dependencies, but if I rebuild them with SPM (or attempt to, because it used Swift 5.1 and would fail), now Xcode fails when it tries to build my app. It's very confusing.
Is there a chance this import behavior changed from Swift 5.0 to 5.1?
I dug out my test project out of the trash and opened it in Xcode 11 (hence Swift 5). It behaves the same way, that is, SDL_PixelFormatEnum is imported as a struct, not an enum. *shrug*
Hmmm, that’s not what I’m seeing. I created a new command-line tool test project, added an Objective-C class to give me a bridging header, and then added this to that bridging header:
What I saw was that SDL 2.0.9 (installed by default on Raspbian Buster) has an anonymous enum. SDL 2.0.10 (what I had installed on macOS) has the typedefed and named enum.
The one sure-fire way to get an enum to come through as an enum is to use NS_ENUM, so I dug into what that actually does. If you have a declaration like this:
It seems that the enum_extensibility attribute is key here. And based on that I took another look at SE-0192 Handling Future Enum Cases, and it turns out that this discusses this pretty extensively. Specifically, in the C enums section it says:
This section only applies to enums that Swift considers "true enums",
rather than option sets or funny integer values … the presence of enum_extensibility(closed) or enum_extensibility(open) will
instruct Swift to treat the enum as a "true enum".
Thanks for the deep dive. I don't mind if it's a struct or an enum, I just want it to stay consistent. Unfortunately, I can't use NS_ENUM, it's a header provided by a third-party. I'd love to add apinotes, but that's hard enough to do, and I don't know if it provides a facility for specifying enum/struct. The SDL header doesn't have anything special in the way it declares its enum, just enum { … }; in 2.0.9, and typedef enum { … } Name; in 2.0.10.
In any case this will all be moot if I can't get SPM to issue the right linker flags.