Reputation: 31
when using Julia calling C, it is very nice that Julia can provide a thin wrapper around existing data allocated by C, which makes the data exchange efficiently between C and Julia. And here is the famous online example in Julia embedding section:
double *existingArray = (double*)malloc(sizeof(double)*10);
jl_array_t *x = jl_ptr_to_array_1d(array_type, existingArray, 10, 0);
the question I have is: can Julia gurus provide an example illustrating how to create a thin wrapper around existing array of strings (already allocated on heap in C)
A follow-up question is: do we also need to take care of write barriers in some way? since the array of strings is basically an array of pointers,
Another question is: how Julia does gc handling when the underlying data is an array of strings: i.e. array of pointers, each pointer pointing to the starting address of a null-terminated string.
Many thanks!!!
Zhiyuan
Upvotes: 1
Views: 419
Reputation: 31
It looks that Julia 0.5.2 does not complain the following approach :
suppose that in C/C++ you have :
char* pchar points to “A is s\0B is so\0C is som\0D is some\0”, size : 34 = size_all
and
char** myStrArray has 4 pointers : pointing to the location of "A", "B", "C", D",
and the goal is to pass pchar and myStrArray back to Julia without copying so that in Julia somehow you have an array of string :
{ "A is s", "B is so", "C is som", "D is some" }
in the C/C++ portion :
jl_value_t* array_type_pointer = jl_apply_array_type(jl_voidpointer_type, 1);
jl_array_t* str_array = jl_ptr_to_array_1d( array_type_pointer, (void*)myStrArray, 4, 1 );
jl_set_nth_field(ret, 14, (jl_value_t*)str_array );
jl_value_t* array_type_uint8 = jl_apply_array_type(jl_uint8_type, 1);
jl_array_t* chr_array = jl_ptr_to_array_1d( array_type_uint8, (uint8_t*)pchar, size_all, 1 );
jl_set_nth_field(ret, 13, (jl_value_t*)chr_array );
Notice that in jl_ptr_to_array_1d call, the last param := 1, which means that Julia owns the buffer.
in Julia portion
you declare the following two :
StrVec::Array
StrVecPtr::Array
both of the above two belong to ret1, say.
julia> String(copy( ret1.StrVec ) )
"A is s\0B is so\0C is som\0D is some\0"
julia> ret1.StrVecPtr
4-element Array{Ptr{Void},1}:
Ptr{Void} @0x000000000404aa00
Ptr{Void} @0x000000000404aa07
Ptr{Void} @0x000000000404aa0f
Ptr{Void} @0x000000000404aa18
julia> unsafe_string( convert( Ptr{UInt8}, ret1.StrVecPtr[1] ) )
"A is s"
julia> unsafe_string( convert( Ptr{UInt8}, ret1.StrVecPtr[2] ) )
"B is so"
julia> unsafe_string( convert( Ptr{UInt8}, ret1.StrVecPtr[3] ) )
"C is som"
julia> unsafe_string( convert( Ptr{UInt8}, ret1.StrVecPtr[4] ) )
"D is some"
Remarks : the contents are contained in an array of UInt8, separated by '\0', contiguous in memory, making it easier to do gc after all.
If someone spots some errors, or have alternative ways, please post.
Many thanks.
Upvotes: 2