Assigning a #define to a raw enum value

As titled, I'm trying to simply assign

enum Input: Int {
    case Release = GLFW_RELEASE
    ....
}

where

#define GLFW_RELEASE 0

but I get

'-' or expected, got 'GLFW_RELEASE'

isn't assumed as a readonly value?

Swift doesn't have a #define statement, as far as I know. It is not like C/C++/etc., where it runs the C preprocessor before parsing the code.

2 Likes

Indeed, it's from a C header. Sorry for not specifying that

In Swift, enum values must literals (see raw-value-style-enum-case in The Swift Programming Language. When you import GLFW_RELEASE from C, the Swift version of it looks something like this:

var GLFW_RELEASE: Int32 { get }

This isn’t a literal, and thus you can’t use it as an enum value.

There’s a few things you can do here:

  • You can use the literal 0. Whether this is reasonable depends on the compatibility guarantees provided by the library you’re using. As GLFW_RELEASE is a macro, the developer of that library can’t change the value in a binary compatibility fashion. However, they may only be guaranteeing source compatibility, in which case relying on the literal 0 would be an issue.

  • You can declare the enum in C, which has access to the GLFW_RELEASE macro. Whether this makes sense depends on what other cases you have. If you have a case that can’t be represented in C — for example, it has an associated value — this approach won’t work.

  • You can avoid raw values and instead create a computed property that maps between the Swift ‘space’ and C ‘space’ values. This approach seems to make sense because the C values are Int32 not Int.

ps In Swift style, enum cases begin with a lowercase letter, meaning it should be case release not case Release.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

6 Likes

Thanks eskimo, I also thought for a moment about a computed property or similar, but I was afraid it was too much over-engineered..

Moreover, since I'd need also to map forth and back enums from Swift and C (callbacks and methods) I dropped the idea

Ps: how would you do that?

how would you do that?

Nothing magic, just boilerplate code.

I’m not familiar with the API you’re using, so here’s an example based on BSD Sockets. Within BSD Sockets there’s an sa_family_t type which is effectively an enum with values like AF_INET, AF_INET6, and so on. In Swift I might model this like so [1]:

enum AddressFamily {
    case ip(version: Int)
    case unix
}

This enum has an associated value, so there’s no way to represent it directly in C. Rather, you can make it raw representable like so:

extension AddressFamily: RawRepresentable {

    init?(rawValue: sa_family_t) {
        switch Int32(rawValue) {
        case AF_INET: self = .ip(version: 4)
        case AF_INET6: self = .ip(version: 6)
        case AF_UNIX: self = .unix
        default: return nil
        }
    }

    var rawValue: sa_family_t {
        switch self {
        case .ip(version: 4): return sa_family_t(AF_INET)
        case .ip(version: 6): return sa_family_t(AF_INET6)
        case .unix: return sa_family_t(AF_UNIX)
        default: fatalError()
        }
    }
}

Now you can map from C to Swift like this:

let afC: sa_family_t = … something …
guard let afSwift = AddressFamily(rawValue: afC) else {
    …
}

and from Swift to C like this:

let afSwift: AddressFamily = … something …
let afC = afSwift.rawValue

I like this approach because it confines the C weirdness to the boundary between the C and Swift worlds, allowing my Swift code to work solely in terms of Swift.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

[1] Actually, this is a completely bonkers way to model this — unless you plan to add support for IPv5 (-: — but just go with the example.

3 Likes

Got it, thanks

You were right, quite boilerplate code.. what about allowing enum initialization from non literals? Would it make sense or is there a specific reason to have literal-only?

what about allowing enum initialization from non literals?

I can only really comment on the language as it currently stands. However, over on Swift Evolution there has definitely been chat about supporting compile-time expressions.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like