Derived enums as bitmap masks

For example I have the following code:

type feature_mask =
  | FEATURE1 [@value 1]
  | FEATURE2 [@value 8]
  | FEATURE3 [@value 0x400]
  [@@deriving enum, show]

let decode_feature x =
   let feature1 = if x land 1 > 0 then "FEATURE1" else "" in
   let feature2 = if x land 8 > 0 then "FEATURE2" else "" in
   let feature3 = if x land 0x400 > 0 then "FEATURE3" else "" in
   String.concat ~sep:" | " [feature1; feature2; feature3]

But the code is ugly in a way that I have do use the numbers in two places, instead of defining them only once. Also it looks a bit like spaghetti. Are there any ways to make it “the right way”?

I filled an issue in ppx_deriving for adding bitmasks, but wonder if there is a way to do that without patching ppx_deriving yet.

How about this?
(1) use ppx_enumerate to get a list of all flags and values
(2) decode is a simple fold with flag/mask
(3) pp using sexp (reversible) or show

??

open Sexplib0.Sexp_conv

type feature_mask_element =
  | FEATURE1 [@value 1]
  | FEATURE2 [@value 8]
  | FEATURE3 [@value 0x400]
  [@@deriving enum, show, enumerate, sexp]

type feature_mask = feature_mask_element list
  [@@deriving show, sexp]

  let all_masks =
    all_of_feature_mask_element
    |> List.map (fun f -> (f,feature_mask_element_to_enum f))

let decode_features x =
  List.fold_left (fun acc (f,mask) ->
      if x land mask <> 0 then (f::acc) else acc)
    [] all_masks
1 Like

Looks OK from the source code point of view, not sure about generated machine code/performance though.