4 ms·
> One issue I've had with any kind of auto-generated bindings based on metadata is that you get low-level types such as "Win32 BOOL" and "Win32 GUID" polluting
by brundolf 6y ago
> One issue I've had with any kind of auto-generated bindings based on metadata is that you get low-level types such as "Win32 BOOL" and "Win32 GUID" polluting the output. Mainstream languages already either have native bool and guid types, or have defacto standard libraries.
This shouldn't actually be a huge problem in Rust, because of the traits system. It should be possible to implement all the expected behaviors (Into/From, Add/Sub/Mul/Div, Ord/Eq, etc) for these special types and if library code is properly generic, it shouldn't care whether or not you're using the native type. If it does require a specific concrete type, From/Into should still make things reasonably ergonomic.
Re: safety, the typical approach has just been to keep one library that's a direct mapping to the C API, which is publicly unsafe, and then create a safe library abstracted on top of that which codifies the documented behaviors. That layer relies on documentation, and on humans to follow it, but then so does all C code ever written :)
- jiggawatts 6y agoIn my experience with both classic Win32 programming in the 1990s in C++ and then later in C# and now in Rust is that it is obscenely verbose. It takes a solid page of code to call a single method, and most of that is just noise. A typical method call is: Allocate some struct. Zero it out. Set a "length" field to the length of the struct. Populate the "parameter" fields. Call the API with the struct and 0 for the return buffer size. You get back an expected error code for "buffer too small". You allocate the required space in your buffer. Call the method again. The amount of data changed! Loop and try again.[1] You get your data! Now to check the error codes. Look up error code using a Win32 method. Free the buffer. If on top of that I'd have to call "into" and "from" for every field and every parameter at every step... I think I might just snap and go become a bricklayer or something. PS: Win32 strings and Rust strings aren't compatible. I've already noticed that the current version of the Microsoft code-generator seems to use an awful lot of [u16] arrays, which are not the system OsString struct. For comparison, the C# "String" type isn't directly compatible with Win32 strings either, but the FFI engine handles this conversion back-and-forth for you. Similarly, few (any?) languages have good built-in support for the various kinds of "struct-followed-by-dynamic-data" typically returned by system APIs. Notably, C does not have any way to encode this in the type system, so API metadata tends to just leave this out or describe it in the documentation. If the metadata was extented to cover this kind of thing, then the generators could do all sorts of clever things with this scenario... [1] A lot of people don't do this, they assume that the second call will always succeed. Programming in this style leads to rare random errors throughout the entire application. A loop is required for correctness for most (but not all) Win32 calls that return dynamic data...
- orra 6y agoYou're not wrong that win32 is very verbose. > A loop is required for correctness for most (but not all) Win32 calls that return dynamic data... A very good point, one very rarely mentioned. Of course, the win32 windowing and common controls and single threaded, so if it's the same application it won't have changed underneath you.
- flohofwoe 6y agoC99 has made all that a lot easier, you can setup, initialize a struct and call the function taking a pointer to that struct all in one call: https://github.com/floooh/sokol-samples/blob/89f5825ab5d36906b2dc219efbce3d00be9e7cdf/d3d11/d3d11entry.c#L52-L59 https://github.com/floooh/sokol-samples/blob/89f5825ab5d3690... The Win32 window system functions and D3D11 are actually very convenient to work with in C99, better than C++ actually.
- ape4 6y agoYes as a simple example of a wrapper: std::string nicerGetComputerName() { char buf[1024]; DWORD len = sizeof(buf); memset(buf, 0, sizeof(buf)); GetComputerNameA(buf, &len); return buf; }