Address of object

passing a variable to a c library by reference, that takes a void* and size, using Ref(x), Base.summarysize(x) does not work as expected for Arrays:

a=[1,2,3]
b=[1,2,3]
f(a) 
f(b)

the C function f should return the same value, since it is based on the memory content of the parameter, but f(a)!=f(b)

Do Julia arrays contain additional data that does not depend on the content?
How can I send a reference only to the content of the array?
Are there other Julia data types that require special treatment when sending them as memory blocks to external libraries?

Hard to say what’s going wrong without a concrete example. In particular, I believe your use of Base.summarysize is misguided - what exactly does your C function expect, a length in bytes? Also, passing an array shouldn’t require wrapping it in Ref, otherwise your C code will get the pointer to the Ref instead of the pointer to the data of the array.

Yes, the array type does have more metadata under the hood, but depending on how you passed it to C, it probably never sees that. How does f work under the hood, how are you calling your C function?

If you’ve followed the docs, that should already happen. The metadata is not passed around when using the ccall interface. Arrays aren’t really special here.

Thanks.

Hard to say what’s going wrong without a concrete example.

I am referring to a hash function that takes (const void* input, size_t length), and I am sending the data as Ref(input), sizeof(input) which works well for built-in numeric types or using Base.summarysize(input) for arrays.

The docs refer to a Vector of Ints, however when using vectors of strings or multi-dimensional arrays, I have to use summarysize to get the total size in bytes.

However, I would like the memory block (defined pointer and size) to be the same for both f(a) and f(b) in my initial example.

I see - there’s multiple issues. In julia, Strings are not stored inline in the array, but rather as pointers to the strings - they are not zero delimited. So passing the array as a whole will hash the pointers to those objects, not the strings (if passed naively).

I suspect you’re using summarysize because the size you got using sizeof did not match up with the expected length? This is also explained by a Vector{String} being a vector of pointers to strings, as sizeof does not recurse into fields/array elements, while summarysize does.

Further, I’m pretty sure that you don’t need to wrap the array with Ref to pass it to C. Wrapping like that is only necessary for objects that don’t have a defined memory address (as is the case for immutables like Int), which arrays already have. A minimal working example showing your code would be helpful for diagnosing what’s going wrong.

What exact code are you using to pass those arrays to C? If their content is the same, the C code should compute the exact same thing, though the pointer to the arrays will be different:

julia> a = [1,2,3]
3-element Vector{Int64}:
 1
 2
 3

julia> b = [1,2,3]
3-element Vector{Int64}:
 1
 2
 3

julia> a == b # the arrays are equal
true

julia> a === b # the arrays are not the same object, hence will have a different address
false
1 Like

Notice that while you’re experimenting you can simulate a C library from Julia like this:

julia> function myhash(p::Ptr{Cvoid}, n::Cint)
           hash = Cuchar(0)
           i = Cint(1)
           while i <= n
               hash ⊻= unsafe_load(Ptr{Cuchar}(p), i)
               i += Cint(1)
           end
           return hash
       end
myhash (generic function with 1 method)

julia> myhash_pointer = @cfunction(myhash, Cuchar, (Ptr{Cvoid}, Cint))
Ptr{Nothing} @0x00007fc3fb970840

julia> a=[13, 17, 19];

julia> b=[13, 17, 19];

julia> ccall(myhash_pointer, Cuchar, (Ptr{Cvoid}, Cint), a, sizeof(a))
0x0f

julia> ccall(myhash_pointer, Cuchar, (Ptr{Cvoid}, Cint), b, sizeof(b))
0x0f

This allows you to conveniently add debug prints to the “C” function or communicate data with the REPL via global variables.

5 Likes