jl_get_global can not get julia function pointer from Python to Julia
Hello,
I try to use PyJulia API to get and call Julia function, but it can not get function pointer. The python I use is 3.7.4, Julia is 1.4.2 and PyJulia is 0.5.3.
The test python code is
from julia.api import LibJulia
api = LibJulia.load()
api.init_julia()
ret = api.jl_eval_string(b"sqrt(2.0)"); # OK
print(api.jl_unbox_float64(ret)) # OK
base = api.jl_eval_string(b"Base")
sqrt = api.jl_symbol(b"sqrt")
func_sqrt = api.jl_get_global(base, sqrt)
print(func_sqrt) # always 0 here
ret = api.jl_call1(func_sqrt, api.jl_box_float64(2.0)) #fail to call the function. The error is
# Traceback (most recent call last):
# File "testPyJulia.py", line 13, in <module>
# ret = api.jl_call1(func_sqrt, api.jl_box_float64(2.0))
# ctypes.ArgumentError: argument 1: <class 'TypeError'>: Don't know how to convert parameter 1
print(api.jl_unbox_float64(ret))
This is probability because that LibJulia does not declare the argtypes for jl_get_global and jl_call1 etc. Also, if func_sqrt is null I guess you'd need to handle the error.
But don't think this is an easy path. I'm not sure if it's possible to safely use Julia's C API without C macros. One workaround would be to use @cfunction on the Julia side to expose a Julia function as a plain C function.
This is probability because that
LibJuliadoes not declare theargtypesforjl_get_globalandjl_call1etc. Also, iffunc_sqrtis null I guess you'd need to handle the error.But don't think this is an easy path. I'm not sure if it's possible to safely use Julia's C API without C macros. One workaround would be to use
@cfunctionon the Julia side to expose a Julia function as a plain C function.
Thanks. @tkf
The issue is that api.jl_get_global can not get the function from Julia module in Python. But in C/C++, jl_get_global can get the function from the same Julia module as I tried successfully in the following C/C++ code.
#include <iostream>
#include <julia.h>
int main()
{
jl_init();
jl_module_t* mod_base = (jl_module_t *)jl_eval_string("Base");
jl_function_t *func_sqrt = jl_get_global(mod_base, jl_symbol("sqrt"));
std::cout << "test_sqrt ptr: " << func_sqrt << "\n"; // func_sqrt pointer is got
jl_value_t *ret = jl_call1(func_sqrt, jl_box_float64(2.0));
{
JL_GC_PUSH1(&ret);
std::cout << "test_sqrt: " << jl_unbox_float64(ret) << "\n";
JL_GC_POP();
}
return 0;
}
As I said, you need to declare argtypes/restype (because some C APIs are not defined by LibJulia):
from ctypes import c_char_p, c_void_p, c_double
from julia.api import LibJulia
api = LibJulia.load()
api.init_julia()
api.jl_symbol.argtypes = [c_char_p]
api.jl_symbol.restype = c_void_p
api.jl_get_global.argtypes = [c_void_p, c_void_p]
api.jl_get_global.restype = c_void_p
api.jl_symbol.restype = c_void_p
api.jl_box_float64.argtypes = [c_double]
api.jl_box_float64.restype = c_void_p
api.jl_call1.argtypes = [c_void_p, c_void_p]
api.jl_call1.restype = c_void_p
base = api.jl_eval_string(b"Base")
sqrt = api.jl_symbol(b"sqrt")
func_sqrt = api.jl_get_global(base, sqrt)
print(func_sqrt)
ret = api.jl_call1(func_sqrt, api.jl_box_float64(2.0))
print(api.jl_unbox_float64(ret))
However, note that this is incorrect use of the Julia C API because there are no JL_GC_* calls.
It works. Thanks @tkf