What is the correct way to create a "pointer to pointer"?

OpenGL has a function for compiling shader source code. It takes an array of strings, a.k.a. const char**. I can see an example in ModernGL.jl of how to do this with pointers:

source::String = "..."
ptr_to_ptr = convert(Ptr{UInt8}, pointer([ convert(Ptr{GLchar}, pointer(source)) ]))
glShaderSource(shader, 1, ptr_to_ptr, C_NULL)

However, my understanding is that Ptr is dangerous compared to the more modern Ref. The docs say that Ref{Any} is a special case that provides a reference to a reference, but in practice it doesn’t seem to work when I make the ccall with Ref{Any}(source).

What is the best-practice way to pass a nested pointer into a ccall?

1 Like

For strings, just use Ptr{Ptr{Cchar}} in the ccall type signature, and the rest is done by Julia. You can refer to https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/#man-bits-types. As for the case in ModernGL.jl, simply passing a vector of strings is fine.

I have created a demo for this, feel free if you have any questions!

Any is a Julia specific container that is capable of holding any Julia value, just like void * in C. Generally, this is used to pass extra arguments to C functions, which will eventually be used by a Julia function. This blog post demonstrates the typical use case. As Julia objects are far more complicated than simple pointers, this is not equivalent to a reference or pointer.

3 Likes

FWIW, in LIKWID.jl I had to wrap a C API and in a few cases, a reference to a char** was required as input on the C side (to realize multiple string outputs). In the ccall I have Ptr{Ptr{Ptr{Cchar}}} for the input argument and to call it, I create and pass over a Ref{Ptr{Ptr{Cchar}}}(). Later, I use unsafe_wrap to get the strings.

3 Likes