JuliaInterop / ObjectiveC.jl

Objective-C embedded in Julia
Other
39 stars 10 forks source link

an objc_msgSend() macro #8

Closed dognotdog closed 1 year ago

dognotdog commented 8 years ago

While mucking about with how to do some GUI stuff for Julia on OSX, while I was having issues with ObjectiveC.jl, I've managed, somehow bending metaprogramming to my will, to come up with a macro version of objc_msgSend() with multiple args that doesnt rely on ccal(), and should produce a ccall() with no runtime overhead.

Maybe ObjectiveC.jl can benefit from this voodoo.

Its curious to note that

UP = (P...,U...)
ccallTypes = parse("$UP")

works in the REPL, if U,P are set to the appropriate datatype-tuples, and produce the same s-expr as

UU = parse("$U")
PP = parse("$P")
Expr(PP.head, PP.args..., UU.args...)

but not when put in the body of the macro, which generates a useless error.

Alas, this works:

# functions binding objc runtime

function SEL(aString::Union{ASCIIString,UTF8String})
  ccall(:sel_getUid, Ptr{Void}, (Ptr{UInt8},), aString)
end

# this objc_msgSend taken from Tk.jl
objc_msgSend{T}(id, uid, ::Type{T}=Ptr{Void}) = ccall(:objc_msgSend, T, (Ptr{Void}, Ptr{Void}), id, SEL(uid))

macro objc_msgSend(T, U, id, methodName::Union{ASCIIString,UTF8String}, args...)
  # the objc_msgSend types for self and selector
  P = (Ptr{Void},Ptr{Void})

  # re-parse the stringified type declarations
  UU = parse("$U")
  PP = parse("$P")
  # manually merge tuples
  ccallTypes = Expr(PP.head, PP.args..., UU.args...)

  # conatenating U and P via (P...,U...) does not seem to work in macro world (julia v0.4.1)

  rval = :(ccall(:objc_msgSend, $T, $ccallTypes, $id, SEL($methodName), $(args...)))
  # rval = Expr(:call, :ccall, :(:objc_msgSend), T, ccallTypes, id, :(SEL($methodName)), args...)
  # @show rval
  # println("\nsexpr(rval)")
  # Meta.show_sexpr(rval)
  # println("")

  return rval
end

# void objc_msgSend_stret(void * stretAddr, id theReceiver, SEL theSelector, ...)
function objc_msgSend_stret{T}(self, methodName, ::Type{T})
  rval = T()
  ccall(:objc_msgSend_stret, Void, (Ptr{T}, Ptr{Void}, Ptr{Void}), Ref(rval), self, SEL(methodName))
  return rval
end

usage like so:

function objcString(aString::Union{ASCIIString,UTF8String})
  oString = objc_msgSend(objc_getClass("NSString"), "alloc")
  oString = @objc_msgSend(Ptr{Void}, (Ptr{UInt8},), oString, "initWithUTF8String:", aString)
  oString = objc_msgSend(oString, "autorelease")
  return oString
end
maleadt commented 1 year ago

I did something like this in #11, so I think we can close this issue.