Plain C enum declarations don't translate properly into Swift

30 views
Skip to first unread message

Jens Alfke

unread,
Nov 4, 2015, 7:20:02 PM11/4/15
to Swift Language
I’ve written a cross-platform C header defining an API. My team is writing bindings to this API in a number of languages including Java, C# and now Swift. For the most part it’s going smoothly, but I’ve found that the enum types I define in the API are translating oddly. For example:

    typedef enum {
        kDeleted        = 0x01,
        kConflicted     = 0x02,
        kHasAttachments = 0x04,
        kExists         = 0x1000
    } C4DocumentFlags;

translates to (as shown in the Xcode assistant editor):

public struct C4DocumentFlags : RawRepresentable, Equatable {
    public init(_ rawValue: UInt32)
    public init(rawValue: UInt32)
    public var rawValue: UInt32
}
public struct C4DocumentFlags : RawRepresentable, Equatable {
    public init(_ rawValue: UInt32)
    public init(rawValue: UInt32)
    public var rawValue: UInt32
}

(Yes, it appears twice.) It’s inconvenient that this turns into a struct instead of a Swift enum or just an integer type. But the biggest glitch is that the actual enum values (kDeleted, etc.) don’t appear in the Swift interface at all! This means that to actually use this enum I have to either redefine the constants in Swift or use hardcoded numbers, either of which is fragile.

This seems like a bug in the C-interface-importer. I’m guessing that Apple put all their effort into fixing up their enum declarations to be Swift-friendly, and then didn’t consider how a plain C enum declaration translates…

(Again, this is a cross-platform header, so I can’t just use CF_ENUM or NS_OPTIONS to declare these enums. I could add some fancy #ifdef APPLE stuff, but I’d rather not uglify the header too much.)

—Jens

Kevin Ballard

unread,
Nov 4, 2015, 7:29:23 PM11/4/15
to swift-l...@googlegroups.com
I just tested your exact enum and you're right, Xcode shows it duplicated like that. But when I actually wrap this up in a module and import it into the Swift REPL it reveals something a little different:
 
struct C4DocumentFlags : RawRepresentable, Equatable {
    init(_ rawValue: Swift.UInt32)
    init(rawValue: Swift.UInt32)
    var rawValue: Swift.UInt32
}
var kConflicted: C4DocumentFlags {
    get {}
}
var kDeleted: C4DocumentFlags {
    get {}
}
var kExists: C4DocumentFlags {
    get {}
}
var kHasAttachments: C4DocumentFlags {
    get {}
}
 
So this looks like a bug in Xcode.
 
-Kevin Ballard
 
On Wed, Nov 4, 2015, at 04:19 PM, Jens Alfke wrote:
I’ve written a cross-platform C header defining an API. My team is writing bindings to this API in a number of languages including Java, C# and now Swift. For the most part it’s going smoothly, but I’ve found that the enum types I define in the API are translating oddly. For example:
 
typedefenum {
        kDeleted        = 0x01,
        kConflicted     = 0x02,
        kHasAttachments = 0x04,
        kExists         = 0x1000
    } C4DocumentFlags;
 
translates to (as shown in the Xcode assistant editor):
 
publicstruct C4DocumentFlags : RawRepresentable, Equatable {
public init(_ rawValue: UInt32)
public init(rawValue: UInt32)
public var rawValue: UInt32
}
publicstruct C4DocumentFlags : RawRepresentable, Equatable {
public init(_ rawValue: UInt32)
public init(rawValue: UInt32)
public var rawValue: UInt32
}
 
(Yes, it appears twice.) It’s inconvenient that this turns into a struct instead of a Swift enum or just an integer type. But the biggest glitch is that the actual enum values (kDeleted, etc.) don’t appear in the Swift interface at all! This means that to actually use this enum I have to either redefine the constants in Swift or use hardcoded numbers, either of which is fragile.
 
This seems like a bug in the C-interface-importer. I’m guessing that Apple put all their effort into fixing up their enum declarations to be Swift-friendly, and then didn’t consider how a plain C enum declaration translates…
 
(Again, this is a cross-platform header, so I can’t just use CF_ENUM or NS_OPTIONS to declare these enums. I could add some fancy #ifdef APPLE stuff, but I’d rather not uglify the header too much.)
 
—Jens


--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to swift-languag...@googlegroups.com.
To post to this group, send email to swift-l...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
 

Ken Ferry

unread,
Nov 4, 2015, 7:45:52 PM11/4/15
to Kevin Ballard, swift-l...@googlegroups.com
Note that neither of these are what you want, though. It looks like your type is a bit mask. For that you’ll want a struct conforming to OptionSetType in Swift. 

For example:

public struct NSStringCompareOptions : OptionSetType {
    public init(rawValue: UInt)

    

    public static var CaseInsensitiveSearch: NSStringCompareOptions { get }
    public static var LiteralSearch: NSStringCompareOptions { get } /* Exact character-by-character equivalence */
    public static var BackwardsSearch: NSStringCompareOptions { get } /* Search from end of source string */
    public static var AnchoredSearch: NSStringCompareOptions { get } /* Search is limited to start (or end, if NSBackwardsSearch) of source string */
    public static var NumericSearch: NSStringCompareOptions { get } /* Added in 10.2; Numbers within strings are compared using numeric value, that is, Foo2.txt < Foo7.txt < Foo25.txt; only applies to compare methods, not find */
    @available(OSX 10.5, *)
    public static var DiacriticInsensitiveSearch: NSStringCompareOptions { get } /* If specified, ignores diacritics (o-umlaut == o) */
    @available(OSX 10.5, *)
    public static var WidthInsensitiveSearch: NSStringCompareOptions { get } /* If specified, ignores width differences ('a' == UFF41) */
    @available(OSX 10.5, *)
    public static var ForcedOrderingSearch: NSStringCompareOptions { get } /* If specified, comparisons are forced to return either NSOrderedAscending or NSOrderedDescending if the strings are equivalent but not strictly equal, for stability when sorting (e.g. "aaa" > "AAA" with NSCaseInsensitiveSearch specified) */
    @available(OSX 10.7, *)
    public static var RegularExpressionSearch: NSStringCompareOptions { get } /* Applies to rangeOfString:..., stringByReplacingOccurrencesOfString:..., and replaceOccurrencesOfString:... methods only; the search string is treated as an ICU-compatible regular expression; if set, no other options can apply except NSCaseInsensitiveSearch and NSAnchoredSearch */
}

I think you’ll need a conditional definition of CF_OPTIONS. You don’t have to conditionalize it on __APPLE__ if you don’t want, just on whether CF_OPTIONS is already defined. 

-ken

Reply all
Reply to author
Forward
0 new messages