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

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

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_DEFs to the makeStruct function, but first I need to generate those TAG_DEFs 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

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.@preserves around but really without a real rationale or understanding so it hasn’t been very useful…

Any pointers (pun intended)?

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

The function I need to call is this one, it wants a pointer to an array of TAG_DEFs.
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

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

void *MakeStruct(TAG_DEF *tags)