Я встраиваю свое приложение в 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
Тем не менее, я пытаюсь сделать что-то подобное в Джулии, а Джулия всегда просто делает копии данных и не может изменить адрес внешней памяти с 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
Как я должен в Юлии делиться адресами памяти структур с C?
Хорошо, я нашел хороший выход, который не кажется слишком неловким. Я в основном перегружаю []
оператор для доступа к полям структуры с помощью символов, например:
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
Это правда, но вы можете использовать свою «измененную» копию для копирования по указанному указателю / адресу. Используя ваш подход, добавленный шаг будет:
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)