On Sun, May 15, 2016 at 2:29 AM, Stefan Schnell <[email protected]> wrote:
>
>
> Hello community,
>
> I am fresh at Julia language and I am fascinated about the possibilities.
> At the moment I examine the possibilities to communicate with C libraries.
> And here I have a question.
>
> I have a structure of pointers as parameter for a C function
> struct connparam{
>   charU * name
>   charU * value
> }
>
> In Julia I try this:
> type connparam
>   name::Ptr{UInt16}
>   value::Ptr{UInt16}
> end
>
> uname = "UserName"
> uvalue = "Stefan"
> cp = connparams(utf16(uname), utf16(uvalue))
>
> ccall((:Test, "CTest.dll"), stdcall, Void, (Ptr{Void},), cp)
>
> But this is not the correct way to transfer type data to a C function,
> because I get the error:
> MethodError: `convert` has no method matching convert(::Type{Ptr{UInt16}},
> ::UTF16String)
>
> What is the correct way to fill the structure with pointers and to transfer
> it to the C function?

Passing structs with pointers to managed memory typically requires
manual management of the object lifetime using `cconvert` and
`unsafe_convert`. This is done automatically for `ccall` arguments
since the lifetime of the argument is very clear to the compiler.

For how `cconvert` and `unsafe_convert` works, see the manual[1]
For an example see my recent PyCall.jl PR[2].

For your case, I would write it as

```
type connparam
    name::Ptr{UInt16}
    value::Ptr{UInt16}
    name_root # GC Root for name (never read from)
    value_root # GC Root for value (never read from)
    function connparam(name, value)
        # As long as `name_root` and `value_root` are live, the
        # `unsafe_convert` result should be valid
        name_root = Base.cconvert(Ptr{UInt16}, name)
        value_root = Base.cconvert(Ptr{UInt16}, value)
        new(Base.unsafe_convert(Ptr{UInt16}, name_root),
                Base.unsafe_convert(Ptr{UInt16}, value_root),
                name_root, value_root)
    end
end

uname = "UserName"
uvalue = "Stefan"
cp = connparams(utf16(uname), utf16(uvalue))

# The Ref{connparam} here should pass a reference of `cp` to the
# C function.
ccall((:Test, "CTest.dll"), stdcall, Void, (Ref{connparam},), cp)
```

The interface is not particularly optimized for using outside `ccall`
so suggestions of how to make this easier to use are welcome. The
explicit rooting and `unsafe_convert` part is basically equivalent to
what you need to do in all other managed languages when the language
runtime/compiler can't reason about the lifetime of the C pointer, the
`cconvert` is needed since data copy might be necessary as part of the
conversion (imagining you have a linked list and want to pass it as an
array to C, the allocation of the array would be done in `cconvert`).

[1] 
http://julia.readthedocs.io/en/latest/manual/calling-c-and-fortran-code/#auto-conversion
[2] https://github.com/stevengj/PyCall.jl/pull/267#issuecomment-217683606

>
> Thanks for tips and hints.
>
> Cheers
> Stefan

Reply via email to