Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast integer to enum type with Flags attribute

I have an enum type with the flags attribute:

[<Flags>]
type StatusWord =
    | DATAPROCESS       = 0b0000000001
    | ERRORPRESENT      = 0b0000000010
    | CHECKSUMERROR     = 0b0000000100
    | PACKETSIZEERROR   = 0b0000001000
    | TIMEOUT           = 0b0000010000
    | DONOTRETRY        = 0b1000000000

and during some data initialization, I have a uint16 value I want to convert to the enum's type, StatusWord, so I can compare it's properties:

let value: uint16 = 0b1000001001us
let flags: StatusWord = StatusWord value

but, as you might be able to guess, this code doesn't compile; the conversion isn't available. Likewise, I also can't do explicit casts, eg. value :> StatusWord or value :?> StatusWord. This is a simple task in C#, so I'm having trouble figuring out why I can't do it in F#.

like image 208
Maurdekye Avatar asked Jan 20 '26 22:01

Maurdekye


2 Answers

So, two things you have to worry about. One (which I think you already realize) is that your underlying enum type is int32, and your value is uint16, so a conversion will need to happen somewhere. Two, you have to construct the enum type.

StatusWord looks like a constructor (similar to a union case member), but it's not. So here are two ways to do it with your uint16 value, and a third way to do it which is much better for readability, if you can do it that way.

let value = 0b1000001001us

// use F# enum operator
let flags1 = enum<StatusWord> (int value)

// use static Enum class
let flags2 = Enum.Parse(typeof<StatusWord>, string value) :?> StatusWord

// do bitwise stuff, of course now the compiler knows what you're doing
let flags3 = StatusWord.DATAPROCESS ||| StatusWord.PACKETSIZEERROR ||| StatusWord.DONOTRETRY

Because there are multiple ways, I had to refresh my memory, which I did at

https://fsharpforfunandprofit.com/posts/enum-types/

which is highly recommended reading (that article and the rest of that blog - it's how many people learn F#).

like image 117
Jim Foye Avatar answered Jan 23 '26 18:01

Jim Foye


To convert a numerical value to an enum in F#, you can use the built-in LanguagePrimitives.EnumOfValue function.

let flags: StatusWord = LanguagePrimitives.EnumOfValue value

In your example, this does not actually work, because the type of value is uint16, but the underlying type of the enum is int (because the values do not have the us suffix). To get that to work, you'll need to either convert uint32 to int, or change the definition of your enum. The following works perfectly:

[<Flags>]
type StatusWord =
    | DATAPROCESS       = 0b0000000001us
    | ERRORPRESENT      = 0b0000000010us
    | CHECKSUMERROR     = 0b0000000100us
    | PACKETSIZEERROR   = 0b0000001000us
    | TIMEOUT           = 0b0000010000us
    | DONOTRETRY        = 0b1000000000us

let value: uint16 = 0b1000001001us
let flags: StatusWord = LanguagePrimitives.EnumOfValue value

EDIT: Jim's answer mentions the enum function. For some reason (I'm not sure why!) this only works with int32 arguments, so using EnumOfValue is probably better if you want to keep the base type of your enum as uint16. If you wanted to keep that as int32, then enum is much nicer option!

like image 38
Tomas Petricek Avatar answered Jan 23 '26 18:01

Tomas Petricek