如何将“Ptr”获取到“NTuple”的元素?

Nic*_*rca 5 julia

说我有一个Cchar喜欢的元组

str = ('f', 'o', 'o', '\0', '\0')
Run Code Online (Sandbox Code Playgroud)

我想将其转换为更传统的字符串。如果strVector,我可以Ptr用它来创造和做各种各样的事情。我尝试了各种方法来传递str给、、 和 的方法pointer,但都没有成功,因为这些方法通常适用于数组而不是元组。有什么建议?PtrRefunsafe_string

注意:我真正拥有的是一个 C 结构,它看起来像

typedef struct foo {
    char str[FOO_STR_MAX_SZ];
    ...
} foo_t;
Run Code Online (Sandbox Code Playgroud)

其中 Clang.jl 包装为

struct foo_t
    str :: NTuple{FOO_STR_MAX_SZ, UInt8}
    ...
end
Run Code Online (Sandbox Code Playgroud)

我也玩过NTupleof Cchar(ie, Int8) 而不是UInt8,我也尝试使用SVector而不是NTuple。但是我仍然找不到Ptr从该str领域生成 a 的方法。我错过了什么吗?

张实唯*_*张实唯 3

既然你问了这个问题,我认为将其收集到数组中a = collect(x.str)并不是你所期望的答案......

即使是不可变的,您也可以使用ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), a)它来获取指针。然而,盲目使用它会产生一些令人困惑的结果:aa

julia> struct foo_t
         str::NTuple{2, UInt8}
       end

julia> a = foo_t((2, 3))
foo_t((0x02, 0x03))

julia> ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), a.str)
Ptr{Nothing} @0x00007f4302c4f670

julia> ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), a.str)
Ptr{Nothing} @0x00007f4302cc47e0
Run Code Online (Sandbox Code Playgroud)

我们从同一个对象得到了两个不同的指针!原因是,既然NTuple是immutable,编译器就会对它做很多“优化”,比如每次使用的时候都应对它。这就是为什么在源代码中明确禁止从不可变对象获取指针:

function pointer_from_objref(@nospecialize(x))
    @_inline_meta
    typeof(x).mutable || error("pointer_from_objref cannot be used on immutable objects")
    ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), x)
end
Run Code Online (Sandbox Code Playgroud)

但是,有几种解决方法。首先,由于表达式复制了元组,因此您可以避免此表达式并直接使用和a.str的地址计算它的地址。(1 表示是第一个字段)afieldoffset(typeof(a), 1)strfoo_t

julia> p = Ptr{UInt8}(ccall(:jl_value_ptr, Ptr{UInt8}, (Any,), a)) + fieldoffset(typeof(a), 1)
Ptr{UInt8} @0x00007f4304901df0

julia> p2 = Ptr{UInt8}(ccall(:jl_value_ptr, Ptr{UInt8}, (Any,), a)) + fieldoffset(typeof(a), 1)
Ptr{UInt8} @0x00007f4304901df0

julia> p === p2
true

julia> unsafe_store!(p, 5)
Ptr{UInt8} @0x00007f4304901df0

julia> a
foo_t((0x05, 0x03))
Run Code Online (Sandbox Code Playgroud)

现在可以了。但是,仍然有一些警告:当您尝试将代码包装在函数中时,它再次出错:

julia> mut!(a) = unsafe_store!(Ptr{UInt8}(ccall(:jl_value_ptr, Ptr{UInt8}, (Any,), a)) + fieldoffset(typeof(a), 1), 8)
mut! (generic function with 1 method)

julia> mut!(a)
Ptr{UInt8} @0x00007f42ec560294

julia> a
foo_t((0x05, 0x03))
Run Code Online (Sandbox Code Playgroud)

a不会改变,因为foo_t它本身也是不可变的,并且将被复制到mut!,因此函数内所做的更改在外部不可见。为了解决这个问题,我们需要包装a一个可变对象,为其在堆中提供一个稳定的地址。Base.RefValue可以用于此目的:

julia> b = Base.RefValue(a)
Base.RefValue{foo_t}(foo_t((0x05, 0x03)))

julia> mut!(b) = unsafe_store!(Ptr{UInt8}(ccall(:jl_value_ptr, Ptr{UInt8}, (Any,), b)) + fieldoffset(typeof(b), 1) + fieldoffset(typeof(a), 1), 8)
mut! (generic function with 1 method)

julia> mut!(b)
Ptr{UInt8} @0x00007f43057b3820

julia> b
Base.RefValue{foo_t}(foo_t((0x08, 0x03)))

julia> b[]
foo_t((0x08, 0x03))
Run Code Online (Sandbox Code Playgroud)