Julia 字符串文字的生命周期是多少

时间:2021-07-10 03:49:30

标签: julia

例如,在 Julia 会话结束之前使用 col0 col1 col2 0 0 1 -10.5 1 3 4 -7.5 2 6 7 -4.5 3 9 10 -1.5 4 12 13 1.5 5 15 16 4.5 6 18 19 7.5 7 21 22 10.5 返回的指针是否安全?在这种情况下,foofoo 之间没有区别,对吗?

bar
foo(s::String="foo") = Base.unsafe_convert(Ptr{UInt8}, Base.cconvert(Ptr{UInt8}, s))

编辑:

我想添加更多背景信息。我正在尝试连接 C++ 库中的一些方法,该库具有默认为 C++ 字符串文字的默认参数值。派生指针用作“引用”(请参阅​​下面的链接)。在 C++ 中只使用文字似乎没问题,我想知道在 Julia 中这样做的最佳方法是什么。 (我认为 bar() = Base.unsafe_convert(Ptr{UInt8}, Base.cconvert(Ptr{UInt8}, "foo")) 不是一个好的解决方案,因为很难知道何时可以安全地对资源进行 GC。)

外部参照:Is it safe to init `StringRef` with a string literal?

2 个答案:

答案 0 :(得分:1)

从文档来看,答案似乎是一个相当明确的“否”。

首先,正如您可能知道的(并且确实可能​​从名称中猜到),Base.unsafe_convert 在这方面没有提供安全性,带有文档字符串警告

<块引用>

小心确保 Julia 对 x 的引用存在,只要此函数的结果将被使用

仅凭指针的存在肯定不会提供这一点。更糟糕的是,作为 Base.cconvert 注释

的文档字符串 <块引用>

convert 和 cconvert 都不应将 Julia 对象转换为 Ptr。

将 Julia 对象转换为 Ptr 的推荐方法是什么?这似乎是 Base.pointer,它在其文档中提供了更多有用的信息

help?> Base.pointer
  pointer(array [, index])

  Get the native address of an array or string, optionally at a given location index.

  This function is "unsafe". Be careful to ensure that a Julia reference to array exists
  as long as this pointer will be used. The GC.@preserve macro should be used to protect
  the array argument from garbage collection within a given block of code.

  Calling Ref(array[, index]) is generally preferable to this function as it guarantees
  validity.

值得注意的是,

返回的指针
julia> s = "some string"
"some string"

julia> Base.pointer(s)
Ptr{UInt8} @0x000000010d4b2838

julia> Base.unsafe_convert(Ptr{UInt8}, s)
Ptr{UInt8} @0x000000010d4b2838

julia> Base.unsafe_convert(Ptr{UInt8}, Base.cconvert(Ptr{UInt8}, s))
Ptr{UInt8} @0x000000010d4b2838

完全相同。

可以依次在 Base.GC.@preserve 文档字符串中找到更多有用的信息:

help?> Base.GC.@preserve
  GC.@preserve x1 x2 ... xn expr

  Mark the objects x1, x2, ... as being in use during the evaluation of the expression
  expr. This is only required in unsafe code where expr implicitly uses memory or other
  resources owned by one of the xs.

  Implicit use of x covers any indirect use of resources logically owned by x which the
  compiler cannot see. Some examples:

    •  Accessing memory of an object directly via a Ptr

    •  Passing a pointer to x to ccall

    •  Using resources of x which would be cleaned up in the finalizer.

  @preserve should generally not have any performance impact in typical use cases where
  it briefly extends object lifetime. In implementation, @preserve has effects such as
  protecting dynamically allocated objects from garbage collection.

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  When loading from a pointer with unsafe_load, the underlying object is implicitly
  used, for example x is implicitly used by unsafe_load(p) in the following:

  julia> let
             x = Ref{Int}(101)
             p = Base.unsafe_convert(Ptr{Int}, x)
             GC.@preserve x unsafe_load(p)
         end
  101

  When passing pointers to ccall, the pointed-to object is implicitly used and should be
  preserved. (Note however that you should normally just pass x directly to ccall which
  counts as an explicit use.)

  julia> let
             x = "Hello"
             p = pointer(x)
             Int(GC.@preserve x @ccall strlen(p::Cstring)::Csize_t)
             # Preferred alternative
             Int(@ccall strlen(x::Cstring)::Csize_t)
         end
  5

所以,一些带回家的点

  1. Base.pointer(s) 返回的指向字符串的指针是安全的,当且仅当 (a) 后面的代码中存在对该字符串 sJulia 引用或 (b) 您使用过 Base.GC.@preserve
  2. Base.unsafe_convert(Ptr{UInt8}, Base.cconvert(Ptr{UInt8}, s)) 返回的指针似乎与 Base.pointer 返回的指针具有相同的警告,但额外增加了一个神秘而不祥的警告。
  3. 没有迹象表明 Julia 对象的类型(String vs Array vs 等等)很重要。

答案 1 :(得分:0)

垃圾收集器可以使您的函数返回的引用无效,如果它们被垃圾收集器重新定位然后被引用。所以不要那样做。如果必须,您可以将代码包装在 GC.@preserve 宏中以将它们标记为不可重定位。注意我在使用不透明的 C 编译库时遇到过一些奇怪的情况,即使那样你也会使 Julia 崩溃,所以如果可以,请避免这种引用。宏就是这样完成的,见https://docs.julialang.org/en/v1/base/base/#Base.GC.@preserve

GC.@preserve x y begin
    x = foo()
    y = bar()
    @show x

# do stuff that might fire off the garbage collector ...

    @show x
end

即使重新定位字符串“foo”本身也可以毫无顾虑地被引用,因为 Julia 总是知道任何给定的字符串 literal 在哪里。

相关问题