说我有一个Cchar喜欢的元组
str = ('f', 'o', 'o', '\0', '\0')
Run Code Online (Sandbox Code Playgroud)
我想将其转换为更传统的字符串。如果str是Vector,我可以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 的方法。我错过了什么吗?
既然你问了这个问题,我认为将其收集到数组中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)