-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The cfunction
1.0 deserves
#26486
The cfunction
1.0 deserves
#26486
Conversation
# NOTE: don't use @check/GitError directly, because the code can be arbitrary | ||
payload = Any[ tree, f ] | ||
cbf = @cfunction(function treewalk_callback(root_cstr::Cstring, entry_ptr::Ptr{Cvoid}, payload::Vector{Any})::Cint |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it possible to capture tree
and f
instead of using the payload
mechanism?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's just slower that way than to just use the C API correctly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I presume it's slower because closure support requires a stub that sets the locals and jumps to the "real" function? Whereas without any closed-over variables, it can just call directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, and in addition to the indirect function call, it also requires allocation to be able to track the lifetime, and runtime work to manage and handle that, etc.
base/c.jl
Outdated
@cfunction(callable, ReturnType, (ArgumentTypes...,)) -> Ptr{Cvoid} | ||
@cfunction(\$callable, ReturnType, (ArgumentTypes...,)) -> CFunction | ||
|
||
Generate a C-callable function pointer from the Julia function `f` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no f
:)
base/exports.jl
Outdated
@@ -925,6 +925,7 @@ export | |||
|
|||
# C interface | |||
cfunction, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this?
## Closure cfunctions | ||
|
||
The first argument to [`@cfunction`](@ref) can be marked with a `$`, in which case | ||
the return value will instead be a `struct CFunction` which closes over the argument. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity: what happens if I don't use a $
, but I give a global name that's bound to a non-singleton function object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like ccall
, it's resolved by the macro to close over the value (not the binding), since that's almost always what you want anyways, and makes it easier to avoid accidentally shooting your self in the (gc) foot.
I think the |
de8ea4a
to
e0d3951
Compare
Not really. Because I felt that it read nicely to keep the function definition and use together this way, I'm (ab)using the knowledge that it got evaluated in global scope early, and then treated as a constant. In either case, it can be an arbitrary expression, the |
+1 for |
@@ -182,26 +185,26 @@ an integer less/greater than zero if `a` should appear before/after `b` (or zero | |||
is permitted). Now, suppose that we have a 1d array `A` of values in Julia that we want to sort | |||
using the `qsort` function (rather than Julia's built-in `sort` function). Before we worry about | |||
calling `qsort` and passing arguments, we need to write a comparison function that works for some | |||
arbitrary type T: | |||
arbitrary type T (which defines `<`): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
T
isn't used in the example anymore.
end | ||
mycompare (generic function with 1 method) | ||
``` | ||
|
||
Notice that we have to be careful about the return type: `qsort` expects a function returning | ||
a C `int`, so we must be sure to return `Cint` via a call to `convert` and a `typeassert`. | ||
a C `int`, so we annotate the return type of the function be sure it returns a `Cint`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing "to"?
@vtjnash: Any chance that this will fix https://discourse.julialang.org/t/performance-issue-with-gtk-jl/6171. That inference on callbacks requires up to minutes is a major issue when using Gtk currently. |
4380acd
to
dffb8cc
Compare
dffb8cc
to
bd386ff
Compare
macro head uses this to resolve symbols, which we want to allow even in pure context (generated functions)
Provide static support for handling dynamic calls and closures
bd386ff
to
81f6524
Compare
Did I miss it or this change isn't mentioned in |
f::Any | ||
_1::Ptr{Cvoid} | ||
_2::Ptr{Cvoid} | ||
let construtor = false end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/construtor/constructor/
(not that it matters, since this variable is not visible) … a comment explaining the purpose of this line would be helpful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It disables creation of the default constructors (ensuring that this type is not accidentally constructible from user code).
This PR turns
cfunction
into a macro, and has it work more similarly toccall
(no runtime compiler required, defined by the containing method parameters, supports any type object including closures). In most cases, v0.6 code should just need to add an@
. But it also has new advanced capabilities to efficiently create gc-managed runtime closures pointers! This will fix #12256, fix #26070 and close #1096 (I feel slightly cheated to not get to close a 3-digit issue on this one 😜).Implements https://github.com/vtjnash/julep/wiki/0004:-Static-compilation-for-cfunction
The best place to see the net effect of this change is perhaps the stdlib, but also the docs (where I have an example of qsort), or the Julep (which has a more complete exploration of qsort usages), or lastly perhaps the tests (which try to have examples of weird uses of almost everything).
Syntax questions:Creating a closure over the value of a local variable requires annotating with$
: i.e.ptr = @cfunction $cl RT (A, B)
. However, I've also considered instead usinglocal
for this purpose instead:ptr = @cfunction local cl RT (A, B)
. Thoughts on which one reads better? As can be seen from there being zero usages in this repo (outside of tests), this feature isn't a particularly common need / the other form is more preferred and easier to use (when possible), so I don't really care either way.Should I continue to use basically the same syntax as before (i.e. just add@
), or use the following alternate syntax rearrangement, or just try to provide both:ptr = @cfunction cl(A, B)::RT
(which, for the variant that creates a closure over the value of the local variablecl
, would beptr = @cfunction $cl(A, B)::RT
orptr = @cfunction local cl(A, B)::RT
)