Closed cedlemo closed 5 years ago
There's no direct support for flag sets in enum
, and what you have looks like a fine approach.
You might consider
view
to automatically convert back and forth between lists of flags
values and the uint32_t
constant
support to retrieve the values for the flags from C rather than hard-coding them into your program (see https://github.com/ocamllabs/ocaml-ctypes/issues/29 for some discussion). I think ppx_cstubs
might make this easier (but I haven't yet used it myself)Ok, thanks @yallop.
Sorry to reopen this but I would like that you have a look at what I have done:
I use Cstubs constant and I create a view to deal with flags list.
First I wrote this in my bindings module:
(** Flags for a Function_info struct. *)
type flags =
| Is_method (** is a method. *)
| Is_constructor (** is a constructor. *)
| Is_getter (** is a getter of a Property_info. *)
| Is_setter (** is a setter of a Property_info. *)
| Wraps_vfunc (** represents a virtual function. *)
| Throws (** the function may throw an error. *)
module Enums = functor (T : Cstubs.Types.TYPE) -> struct
(** Other stuff *)
let gi_function_is_method = T.constant "GI_FUNCTION_IS_METHOD" T.int64_t
let gi_function_is_constructor = T.constant "GI_FUNCTION_IS_CONSTRUCTOR" T.int64_t
let gi_function_is_getter = T.constant "GI_FUNCTION_IS_GETTER" T.int64_t
let gi_function_is_setter = T.constant "GI_FUNCTION_IS_SETTER" T.int64_t
let gi_function_wraps_vfunc = T.constant "GI_FUNCTION_WRAPS_VFUNC" T.int64_t
let gi_function_throws = T.constant "GI_FUNCTION_THROWS" T.int64_t
end
I have a Stubs.ml file:
module Enums = Bindings.Enums(Bindings_stubs)
include Enums
And in my Function_info.ml file, I have the following:
open Ctypes
open Foreign
open Stubs
type t
let functioninfo : t structure typ = structure "Function_info"
let all_flags : (int64 * Bindings.flags) list= [
gi_function_is_method, Is_method;
gi_function_is_constructor, Is_constructor;
gi_function_is_getter, Is_getter;
gi_function_is_setter, Is_setter;
gi_function_wraps_vfunc, Wraps_vfunc;
gi_function_throws, Throws;
]
let flags_of_int64 v =
let open Int64 in
let rec build_flags_list allf acc =
match allf with
| [] -> acc
| (i, f) :: q -> if ((logand v i) <> zero) then build_flags_list q (f :: acc)
else build_flags_list q acc
in build_flags_list all_flags []
let int64_of_flags (f : Bindings.flags list) =
let open Int64 in
let bitwise_or = fun acc value ->
let (i, _f) = List.find (fun (i', f') -> value = f') all_flags in logor acc i
in
List.fold_left bitwise_or Int64.zero f
let flags =
view int64_t
~read:flags_of_int64
~write:int64_of_flags
let get_flags =
foreign "g_function_info_get_flags"
(ptr functioninfo @-> returning flags)
I can compile it and simple tests seem to be fine.
The problem is the int64_t
, that you use to define the view. Is the underlying type really 64-bit long and signed? Probably not, c compilers are free to choose an appropriate type for enums.
Your flags
type will work as expected, if you use it in order to pass parameters to functions (or as a return value). The c compiler will then implicitly cast it to an integer type of the proper width or back to int64_t
. You will probably not trigger any bugs due to differences in signedness and bit-width, because your constants are within a safe range.
But if you use flags
inside other contexts (as pointer, as a field inside a structure, Foreign
,...), it's not longer safe. If the type of GI_FUNCTION_IS_METHOD
has less than 64 bits, you will write out of bounds.
The problem is the int64_t, that you use to define the view. Is the underlying type really 64-bit long and signed? Probably not, c compilers are free to choose an appropriate type for enums.
This is true, and the problem can be fixed by using enum
, which will retrieve the correct size and alignment for the enum type from C.
So if you have a C definition like this:
enum flags { ... };
then you might add the following to your bindings functor:
let enum_flags = enum "flags" [] ~unexpected:(fun x -> x)
When you link everything together you'll end up with a value of the type int64 typ
with the correct size and alignment for enum flags
, which you can use as the basis for the view:
let flags = view enum_flags ~read:... ~write:...
thanks to both of you.
I would like to know if using the Cstubs enum in order to bind a C enum values that are used as flags (ie ored value) is the good way to do?
For example this function:
returns a set of values that are bitwise ored. Previously, I created my bindings like this:
And it returns a list of flags.Is there a way to deal with ored values with the Cstubs enum type?