Clang.jl icon indicating copy to clipboard operation
Clang.jl copied to clipboard

Add support for automatically calling unsafe_load() in getproperty()

Open JamesWrigley opened this issue 7 months ago • 30 comments

Copying the description from the code:

By default the getproperty!(x::Ptr, ::Symbol) methods created for wrapped types will return pointers (Ptr{T}) to the struct fields. That behaviour is useful for accessing nested struct fields but it does require explicitly calling unsafe_load() every time. When enabled this option will automatically call unsafe_load() for you except on nested struct fields and arrays, which should make explicitly calling unsafe_load() unnecessary in most cases.

Here's the generated code for the struct-properties.h header in the tests:

using CEnum: CEnum, @cenum                                                                                                                                                                                                           [75/97867]
                                                                                                                                                                                                                                               
struct TypedefStruct                                                                                                                                                                                                                           
    i::Cint                                                                                                                                                                                                                                    
end                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                               
struct Other                                                                                                                                                                                                                                   
    i::Cint                                                                                                                                                                                                                                    
end                                                                                                                                                                                                                                            
function _getptr(x::Ptr{Other}, f::Symbol)                                                                                                                                                                                                     
    f === :i && return Ptr{Cint}(x + 0)                                                                                                                                                                                                        
    error("Unrecognized field of type `Other`" * ": $(f)")                                                                                                                                                                                     
end                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                               
function Base.getproperty(x::Ptr{Other}, f::Symbol)                                                                                                                                                                                            
    f === :i && return unsafe_load(_getptr(x, f))                                                                                                                                                                                              
    return getfield(x, f)                                                                                                                                                                                                                      
end                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                               
function Base.setproperty!(x::Ptr{Other}, f::Symbol, v)                                                                                                                                                                                        
    unsafe_store!(_getptr(x, f), v)                                                                                                                                                                                                            
end                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                               
mutable struct WithFields                                                                                                                                                                                                                      
    int_value::Cint                                                                                                                                                                                                                            
    int_ptr::Ptr{Cint}                                                                                                                                                                                                                         
    struct_value::Other                                                                                                                                                                                                                        
    struct_ptr::Ptr{Other}                                                                                                                                                                                                                     
    typedef_struct_value::TypedefStruct                                                                                                                                                                                                        
    array::NTuple{2, Cint}                                                                                                                                                                                                                     
end                                                                                                                                                                                                                                            
function _getptr(x::Ptr{WithFields}, f::Symbol)
    f === :int_value && return Ptr{Cint}(x + 0)
    f === :int_ptr && return Ptr{Ptr{Cint}}(x + 8)
    f === :struct_value && return Ptr{Other}(x + 16)
    f === :struct_ptr && return Ptr{Ptr{Other}}(x + 24)
    f === :typedef_struct_value && return Ptr{TypedefStruct}(x + 32)
    f === :array && return Ptr{NTuple{2, Cint}}(x + 36)
    error("Unrecognized field of type `WithFields`" * ": $(f)")
end

function Base.getproperty(x::Ptr{WithFields}, f::Symbol)
    f === :int_value && return unsafe_load(_getptr(x, f))
    f === :int_ptr && return unsafe_load(_getptr(x, f))
    f === :struct_value && return _getptr(x, f)
    f === :struct_ptr && return unsafe_load(_getptr(x, f))
    f === :typedef_struct_value && return _getptr(x, f)
    f === :array && return _getptr(x, f)
    return getfield(x, f)
end

function Base.setproperty!(x::Ptr{WithFields}, f::Symbol, v)
    unsafe_store!(_getptr(x, f), v)
end

I've also tested it with CImGui.jl: https://github.com/Gnimuc/CImGui.jl/pull/131 I will admit the code is a bit hairy :octopus: Not sure if it's the cleanest implementation.

EDIT: marked as a draft because JET.jl found some issues with it.

JamesWrigley avatar Jul 06 '24 19:07 JamesWrigley