Hi all. I have a large number of types that I need to implement in Julia that correspond to some C/CPP created memory. I’m able to use ccall
to allocate the memory and the way this library is set up is that ccall
then returns a pointer to the memory address. This is all well and good but I don’t want users dealing with the pointer to the address and have decided having types in Julia that correspond to these various memory allocations is a good way to make a nice interface.
abstract type CWrapper end
Base.convert(T::Type{<:CWrapper}, p::Ptr{Nothing}) = T(p)
Base.unsafe_convert(T::Type{Ptr{Nothing}}, t::CWrapper) = t.ptr
struct CppClass1 <: CWrapper
ptr::Ptr{Nothing}
end
struct CStruct1 <: CWrapper
ptr::Ptr{Nothing}
end
struct CppClass2 <: CWrapper
ptr::Ptr{Nothing}
end
struct CStruct2 <: CWrapper
ptr::Ptr{Nothing}
end
This works really well because now I can write functions that prevent incorrect pointers being passed to the C library by strict typing
function createCppClass1(x::Int)
ptr = ccall((:createCppClass1, LIBRARY), Ptr{CVoid}, (CInt), x)
return CppClass1(ptr)
end
function someOperationOnCppClass1(o::CppClass1)
ccall((:someOperationOnCppClass1, LIBRARY), Cint, (Ptr{CVoid}, ), o)
end
This makes for a nice safer interface because now for example, users will not be able to call someOperationOnCppClass1
by passing in an argument of CppClass2
.
The C library in question occasionally may fail to allocate memory and may return a null pointer. If this happens, I want to raise an error. I initially wanted to use an inner constructor for this. However, I have the exact same code being used and was wondering if there was a nicer way to do this.
abstract type CWrapper end
Base.convert(T::Type{<:CWrapper}, p::Ptr{Nothing}) = T(p)
Base.unsafe_convert(T::Type{Ptr{Nothing}}, t::CWrapper) = t.ptr
struct CppClass1 <: CWrapper
ptr::Ptr{Nothing}
function CppClass1(ptr::Ptr{Nothing})
ptr == C_NULL && error("Received null pointer from C interface. Something went wrong")
new(ptr)
end
end
struct CStruct1 <: CWrapper
ptr::Ptr{Nothing}
function CStruct1(ptr::Ptr{Nothing})
ptr == C_NULL && error("Received null pointer from C interface. Something went wrong")
new(ptr)
end
end
struct CppClass2 <: CWrapper
ptr::Ptr{Nothing}
function CppClass2(ptr::Ptr{Nothing})
ptr == C_NULL && error("Received null pointer from C interface. Something went wrong")
new(ptr)
end
end
struct CStruct2 <: CWrapper
ptr::Ptr{Nothing}
function CStruct2(ptr::Ptr{Nothing})
ptr == C_NULL && error("Received null pointer from C interface. Something went wrong")
new(ptr)
end
end
Is there a way to write a common constructor for all structs that are a subtype of a specific abstract type? If I have 50 or more types that I want to define I don’t want to write the same inner constructor 50 or so times. It seems like an awful lot of code repetition. Macros would be one way to do it but I’m wondering if there’s a way to do it without macros? I may have some additional unique constraints to pose for a few of the inner constructors for some of the types, and I don’t think I can generalize those easily using macros.