I have a use case, where code needs to generate multiple sets of functions.
So functions do not get overwritten, I want to add a unique pre-/postfix equal for each function in a set.
Specifically, functions
init()
run()
…
are created for every set.
How do I name all functions in the nth set of functions
init_n()
run_n()
…
Suppose, the definition of functions stays the same, I just want to change their names.
This probably falls into the area of macro programming but I could not find an illustrative example that would have helped me.
I want such functionality:
a = “Function_name” @macro a()
println("Hello)
end
Function_name()
Hello
I would be happy for any pointer you may be able to provide.
You could do something like this, but such module surgery is not necessarily recommended (I am pretty confident there is a better way to achieve your goals without doing such things):
function rename_function!(f, newname::Symbol)
eval(:(global $(newname) = $f))
# without renaming here also -
# the function name will still be print/shown with the old name
typeof(eval(newname)).name.mt.name = newname
end
function fgenerator()
eval(:(init_1() = "initial name: init_1"))
end
fgenerator()
@info names(Main) # init_1 already defined
rename_function!(init_1, :init_2)
@info names(Main) # init_2 defined, init_1 printing as init_2
@info init_2() # prints "initial name: init_1"
# shown as init_2
@info init_2
# also shown as init_2
@info init_1
# revert
rename_function!(init_2, :init_1)
# shown as init_1
@info init_2
# also shown as init_1
@info init_1
At this point, I am unaware of any method to delete a name once defined.
Again, while the above code is possible, it is not something that I would recommend doing.
a = Symbol("function_name")
@eval function $a()
println("Hello")
end
julia> function_name()
Hello
This is often used for things like programmatically generating wrappers for external libraries (look at the BLAS and LAPACK wrappers in the LinearAlgebra standard library).
This does not seem like a use case for eval. Dynamically generating identifiers/names is universally discouraged in every programming language. (Google “eval is evil” or something like that).
Use anonymous functions. Your use case is not very clear, but you could for example collect them in a vector, and call them as init[n]().
Making many functions with the exact same body means you’re going to compile their function calls to identical native code many times, which is overhead to be avoided if possible. What is a set here, and why do they need init, run, etc each?
To expand on what others have said, a vector of functions could be a much better alternative than creating a set of similar functions with generated names. It could look like that:
julia> const init = map(1:5) do n
function () # Anonymous function but will be available as init[n]
println("Hello, I'm function $n")
end
end
5-element Vector{var"#6#8"{Int64}}:
[...]
julia> init[3]()
Hello, I'm function 3
Thank you for bringing this up. I definitely see massive issues server-side exposing input to eval to a user - similar to SQL injection. Client-side it’s probably only additional overhead thats a downside, right?
If the C API is properly designed, however, this should not be needed — properly designed C APIs that accept a function pointer should also take a void* argument that is passed through to your function, in order to simulate closures (as otherwise the code cannot be re-entrant). In this case you can use the void* to pass through an anonymous function to a thin static wrapper routine, as e.g. explained here (somewhat old, but the basic principle still holds).
(And if the C API is not re-entrant, then you might as well make your Julia callback non-re-entrant too. Just write a thin static wrapper that calls your “actual” callback stashed in a global.)
Yes, I just tried to make a concise problem statement. In fact, a slight change may be required in any of the generated functions’ bodies - but not enough to make writing them by hand efficient.
Thanks for mentioning the performance hit. This is indeed a concern. In my case only 2-3 sets may have to be generated at the beginning of execution - which should not be too bad.
You might use the package to design the runtime-changeable parts of your functions and store the dynamic expressions in a Ref (so you don’t need to recompile the calling function).
In that way, you might be able to have functions that behave like the function body was changed (e.g., operating on the Ref containing the dynamic expressions from outside the function) without actually having a function body change (and without performance hit).
This might not be out of the box 100% compatible with your use case - but if performance is paramount, you can design your project around it.
Any way to make that slight change a method argument or a field of a callable instance? Those won’t need you to generate multiple methods to compile separately, but if those changes mean different types then the same method will be compiled many ways, which doesn’t save much compilation time but does trim down the number of functions.