Swift C enum Case Mapping


(Ryan Lovelett) #1

I've successfully(?) imported C enum that is defined as:

typedef enum fe_type {
  FE_QPSK,
  FE_QAM,
  FE_OFDM,
  FE_ATSC
} fe_type_t;

I wanted this enum to conform to `CustomStringConvertible` so I added
the following extension:

extension fe_type: CustomStringConvertible {
  var description: String {
    switch self {
    case .FE_QPSK: return "QPSK"
    case .FE_QAM: return "QAM"
    case .FE_OFDM: return "OFDM"
    case .FE_ATSC: return "ATSC"
    }
  }
}

Unfortunately this does not compile successfully. The compiler states
that "enum case 'FE_QPSK' not found in type 'fe_type'" (and repeats for
all the cases). Does that mean that fe_type is _NOT_ an enum in Swift?
If it is how can I determine what Swift imported the case names as?


(Austin Zheng) #2

fe_type is being imported as a struct (alt-click 'fe_type' in Swift). I think if you want it to be imported as an enum you need to use the NS_ENUM macro in the definition, which might not be possible in your case.

Austin

···

On Jan 10, 2016, at 1:06 PM, Ryan Lovelett via swift-dev <swift-dev@swift.org> wrote:

typedef enum fe_type {
FE_QPSK,
FE_QAM,
FE_OFDM,
FE_ATSC
} fe_type_t;


(Ryan Lovelett) #3

Austin,

I guess I should say that the `typedef` is coming from a Linux kernel
header[1]. So I don't think I'm going to be able to add any macros to
the definition.

What do you mean about alt-click? Alt click where?

···

On Sun, Jan 10, 2016, at 04:12 PM, Austin Zheng wrote:

fe_type is being imported as a struct (alt-click 'fe_type' in
Swift). I think if you want it to be imported as an enum you need to
use the NS_ENUM macro in the definition, which might not be possible
in your case.

Austin

On Jan 10, 2016, at 1:06 PM, Ryan Lovelett via swift-dev <swift- >> dev@swift.org> wrote:

typedef enum fe_type { FE_QPSK, FE_QAM, FE_OFDM, FE_ATSC } fe_type_t;

Links:

  1. http://lxr.free-electrons.com/source/include/linux/dvb/frontend.h?v=3.2


(Austin Zheng) #4

Hi Ryan,

Apologies, I should have been more clear. In Xcode you can alt(?)-click on a type (e.g the 'MyType' in "let a : MyType = 123") in the IDE to pop up a little window that shows you the definition, including the type and some other information. If you're on a Linux box or not using an IDE you probably don't have that option.

The only methods I see exposed on the Swift imported type are initializers taking a integer raw value, and a 'rawValue' property for getting back out the raw value. Hope that helps.

Austin

···

On Jan 10, 2016, at 1:18 PM, Ryan Lovelett <swift-dev@ryan.lovelett.me> wrote:

Austin,

I guess I should say that the `typedef` is coming from a Linux kernel header <http://lxr.free-electrons.com/source/include/linux/dvb/frontend.h?v=3.2>. So I don't think I'm going to be able to add any macros to the definition.

What do you mean about alt-click? Alt click where?

On Sun, Jan 10, 2016, at 04:12 PM, Austin Zheng wrote:

fe_type is being imported as a struct (alt-click 'fe_type' in Swift). I think if you want it to be imported as an enum you need to use the NS_ENUM macro in the definition, which might not be possible in your case.

Austin

On Jan 10, 2016, at 1:06 PM, Ryan Lovelett via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

typedef enum fe_type {
FE_QPSK,
FE_QAM,
FE_OFDM,
FE_ATSC
} fe_type_t;


(Austin Zheng) #5

I spoke too soon, the cases are also defined as values of that type. So, a working version of your code:

extension fe_type : CustomStringConvertible {
  public var description: String {
    switch self {
    case FE_QPSK: return "QPSK"
    case FE_QAM: return "QAM"
    case FE_OFDM: return "OFDM"
    case FE_ATSC: return "ATSC"
    default: fatalError("can't be exhaustive")
    }
  }
}

Best,
Austin

···

On Jan 10, 2016, at 1:22 PM, Austin Zheng <austinzheng@gmail.com> wrote:

Hi Ryan,

Apologies, I should have been more clear. In Xcode you can alt(?)-click on a type (e.g the 'MyType' in "let a : MyType = 123") in the IDE to pop up a little window that shows you the definition, including the type and some other information. If you're on a Linux box or not using an IDE you probably don't have that option.

The only methods I see exposed on the Swift imported type are initializers taking a integer raw value, and a 'rawValue' property for getting back out the raw value. Hope that helps.

Austin

On Jan 10, 2016, at 1:18 PM, Ryan Lovelett <swift-dev@ryan.lovelett.me <mailto:swift-dev@ryan.lovelett.me>> wrote:

Austin,

I guess I should say that the `typedef` is coming from a Linux kernel header <http://lxr.free-electrons.com/source/include/linux/dvb/frontend.h?v=3.2>. So I don't think I'm going to be able to add any macros to the definition.

What do you mean about alt-click? Alt click where?

On Sun, Jan 10, 2016, at 04:12 PM, Austin Zheng wrote:

fe_type is being imported as a struct (alt-click 'fe_type' in Swift). I think if you want it to be imported as an enum you need to use the NS_ENUM macro in the definition, which might not be possible in your case.

Austin

On Jan 10, 2016, at 1:06 PM, Ryan Lovelett via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

typedef enum fe_type {
FE_QPSK,
FE_QAM,
FE_OFDM,
FE_ATSC
} fe_type_t;


(Jordan Rose) #6

Right. This is because Swift can't tell if your enum is actually an option set, a true, non-overlapping enum, or just a set of related constants, so it picks the lowest common denominator. We currently don't have a great way to override that in headers you don't control.

(Heck, on non-Apple platforms we don't have a great way to do it in headers you do control; Swift is currently keying off the macro names.)

Jordan

···

On Jan 10, 2016, at 13:25 , Austin Zheng via swift-dev <swift-dev@swift.org> wrote:

I spoke too soon, the cases are also defined as values of that type. So, a working version of your code:

extension fe_type : CustomStringConvertible {
  public var description: String {
    switch self {
    case FE_QPSK: return "QPSK"
    case FE_QAM: return "QAM"
    case FE_OFDM: return "OFDM"
    case FE_ATSC: return "ATSC"
    default: fatalError("can't be exhaustive")
    }
  }
}

Best,
Austin

On Jan 10, 2016, at 1:22 PM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

Hi Ryan,

Apologies, I should have been more clear. In Xcode you can alt(?)-click on a type (e.g the 'MyType' in "let a : MyType = 123") in the IDE to pop up a little window that shows you the definition, including the type and some other information. If you're on a Linux box or not using an IDE you probably don't have that option.

The only methods I see exposed on the Swift imported type are initializers taking a integer raw value, and a 'rawValue' property for getting back out the raw value. Hope that helps.

Austin

On Jan 10, 2016, at 1:18 PM, Ryan Lovelett <swift-dev@ryan.lovelett.me <mailto:swift-dev@ryan.lovelett.me>> wrote:

Austin,

I guess I should say that the `typedef` is coming from a Linux kernel header <http://lxr.free-electrons.com/source/include/linux/dvb/frontend.h?v=3.2>. So I don't think I'm going to be able to add any macros to the definition.

What do you mean about alt-click? Alt click where?

On Sun, Jan 10, 2016, at 04:12 PM, Austin Zheng wrote:

fe_type is being imported as a struct (alt-click 'fe_type' in Swift). I think if you want it to be imported as an enum you need to use the NS_ENUM macro in the definition, which might not be possible in your case.

Austin

On Jan 10, 2016, at 1:06 PM, Ryan Lovelett via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

typedef enum fe_type {
FE_QPSK,
FE_QAM,
FE_OFDM,
FE_ATSC
} fe_type_t;

_______________________________________________
swift-dev mailing list
swift-dev@swift.org <mailto:swift-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-dev


(Ryan Lovelett) #7

Jordan,

Perhaps I'm not following that parenthetical comment. Are you saying
that even if I could add NS_ENUM to the header it still wouldn't compile
into a true Swift enum (on a non-Apple platform)?

Swift's pattern matching capability is probably a top 3 reason why I am
trying to port my C application to use Swift rather than just straight
C. Not being able to get this nicety out-of-the-box is just part of
doing business on a bleeding edge programming language.

However this *seems* like something that should be able to be achieved.
Looking at the fe_type enum definition, because it had no bit pattern
associated with it, I would have assumed it was a true non-overlapping
enum. Its probable I'm not seeing the whole landscape here so would you
be able to illuminate why isn't this the default?

···

On Mon, Jan 11, 2016, at 01:11 PM, Jordan Rose wrote:

Right. This is because Swift can't tell if your enum is actually an
option set, a true, non-overlapping enum, or just a set of related
constants, so it picks the lowest common denominator. We currently
don't have a great way to override that in headers you don't control.

(Heck, on non-Apple platforms we don't have a great way to do it
in headers you do control; Swift is currently keying off the
macro names.)

Jordan

On Jan 10, 2016, at 13:25 , Austin Zheng via swift-dev <swift- >> dev@swift.org> wrote:

I spoke too soon, the cases are also defined as values of that type.
So, a working version of your code:

extensionfe_type:CustomStringConvertible{
publicvardescription:String{ switchself{ caseFE_QPSK:return"QPSK"
caseFE_QAM:return"QAM" caseFE_OFDM:return"OFDM"
caseFE_ATSC:return"ATSC" default:fatalError("can't be
exhaustive") } } }

Best, Austin

On Jan 10, 2016, at 1:22 PM, Austin Zheng <austinzheng@gmail.com> >>> wrote:

Hi Ryan,

Apologies, I should have been more clear. In Xcode you can alt(?)-
click on a type (e.g the 'MyType' in "let a : MyType = 123") in the
IDE to pop up a little window that shows you the definition,
including the type and some other information. If you're on a Linux
box or not using an IDE you probably don't have that option.

The only methods I see exposed on the Swift imported type are
initializers taking a integer raw value, and a 'rawValue' property
for getting back out the raw value. Hope that helps.

Austin

On Jan 10, 2016, at 1:18 PM, Ryan Lovelett <swift- >>>> dev@ryan.lovelett.me> wrote:

Austin,

I guess I should say that the `typedef` is coming from aLinux
kernel header[1]. So I don't think I'm going to be able to add any
macros to the definition.

What do you mean about alt-click? Alt click where?

On Sun, Jan 10, 2016, at 04:12 PM, Austin Zheng wrote:

fe_type is being imported as a struct (alt-click 'fe_type' in
Swift). I think if you want it to be imported as an enum you need
to use the NS_ENUM macro in the definition, which might not be
possible in your case.

Austin

On Jan 10, 2016, at 1:06 PM, Ryan Lovelett via swift-dev <swift- >>>>>> dev@swift.org> wrote:

typedef enum fe_type { FE_QPSK, FE_QAM, FE_OFDM, FE_ATSC }
fe_type_t;

_______________________________________________
swift-dev mailing list swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

Links:

  1. http://lxr.free-electrons.com/source/include/linux/dvb/frontend.h?v=3.2


(Jordan Rose) #8

Oh, you can certainly define a macro named NS_ENUM yourself, but that doesn't really seem like the right thing to do on Linux.

As for why we can't "guess" that this is a true enum, consider the following:

enum Foo {
  A = 1,
  B = 2,
};

Is this a very small option set, or an enum that deliberately doesn't start at 0? Or just a way to define constants for some other type, rather than using "static const"? If the compiler guesses here, (a) it might guess wrong, making the type hard to use now, and (b) if the headers are updated in a newer version of the library, it might change its guess, which would break source compatibility. (This can happen anyway, e.g. the first time an annotation is added, but at least that's supposed to be changing in the right direction, and is unlikely to change again.)

Hope that clarifies the motivation here, even if it's less than satisfactory.

Jordan

···

On Jan 11, 2016, at 11:22, Ryan Lovelett <swift-dev@ryan.lovelett.me> wrote:

Jordan,

Perhaps I'm not following that parenthetical comment. Are you saying that even if I could add NS_ENUM to the header it still wouldn't compile into a true Swift enum (on a non-Apple platform)?

Swift's pattern matching capability is probably a top 3 reason why I am trying to port my C application to use Swift rather than just straight C. Not being able to get this nicety out-of-the-box is just part of doing business on a bleeding edge programming language.

However this seems like something that should be able to be achieved. Looking at the fe_type enum definition, because it had no bit pattern associated with it, I would have assumed it was a true non-overlapping enum. Its probable I'm not seeing the whole landscape here so would you be able to illuminate why isn't this the default?

On Mon, Jan 11, 2016, at 01:11 PM, Jordan Rose wrote:

Right. This is because Swift can't tell if your enum is actually an option set, a true, non-overlapping enum, or just a set of related constants, so it picks the lowest common denominator. We currently don't have a great way to override that in headers you don't control.

(Heck, on non-Apple platforms we don't have a great way to do it in headers you do control; Swift is currently keying off the macro names.)

Jordan

On Jan 10, 2016, at 13:25 , Austin Zheng via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

I spoke too soon, the cases are also defined as values of that type. So, a working version of your code:

extensionfe_type:CustomStringConvertible{
publicvardescription:String{
switchself{
caseFE_QPSK:return"QPSK"
caseFE_QAM:return"QAM"
caseFE_OFDM:return"OFDM"
caseFE_ATSC:return"ATSC"
default:fatalError("can't be exhaustive")
    }
  }
}

Best,
Austin

On Jan 10, 2016, at 1:22 PM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

Hi Ryan,

Apologies, I should have been more clear. In Xcode you can alt(?)-click on a type (e.g the 'MyType' in "let a : MyType = 123") in the IDE to pop up a little window that shows you the definition, including the type and some other information. If you're on a Linux box or not using an IDE you probably don't have that option.

The only methods I see exposed on the Swift imported type are initializers taking a integer raw value, and a 'rawValue' property for getting back out the raw value. Hope that helps.

Austin

On Jan 10, 2016, at 1:18 PM, Ryan Lovelett <swift-dev@ryan.lovelett.me <mailto:swift-dev@ryan.lovelett.me>> wrote:

Austin,

I guess I should say that the `typedef` is coming from aLinux kernel header <http://lxr.free-electrons.com/source/include/linux/dvb/frontend.h?v=3.2>. So I don't think I'm going to be able to add any macros to the definition.

What do you mean about alt-click? Alt click where?

On Sun, Jan 10, 2016, at 04:12 PM, Austin Zheng wrote:

fe_type is being imported as a struct (alt-click 'fe_type' in Swift). I think if you want it to be imported as an enum you need to use the NS_ENUM macro in the definition, which might not be possible in your case.

Austin

On Jan 10, 2016, at 1:06 PM, Ryan Lovelett via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

typedef enum fe_type {
FE_QPSK,
FE_QAM,
FE_OFDM,
FE_ATSC
} fe_type_t;

_______________________________________________
swift-dev mailing list
swift-dev@swift.org <mailto:swift-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-dev


(Ryan Lovelett) #9

I think I get where you are going. But I'm not going to drop it quite
that easily. Forgive my insolence but I still think there is room for a
sane default here.

To answer your question: it is an enum that deliberately does not start
at 0. If Swift made that the default import symantics then I think all
the cases you covered (selfishly even my case) would be adequately
addressable.

Lets say there are, at least, 3 types of enums:
1. A non-overlapping, non-option set enum
2. A non-overlapping option set
3. An overlapping option set

In this case, I'd say the enum was of type 2; a non-overlapping option
set. If it was something the developer needed to conform to the
`OptionSetType` then the developer who imported the type could provide a
simple extension to the enum that provides conformance with the
`OptionSetType`. Voila. Now it is an option set.

More concretely:

enum Foo: Int { case A = 1 case B = 2 }

// Not automatic but available in this hypothetical world extension
Foo: OptionSetType { init(rawValue: Int) { self =
Foo.init(rawValue: rawValue) ?? Foo.A } }

If it was really some sort of constant then now it can be accessed via
it's `rawValue` property (isn't that the way it would be accessed as it
currently stands?).

If it was just an enum, well then that's covered too.

I tried compiling a few contrived enums based on this one to illustrate
how I'd expect them to be mapped. I've put them into this gist[1].

I think if you took it in terms of pattern matching where the the first
case is the strictest pattern (and thus the hardest to conform to) by
the time you reach the 3rd case your back to the current behavior. Swift
would then be able to account for more generic enum imports than it
currently can.

···

On Mon, Jan 11, 2016, at 02:51 PM, Jordan Rose wrote:

Oh, you can certainly define a macro named NS_ENUM yourself, but that
doesn't really seem like the right thing to do on Linux.

As for why we can't "guess" that this is a true enum, consider the
following:

enum Foo { A = 1, B = 2, };

Is this a very small option set, or an enum that deliberately doesn't
start at 0? Or just a way to define constants for some other type,
rather than using "static const"? If the compiler guesses here, (a) it
might guess wrong, making the type hard to use *now,* and (b) if the
headers are updated in a newer version of the library, it might
*change* its guess, which would break source compatibility. (This can
happen anyway, e.g. the first time an annotation is added, but at
least that's supposed to be changing in the right direction, and is
unlikely to change again.)

Hope that clarifies the motivation here, even if it's less than
satisfactory.

Jordan

On Jan 11, 2016, at 11:22, Ryan Lovelett <swift- >> dev@ryan.lovelett.me> wrote:

Jordan,

Perhaps I'm not following that parenthetical comment. Are you saying
that even if I could add NS_ENUM to the header it still wouldn't
compile into a true Swift enum (on a non-Apple platform)?

Swift's pattern matching capability is probably a top 3 reason why I
am trying to port my C application to use Swift rather than just
straight C. Not being able to get this nicety out-of-the-box is just
part of doing business on a bleeding edge programming language.

However this *seems* like something that should be able to be
achieved. Looking at the fe_type enum definition, because it had no
bit pattern associated with it, I would have assumed it was a true
non-overlapping enum. Its probable I'm not seeing the whole landscape
here so would you be able to illuminate why isn't this the default?

On Mon, Jan 11, 2016, at 01:11 PM, Jordan Rose wrote:

Right. This is because Swift can't tell if your enum is actually an
option set, a true, non-overlapping enum, or just a set of related
constants, so it picks the lowest common denominator. We currently
don't have a great way to override that in headers you don't
control.

(Heck, on non-Apple platforms we don't have a great way to do it in
headers you do control; Swift is currently keying off the macro
names.)

Jordan

On Jan 10, 2016, at 13:25 , Austin Zheng via swift-dev <swift- >>>> dev@swift.org> wrote:

I spoke too soon, the cases are also defined as values of that
type. So, a working version of your code:

extensionfe_type:CustomStringConvertible{
publicvardescription:String{ switchself{ caseFE_QPSK:return"QPSK"
caseFE_QAM:return"QAM" caseFE_OFDM:return"OFDM"
caseFE_ATSC:return"ATSC" default:fatalError("can't be exhaustive")
} } }

Best, Austin

On Jan 10, 2016, at 1:22 PM, Austin Zheng <austinzheng@gmail.com> >>>>> wrote:

Hi Ryan,

Apologies, I should have been more clear. In Xcode you can alt(?)-
click on a type (e.g the 'MyType' in "let a : MyType = 123") in
the IDE to pop up a little window that shows you the definition,
including the type and some other information. If you're on a
Linux box or not using an IDE you probably don't have that option.

The only methods I see exposed on the Swift imported type are
initializers taking a integer raw value, and a 'rawValue' property
for getting back out the raw value. Hope that helps.

Austin

On Jan 10, 2016, at 1:18 PM, Ryan Lovelett <swift- >>>>>> dev@ryan.lovelett.me> wrote:

Austin,

I guess I should say that the `typedef` is coming from aLinux
kernel header[2]. So I don't think I'm going to be able to add
any macros to the definition.

What do you mean about alt-click? Alt click where?

On Sun, Jan 10, 2016, at 04:12 PM, Austin Zheng wrote:

fe_type is being imported as a struct (alt-click 'fe_type' in
Swift). I think if you want it to be imported as an enum you
need to use the NS_ENUM macro in the definition, which might not
be possible in your case.

Austin

On Jan 10, 2016, at 1:06 PM, Ryan Lovelett via swift-dev <swift- >>>>>>>> dev@swift.org> wrote:

typedef enum fe_type { FE_QPSK, FE_QAM, FE_OFDM, FE_ATSC }
fe_type_t;

_______________________________________________
swift-dev mailing list swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

Links:

  1. https://gist.github.com/anonymous/232d62d77999f5eb27cd
  2. http://lxr.free-electrons.com/source/include/linux/dvb/frontend.h?v=3.2


(Jordan Rose) #10

Not insolence at all. :slight_smile: It's important to justify or come up with good logic here, and doubly so on Linux where NS_ENUM/NS_OPTIONS aren't standard.

The usefulness of an enum is that you can guarantee that all cases are enumerated. But even that's not always true; people do add new elements to C enums without breaking binary compatibility. Or they have "private" cases that are ABI-compatible but do not correspond to a public declaration. So we'll already probably be dropping the idea of exhaustive switch over an imported enum unless we have some prior knowledge. (That still needs design.)

So then what's the cost of using an enum over a struct for the option set? I guess it's mostly the implication that you could switch over it, something you should never do with an option set. There are also option sets with predefined combinations of options:

typedef NS_OPTIONS(NSUInteger, MyOptions) {
  MyOptionA = 1,
  MyOptionB = 2,
  MyOptionC = 4,
  MyOptionDefault = MyOptionA | MyOptionC,
};

…which would be weird for an enum. As you say, though, it's not a deal-breaker.

For your gist <https://gist.github.com/anonymous/232d62d77999f5eb27cd> I'm not really sure what separates type 1 from type 2. Is it the explicit values assigned to the cases? The fact that all of the "type 2" values are powers of 2? And what makes type 3 a set of constants rather than just an enum or option set with a compatibility alias for its first element?

I don't think we want to make these heuristics much more complicated—the more rules there are, the more likely people will be confused when they don't work. One advantage of the current heuristic is that it encourages people to make their headers better; a disadvantage is that not all headers are editable. Maybe we can fix that problem instead, for example by looking for annotations on redeclarations of the enum.

Jordan

···

On Jan 11, 2016, at 13:25 , Ryan Lovelett <swift-dev@ryan.lovelett.me <mailto:swift-dev@ryan.lovelett.me>> wrote:

I think I get where you are going. But I'm not going to drop it quite that easily. Forgive my insolence but I still think there is room for a sane default here.

To answer your question: it is an enum that deliberately does not start at 0. If Swift made that the default import symantics then I think all the cases you covered (selfishly even my case) would be adequately addressable.

Lets say there are, at least, 3 types of enums:
A non-overlapping, non-option set enum
A non-overlapping option set
An overlapping option set

In this case, I'd say the enum was of type 2; a non-overlapping option set. If it was something the developer needed to conform to the `OptionSetType` then the developer who imported the type could provide a simple extension to the enum that provides conformance with the `OptionSetType`. Voila. Now it is an option set.

More concretely:

    enum Foo: Int {
      case A = 1
      case B = 2
    }

    // Not automatic but available in this hypothetical world
    extension Foo: OptionSetType {
      init(rawValue: Int) {
        self = Foo.init(rawValue: rawValue) ?? Foo.A
      }
    }

If it was really some sort of constant then now it can be accessed via it's `rawValue` property (isn't that the way it would be accessed as it currently stands?).

If it was just an enum, well then that's covered too.

I tried compiling a few contrived enums based on this one to illustrate how I'd expect them to be mapped. I've put them into this gist <https://gist.github.com/anonymous/232d62d77999f5eb27cd>.

I think if you took it in terms of pattern matching where the the first case is the strictest pattern (and thus the hardest to conform to) by the time you reach the 3rd case your back to the current behavior. Swift would then be able to account for more generic enum imports than it currently can.

On Mon, Jan 11, 2016, at 02:51 PM, Jordan Rose wrote:

Oh, you can certainly define a macro named NS_ENUM yourself, but that doesn't really seem like the right thing to do on Linux.

As for why we can't "guess" that this is a true enum, consider the following:

enum Foo {
  A = 1,
  B = 2,
};

Is this a very small option set, or an enum that deliberately doesn't start at 0? Or just a way to define constants for some other type, rather than using "static const"? If the compiler guesses here, (a) it might guess wrong, making the type hard to use now, and (b) if the headers are updated in a newer version of the library, it might change its guess, which would break source compatibility. (This can happen anyway, e.g. the first time an annotation is added, but at least that's supposed to be changing in the right direction, and is unlikely to change again.)

Hope that clarifies the motivation here, even if it's less than satisfactory.

Jordan

On Jan 11, 2016, at 11:22, Ryan Lovelett <swift-dev@ryan.lovelett.me <mailto:swift-dev@ryan.lovelett.me>> wrote:

Jordan,

Perhaps I'm not following that parenthetical comment. Are you saying that even if I could add NS_ENUM to the header it still wouldn't compile into a true Swift enum (on a non-Apple platform)?

Swift's pattern matching capability is probably a top 3 reason why I am trying to port my C application to use Swift rather than just straight C. Not being able to get this nicety out-of-the-box is just part of doing business on a bleeding edge programming language.

However this seems like something that should be able to be achieved. Looking at the fe_type enum definition, because it had no bit pattern associated with it, I would have assumed it was a true non-overlapping enum. Its probable I'm not seeing the whole landscape here so would you be able to illuminate why isn't this the default?

On Mon, Jan 11, 2016, at 01:11 PM, Jordan Rose wrote:

Right. This is because Swift can't tell if your enum is actually an option set, a true, non-overlapping enum, or just a set of related constants, so it picks the lowest common denominator. We currently don't have a great way to override that in headers you don't control.

(Heck, on non-Apple platforms we don't have a great way to do it in headers you do control; Swift is currently keying off the macro names.)

Jordan

On Jan 10, 2016, at 13:25 , Austin Zheng via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

I spoke too soon, the cases are also defined as values of that type. So, a working version of your code:

extensionfe_type:CustomStringConvertible{
publicvardescription:String{
switchself{
caseFE_QPSK:return"QPSK"
caseFE_QAM:return"QAM"
caseFE_OFDM:return"OFDM"
caseFE_ATSC:return"ATSC"
default:fatalError("can't be exhaustive")
    }
  }
}

Best,
Austin

On Jan 10, 2016, at 1:22 PM, Austin Zheng <austinzheng@gmail.com <mailto:austinzheng@gmail.com>> wrote:

Hi Ryan,

Apologies, I should have been more clear. In Xcode you can alt(?)-click on a type (e.g the 'MyType' in "let a : MyType = 123") in the IDE to pop up a little window that shows you the definition, including the type and some other information. If you're on a Linux box or not using an IDE you probably don't have that option.

The only methods I see exposed on the Swift imported type are initializers taking a integer raw value, and a 'rawValue' property for getting back out the raw value. Hope that helps.

Austin

On Jan 10, 2016, at 1:18 PM, Ryan Lovelett <swift-dev@ryan.lovelett.me <mailto:swift-dev@ryan.lovelett.me>> wrote:

Austin,

I guess I should say that the `typedef` is coming from aLinux kernel header <http://lxr.free-electrons.com/source/include/linux/dvb/frontend.h?v=3.2>. So I don't think I'm going to be able to add any macros to the definition.

What do you mean about alt-click? Alt click where?

On Sun, Jan 10, 2016, at 04:12 PM, Austin Zheng wrote:

fe_type is being imported as a struct (alt-click 'fe_type' in Swift). I think if you want it to be imported as an enum you need to use the NS_ENUM macro in the definition, which might not be possible in your case.

Austin

On Jan 10, 2016, at 1:06 PM, Ryan Lovelett via swift-dev <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote:

typedef enum fe_type {
FE_QPSK,
FE_QAM,
FE_OFDM,
FE_ATSC
} fe_type_t;

_______________________________________________
swift-dev mailing list
swift-dev@swift.org <mailto:swift-dev@swift.org>
https://lists.swift.org/mailman/listinfo/swift-dev


(Ryan Lovelett) #11

I was hoping it would come through as light-hearted. That certainly was
my intent. Text is sometimes hard to convey intent through.

Responses inline.

Not insolence at all. :slight_smile: It's important to justify or come up with good logic here, and doubly so on Linux where NS_ENUM/NS_OPTIONS aren't standard.

The usefulness of an enum is that you can guarantee that all cases are enumerated. But even that's not always true; people *do* add new elements to C enums without breaking binary compatibility. Or they have "private" cases that are ABI-compatible but do not correspond to a public declaration. So we'll *already* probably be dropping the idea of exhaustive switch over an imported enum unless we have some prior knowledge. (That still needs design.)

Is Swift effectively worried about the case where in v1 of some header
the enum is defined as

enum Foo {
  A, B
}

and in v2 they add `C` or remove `B`. Thus requiring all the Swift code
case statements to no longer compile because they are "not exhaustive".
If so that sounds like a good thing and not a bad thing to me.

Also, if I understand the "private" cases concern. Then I would contend
it's going to happen with the current import behavior. I think that a
"private" case in Swift's switch developers will have to exhaustively
account for that case even though in practice we know it will never
happen; thus leading to coding "dead" cases.

Consider a C enum that was imported as:

enum Foo {
  case PublicA, Private1, PublicB, Private2
}

the `switch` for the "private" might look something like this:

switch temp {
case .PublicA: fallthrough
case .PublicB: print("Expected")
case .Private1: fallthrough
case .Private2: fatalError("These cases are private")
}

Compare this with the current import behavior of a enum which we, human
in the loop, know to have no "private" cases.

typedef enum fe_type {
  FE_QPSK,
  FE_QAM,
  FE_OFDM,
  FE_ATSC
} fe_type_t;

switch type {
case FE_QPSK: fallthrough
case FE_QAM: fallthrough
case FE_OFDM: fallthrough
case FE_ATSC: fallthrough
default: fatalError("This switch is actually exhaustive. Though the
compiler doesn't know that.")
}

Seems like you end up in the same place. Or perhaps I completely missed
your point?

So then what's the cost of using an enum over a struct for the option set? I guess it's mostly the *implication* that you could switch over it, something you should never do with an option set. There are also option sets with predefined combinations of options:

typedef NS_OPTIONS(NSUInteger, MyOptions) {
MyOptionA = 1,
MyOptionB = 2,
MyOptionC = 4,
MyOptionDefault = MyOptionA | MyOptionC,
};

…which would be weird for an enum. As you say, though, it's not a deal-breaker.

For your gist[https://gist.github.com/anonymous/232d62d77999f5eb27cd] I'm not really sure what separates type 1 from type 2. Is it the explicit values assigned to the cases? The fact that all of the "type 2" values are powers of 2? And what makes type 3 a set of constants rather than just an enum or option set with a compatibility alias for its first element?

I guess I could have been a bit less cryptic. :hushed:

In my mind the fact that it has no explicit values assigned to the case
indicates that it is an "true" enum. The type 2 case is distinct from
the type 1 in that it supplies a value at all. The type 3 case is
distinct from the type 2 case in that it has a repeated value. Or put
more broadly, type 3 is anything that is more complicated than type 1 or
2.

I don't think we want to make these heuristics much more complicated—the more rules there are, the more likely people will be confused when they don't work. One advantage of the current heuristic is that it encourages people to make their headers better; a disadvantage is that not all headers are editable. Maybe we can fix *that* problem instead, for example by looking for annotations on redeclarations of the enum.

Jordan

After writing and thinking about this for a few days I went looking for
C headers that I didn't write or haven't been religiously using for 5
years to get a survey of how these things are done. The take-away: I
give up and now somewhat understand why the defaults are what they are.
I still don't _enjoy_ them mind you but it seems like that might be
beyond Swift's control. The way that enums are (ab)used is so crazy.

I think after having played that game redeclaration upon import or a
Swift API to convert a type through a protocol (is this even possible? I
love me some protocols.) or some other human-in-the-loop mechanism to
promote the imported enum to an first class enum once imported into to
Swift probably would end up being the most sane. It allows the C people
to continue to be, well C people, and the Swift people to write
idiomatic Swift code. After having thought about it for awhile that
seems like the most sane to me.

Can we get this implemented and merged tomorrow? :smiling_imp:

···

On Tue, Jan 12, 2016, at 12:52 PM, Jordan Rose wrote: