pyjulia icon indicating copy to clipboard operation
pyjulia copied to clipboard

jl_get_global can not get julia function pointer from Python to Julia

Open storepos opened this issue 5 years ago • 4 comments

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))

storepos avatar Jun 22 '20 15:06 storepos

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.

tkf avatar Jun 23 '20 01:06 tkf

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.

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; 
}

storepos avatar Jun 23 '20 09:06 storepos

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.

tkf avatar Jun 24 '20 06:06 tkf

It works. Thanks @tkf

storepos avatar Jun 24 '20 09:06 storepos