【问题标题】:Read/write struct data with Julia of memory address from C使用来自 C 的内存地址的 Julia 读取/写入结构数据
【发布时间】:2017-06-19 20:17:26
【问题描述】:

我将我的应用程序嵌入到 Julia 中,我需要一种从 Julia 和 C++ 读取/写入相同结构的好方法。

在 Python 中我可以简单地做:

ffi.cdef("""
    struct keyboard_s {
        int forward;
        int backward;
        int left;
        int right;
        int jump;
    }

    struct keyboard_s *app_get_keyboard();
"""

app = ffi.dlopen("app.dll")

thekeyboard = app.app_get_keyboard();

thekeyboard.forward = 1; # this would immediatly change the memory in C

但是,我在 Julia 中尝试了类似的操作,而 Julia 总是只复制数据并且无法从 C 中更改外部内存地址:

type keyboard_s
    forward::Int32
    backward::Int32
    left::Int32
    right::Int32
    jump::Int32
end

# lets imply this would return the memory struct just like app_get_keyboard()
# I just use malloc(sizeof(keyboard_s)) so everybody here can test for themselves...
address = ccall(:malloc, (Int64), (Int64, ), sizeof(keyboard_s))

# address is now a valid Int64 address, so lets map it as pointer of type keyboard_s
ptr = Ptr{keyboard_s}(address)

# thekeyboard contains now the random data from the c static memory
thekeyboard = unsafe_load(ptr)

# this will change only the value of "thekeyboard",
# it doesn't touch C the Int64 address memory pointer...
thekeyboard.forward = 123 # this has no effect on the real memory address :(

# lets load the keyboard again from same address
thekeyboard = unsafe_load(ptr)

thekeyboard.forward == 123 # this is false! no effect whatsoever in C memory from Julia

在 Julia 中我应该如何与 C 共享结构的内存地址?

【问题讨论】:

    标签: c++ pointers julia ffi


    【解决方案1】:

    好的,我想出了一个不错的出路,不会觉得太尴尬。我基本上重载了[] 运算符以通过符号访问结构字段,如下所示:

    thekeyboard[:forward] = 123
    

    代码:

    function offsetof(type_, member::Symbol)
      for (i, item) in enumerate(fieldnames(type_))
        if item == member
          return fieldoffset(type_, i)
        end
        #print(typeof(i))
      end
      # what to do when symbol not in type_?
      throw("$type_ has no member named $member")
    end
    
    function GetStructType(type_, member::Symbol)
      for (i, item) in enumerate(fieldnames(type_))
        if item == member
          return fieldtype(type_, i)
        end
        #print(typeof(i))
      end
      # what to do when symbol not in type_?
      throw("$type_ has no member named $member")
    end
    
    function Base.getindex(ptr::Ptr{T}, s::Symbol) where {T}
      address = UInt(ptr)
      if address == 0
        throw("Base.getindex(Ptr::{$T}) would dereference a NULL pointer")
      end
      offset = offsetof(T, s)
      fieldtype = GetStructType(T, s)
      fieldptr = Ptr{fieldtype}(address + offset)
      #log("Symbol $s $ptrtype address=$address offset=$offset fieldtype=$fieldtype ptr=$ptr fieldptr=$fieldptr\n")
      #return 123
      return unsafe_load(fieldptr)
    end
    
    function Base.setindex!(ptr::Ptr{T}, value, s::Symbol) where {T}
      address = UInt(ptr)
      if address == 0
        throw("Base.setindex!(Ptr) would write to a NULL pointer")
      end
      offset = offsetof(T, s)
      fieldtype = GetStructType(T, s)
      fieldptr = Ptr{fieldtype}(address + offset)
      #log("Symbol $s $ptrtype address=$address offset=$offset fieldtype=$fieldtype ptr=$ptr fieldptr=$fieldptr\n")
      unsafe_store!(fieldptr, value)
      return value
    end
    

    【讨论】:

      【解决方案2】:

      确实如此,但是您可以使用“修改后的”副本在指定的指针/地址处进行复制。使用您的方法,添加的步骤将是:

      thekeyboard = unsafe_load(ptr)  #> keyboard_s(62752576, 0, 1836674671, 1601402223, 909193782)
      thekeyboard.forward = 123;       
      unsafe_store!(ptr, thekeyboard);
      thekeyboard = unsafe_load(ptr)  #> keyboard_s(123, 0, 1836674671, 1601402223, 909193782)
      

      【讨论】:

      • 是的,我知道,但我不想仅仅因为更新了一个字段值就复制整个结构。线程等也有问题。我刚刚完成了用于单个字段访问的自定义函数,稍后再发布。
      • 哦,我明白你的意思了。您想要一个共享模型,而不仅仅是访问。在这种情况下,是的,您可能必须将新值直接分配给内存中的类型字段。
      猜你喜欢
      • 2015-10-14
      • 2012-12-25
      • 1970-01-01
      • 2014-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-16
      • 2012-12-22
      相关资源
      最近更新 更多