Interpret `NTuple{1024, UInt8}` as a C string

I am calling some C code. One of the structs has a field is of type char [1024], which Clang.jl turns into NTuple{1024,UInt8}. This is a null-terminated C string.

How can I get a String-like thing from this without copying? I see there is String(v::Array{UInt8,1}) or String(v::AbstractArray{UInt8,1}), but I have a tuple, not an array.

I was able to do

julia> data = (0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x73, 0x73, 0x69, 0x73, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x74, 0x00, 0x4f)
(0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x73, 0x73, 0x69, 0x73, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x74, 0x00, 0x4f)

julia> using StaticArrays

julia> String(SArray{Tuple{length(data)}}(data))
"base_chassis_joint\0O"

which possibly does not do any copying (not sure). But it doesn’t interpret the String as null-terminated.

If I could get a pointer to the data, then I can use Cstring, however the field is an NTuple, which has immutable semantics. I cannot call pointer_from_objref on it.

1 Like

I believe it is something like

unsafe_string(Ptr{UInt8}(pointer_from_objref(s) + fieldoffset(Cstruct, 1)))

where s is your struct instance and 1 is the field number of the string (here the type Cstruct just has the string as the first field).

You can use

mutable struct Foo
field1::Int 
stringfield::NTuple{1024, UInt8}
#...
end

and then use pointer_from_objref(some_foo)+fieldoffset(Foo, 2). I am assuming that all your C functions actually operate on Foo* instead of Foo?

Thanks. In my case the struct s is immutable itself, because I was dereferencing the Ref. But perhaps, then, I should return the Ref if I want to use that.

Just to be concrete, this is what Clang.jl emits:

struct b3JointInfo
    m_linkName::NTuple{1024, UInt8}
    m_jointName::NTuple{1024, UInt8}
    m_jointType::Cint
    m_qIndex::Cint
    m_uIndex::Cint
    m_jointIndex::Cint
    m_flags::Cint
    m_jointDamping::Cdouble
    m_jointFriction::Cdouble
    m_jointLowerLimit::Cdouble
    m_jointUpperLimit::Cdouble
    m_jointMaxForce::Cdouble
    m_jointMaxVelocity::Cdouble
    m_parentFrame::NTuple{7, Cdouble}
    m_childFrame::NTuple{7, Cdouble}
    m_jointAxis::NTuple{3, Cdouble}
    m_parentIndex::Cint
    m_qSize::Cint
    m_uSize::Cint
end

You can use pointer_from_objref(some::Base.RefValue{b3JointInfo}).

The reason why Clang.jl emitted the code like this is that there is no concise one-to-one correspondence between Julia structs and C structs. We have to use NTuple to ensure alignment consistency. The copying is inevitable when your C function returns an instance of that type. If you don’t want that copying, your function has to return a pointer. A common pattern is to define an opaque struct like typedef struct b3JointInfo* b3JointInfoHandle; and access the string data via byte offset.