Hi Matthew,
Wow! Thank you for the detailed response, and the saintly patience behind it.
In following your email, this is the working result after using '_pointer' in the function signature:
(define instance-ptr (malloc _VkInstance))
(vkCreateInstance instcreateinfo #f instance-ptr)
(define instance (ptr-ref instance-ptr _VkInstance))
(vkDestroyInstance instance #f)
My confusion did indeed stem from not understanding how pointer indirection was modeled in Racket. I was distracted by questions like "What's the address-of operator equivalent?" while staring at the arguments to the wrapper procedures.
I still prefer this form for the unsafe layer because of its faithful representation of Vulkan. This is not at all to discount your advice for using _ptr as the best working translation. The spec supports a view where I can generate a second layer of helpful wrapper procedures that use the first, as opposed to generating the most tailored wrapper procedure at the first oppurtunity.
Thank you again. I know you didn't have to take the time, so it means a lot to me that you did. Hopefully I won't end up confused again. :)
~slg
‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
On Tuesday, October 22, 2019 9:28 AM, Matthew Flatt <
mfl...@cs.utah.edu> wrote:
> The short answer is: `(_ptr io _VkInstance)` does not mean that a
> pointer to a `_VkInstance` happens on the Racket side. It means that a
> `_VkInstance` (not a pointer to a `_VkInstance`) happens on the Racket
> side, and a pointer to a `_VkInstance` happens on the C side.
>
> Taking it from the top:
>
> At Mon, 21 Oct 2019 19:07:40 +0000, Sage Gerard wrote:
>
> > (define _VkInstance (_cpointer 'VkInstance_T))
>
> So far, so good.`VkInstance` is defined in Vulkan to be a pointer to
> unspecified memory, so this makes sense.
>
> To be clear,
>
> (malloc _VkInstance)
>
> does not create a `_VkInstance`. It creates a pointer to a
> `_VkInstance` (i.e., a pointer to a pointer). That could be what you
> want, depending on how `vkCreateInstance` is defined. Specifically, to
> make this work:
>
> (define instance-ptr (malloc _VkInstance))
>
> (vkCreateInstance instcreateinfo #f instance-ptr)
>
> (define created-instance (ptr-ref instance-ptr _VkInstance))
>
> Define `vkCreateInstance` like this:
>
> (define-vulkan vkCreateInstance
> (_fun _VkInstanceCreateInfo-pointer/null
> _VkAllocationCallbacks-pointer/null
> _pointer ; to a _VkInstance
> -> (r : _VkResult)
>
> -> (check-vkResult r 'vkCreateInstance)))
>
>
> I've written the third type as just`_pointer` because there's not a
> simple way to say "pointer to a `_VkInstance`". There's no way to write
> that because it would offer no more checking than just `_pointer`. It
> might be a little better to write the comment as a `define`:
>
> (define _VkInstance-pointer _pointer)
>
> (define-vulkan vkCreateInstance
> (_fun _VkInstanceCreateInfo-pointer/null
> _VkAllocationCallbacks-pointer/null
> _VkInstance-pointer
> -> (r : _VkResult)
>
> -> (check-vkResult r 'vkCreateInstance)))
>
>
> You could go further and write
>
> ;; pointer to a `_VkInstance`:
> (define _VkInstance-pointer (_cpointer 'VkInstance_T*))
>
> (define-vulkan vkCreateInstance
> (_fun _VkInstanceCreateInfo-pointer/null
> _VkAllocationCallbacks-pointer/null
> _VkInstance-pointer
> -> (r : _VkResult)
>
> -> (check-vkResult r 'vkCreateInstance)))
>
>
> (define instance-ptr (malloc _VkInstance))
> (cpointer-push-tag! instance-ptr 'VkInstance_T*)
> (vkCreateInstance instcreateinfo #f instance-ptr)
> (define created-instance (ptr-ref instance-ptr _VkInstance))
>
> That's probably more trouble than it's worth, and the checking benefits
> depend on `malloc` and `cpointer-push-tag!` begin used consistently
> together. Declaring `_VkInstance-pointer` could make sense if the
> library provides a `make-VkInstance-pointer` function to combine the
> `malloc` and `cpointer-push-tag!` steps, helping to make sure they're
> always consistent.
>
> Instead of `_pointer` or `_VkInstance-pointer`, you've written
>
> (_ptr io _VkInstance)
>
> As an argument type, that means:
>
> - The caller supplies a `_VkInstance` (not a pointer to a `VkInstance`).
>
> - Fresh memory for a `_VkInstance` is allocated.
>
> - The given `_VkInstance` is copied into the fresh memory.
>
> - The address of the fresh memory is passed to the foreign function as
> a pointer to a `_VkInstance`.
>
> So, you're seeing an error because `(malloc _VkInstance)` does not
> create a `_VkInstance`. Anyway, this is not what you want, because
> `vkCreateInstance` would set the pointer at the fresh memory, which is
> then discarded.
>
> Generally, a `(_ptr io ...)` argument without a name binding doesn't
> make any sense --- unless you really want to ignore the result (which
> you don't, in this case).
>
> It's pretty typical for "middleware" layers like the FFI to take into
> account the in--out behavior of function arguments, and that's why many
> C prototype descriptions are augmented with that information. The
> `ffi/unsafe` layer is meant to be used that way, too, so the
> translation that works best for `ffi/unsafe` is
>
> (define-vulkan vkCreateInstance
> (_fun _VkInstanceCreateInfo-pointer/null
> _VkAllocationCallbacks-pointer/null
> (o : (_ptr o _VkInstance))
> -> (r : _VkResult)
>
> -> (begin
>
> (check-vkResult r 'vkCreateInstance)
> o)))
>
>
>
> because the third argument is an out argument.
>
> If you don't make to make these in--out distinctions, then the first or
> second definition of `vkCreateInstance` above is the most practical
> choice.
</
mfl...@cs.utah.edu>