说我有一个Cchar
元组,例如
str = ('f', 'o', 'o', '\0', '\0')
,我想将其转换为更传统的字符串。如果str
是Vector
,我可以创建Ptr
并用它做各种各样的事情。我尝试了各种方法将str
传递给pointer
,Ptr
,Ref
和unsafe_string
的方法,因为它们通常在数组而不是数组上工作,元组。有什么建议吗?
注意:我真正拥有的是一个看起来像C的结构
typedef struct foo {
char str[FOO_STR_MAX_SZ];
...
} foo_t;
其中Clang.jl包装为
struct foo_t
str :: NTuple{FOO_STR_MAX_SZ, UInt8}
...
end
我还玩了NTuple
中的Cchar
(即Int8
)而不是UInt8
,并且我尝试使用SVector
而不是{{ 1}}。但是我仍然找不到从NTuple
字段生成Ptr
的方法。我想念什么吗?
答案 0 :(得分:2)
自从您提出问题以来,我认为将其收集到数组a = collect(x.str)
并不是您所期望的答案...
即使ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), a)
不可变,也可以使用a
获取a
的指针。但是,盲目使用它会产生一些令人困惑的结果:
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
我们从同一个对象获得了两个不同的指针!原因是由于NTuple
是不可变的,因此编译器将对其进行许多“优化”,例如,每次使用它时都会对其进行处理。这就是为什么在source code中明确禁止从不可变对象获取指针的原因:
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
但是,有几种解决方法。首先,由于表达式a.str
复制了元组,因此您可以避免使用此expressoin并直接使用a
和fieldoffset(typeof(a), 1)
的地址来计算它的地址。 (1表示str
是foo_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))
现在可以使用了。但是,仍然有一些警告:当您尝试将代码包装到一个函数中时,它又变成了错误:
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))
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))
答案 1 :(得分:1)
如@张实唯所解释,str
是一个常量数组,它是堆栈分配的,因此您需要使用指针算术来访问该字段。为此,有一个名为Blobs.jl的软件包。至于可变性,为方便起见,您也可以使用Setfield.jl。
顺便说一句,Clang.jl支持通过ctx.options["is_struct_mutable"] = true
生成可变结构。