# Preserve data behind nested pointers in ccall with array of structs of pointers

**URL:** <https://discourse.julialang.org/t/preserve-data-behind-nested-pointers-in-ccall-with-array-of-structs-of-pointers/124607>\
**Category:** General Usage\
**Created:** [January 9, 2025, 7:57pm UTC](https://discourse.julialang.org/t/preserve-data-behind-nested-pointers-in-ccall-with-array-of-structs-of-pointers/124607 "2025-01-09T19:57:06Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![cshen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cshen/32/217287_2.png) [@cshen](https://discourse.julialang.org/u/cshen)\
**Post date:** [January 9, 2025, 7:57pm UTC](https://discourse.julialang.org/t/preserve-data-behind-nested-pointers-in-ccall-with-array-of-structs-of-pointers/124607/1 "2025-01-09T19:57:06Z")

</div>

Hello,  
I’m doing some FFI gymnastics but I am a bit lost in the woods.  
Here is a mock up of my situation:

```julia
struct TAG_DEF # this structure was automatically generated by Clang.jl
    name::Ptr{Cuchar} # string
    dims::Ptr{Int64} # array of ints
    type::Ptr{Cvoid} # opaque type (long story)
end

function makeStruct(tags)
    ccall((:makeStruct, lib), <some return type>, (ptr{TAG_DEF},), tags)
end 

```

so In practice, I need to pass an array of `TAG_DEF`s to the `makeStruct` function, but first I need to generate those `TAG_DEF`s from julia objects.  
I am familiar with the `cconvert/unsafe_convert` dance, but so far I’ve only needed to interface with passing refs and pointers directly to the ccall.

If I had to pass only a single `TAG_DEF` to the ccall, I would try something like

```julia
name = "name"
dims = [1, 2, 3]
type = 4 # types from the c side.
v = (name, dims, type)

# with
Base.cconvert(::Type{TAG_DEF}, v::Tuple) = begin
    (Base.cconvert(Cstring, v[1]), v[2], Ref(v[3]))
end
Base.unsafe_convert(::Type{TAG_DEF}, v::Tuple) = begin
    TAG_DEF(Base.unsafe_convert(Cstring, v[1]), pointer(v[2]), Base.unsafe_convert(Ptr{Cvoid}, v[3]))
end

# and a makeStruct call like
function makeStruct(single_tag)
    ccall((:makeStruct, lib), < some return type>, (TAG_DEF,), single_tag)
end

# and call it with
makeStruct(v)

```

to make the translation happen automatically inside the ccall.  
But the extra pointer required by the array argument really throws me off.

In particular, I’m not sure how I can make sure to tell the gc to not delete objects that are behind 3 layers of pointers. Or how to override the `cconvert` and `unsafe_convert` calls to also account for the additional array conversion.

I’ve sprinkled a lot of `GC.@preserve`s around but really without a real rationale or understanding so it hasn’t been very useful…

Any pointers (pun intended)?

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [January 11, 2025, 2:44pm UTC](https://discourse.julialang.org/t/preserve-data-behind-nested-pointers-in-ccall-with-array-of-structs-of-pointers/124607/2 "2025-01-11T14:44:10Z")

</div>

What’s the signature of the C function you’re trying to call?

---

<div class="post-metadata">

**Author:** ![cshen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cshen/32/217287_2.png) [@cshen](https://discourse.julialang.org/u/cshen)\
**Post date:** [January 11, 2025, 3:54pm UTC](https://discourse.julialang.org/t/preserve-data-behind-nested-pointers-in-ccall-with-array-of-structs-of-pointers/124607/3 "2025-01-11T15:54:00Z")

</div>

> [@cshen](#):
>
> ```julia
> function makeStruct(tags)
> ccall((:makeStruct, lib), <some return type>, (ptr{TAG_DEF},), tags)
> end 
> 
> ```

The function I need to call is this one, it wants a pointer to an array of `TAG_DEF`s.  
The return value is another pointer to another type of structure, but it’s not really relevant, that’s why I just left it out.

The exact C code is this

```julia
typedef struct {
    char *name;
    MEMINT *dims; // MEMINT translates to Int64
    void *type;
} TAG_DEF;

void *MakeStruct(TAG_DEF *tags)

```

---

<div class="post-metadata">

**Author:** ![cshen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cshen/32/217287_2.png) [@cshen](https://discourse.julialang.org/u/cshen)\
**Post date:** [January 22, 2025, 11:18pm UTC](https://discourse.julialang.org/t/preserve-data-behind-nested-pointers-in-ccall-with-array-of-structs-of-pointers/124607/4 "2025-01-22T23:18:53Z")

</div>

Managed to find a solution:

```julia
function TAG_DEF(
	name::Union{String, Nothing},
	dims::Union{Vector{Int}, Nothing},
	type::Union{DataType, Nothing}
)
	_name = isnothing(name) ? C_NULL : begin
		name_save = Base.cconvert(Ptr{Cchar}, name)
		Base.unsafe_convert(Ptr{Cchar}, name_save)
	end

	_dims = isnothing(dims) ? C_NULL : begin
		dims_save = Base.cconvert(Ptr{Int}, dims)
		Base.unsafe_convert(Ptr{Int}, dims_save)
	end

	_opaquetype = isnothing(type) ? C_NULL :
		type <: JL_SCALAR ? Ptr{Cvoid}(translate_type(type)) : Ptr{Cvoid}(type)

	TAG_DEF(_name, _dims, _opaquetype)
end

Base.cconvert(::Type{Ptr{TAG_DEF}}, tags::Vector{Tuple}) = begin
	tag_structs = splat(TAG_DEF).(tags)
	tag_structs_save = Base.cconvert(Ptr{TAG_DEF}, tag_structs)
	return (tag_structs_save, tags)
end

Base.unsafe_convert(::Type{Ptr{TAG_DEF}}, cct::Tuple{MemoryRef{TAG_DEF}, Vector{Tuple}}) = begin
	Base.unsafe_convert(Ptr{LibIDL.IDL_STRUCT_TAG_DEF}, first(cct))
end

```

And I let `ccall` do the rest with  
`makeStruct(tags)` where for example `tags =[("A", nothing, Int64), ("B", [1,2], Int64), (nothing, nothing, nothing)]` (must be null terminated)
