Incidentally, the general function for getting a unique integer for a
variant type with non-constant constructors isn't that bad either:

let int_of_gen x =
 let x = Obj.repr x
 in
   if Obj.is_block x
   then -1 * Obj.tag x - 1
   else (Obj.magic x : int)

I consider that version much better. You could also write it in a more restrictive way :

 let to_int x =
   let repr = Obj.repr x in
   if Obj.is_int repr
   then (Obj.obj repr : int)
   else invalid_arg "to_int";;

The good point about those is that they check that the memory representation is compatible before casting (while the "%identity" does not). They're safer. Of course, they still break parametricity if used polymorphically, so as you said type should be constrained.

I still would prefer the more direct pattern-matching definition : it may require code generation not to be tedious, but the generated code is an honourable OCaml citizen.