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