关于C#如何使用julia的jl_call传递多个参数到julia函数中

julia版本已经到了1.8.5版本了,但是觉得它的生态还是不够好,很多问题都找不到答案。

在C#调用julia方面,目前已实现了

jl_call1
jl_call2
jl_call3
jl_init__threading
jl_eval_string
jl_unbox_float64
jl_box_float64
jl_atexit_hook

的使用。

此前搜索到前辈们留下的一个问题,http://cn.voidcc.com/question/p-dhvlazdi-ux.html
(将Julia脚本嵌入到C#中:jl_get_function不存在? - VoidCC)

我发现他的第1个和第2个结果是错误的,我把

public static extern IntPtr jl_box_float64(float value);

改为(float => double)

public static extern IntPtr jl_box_float64(double value);

得到的结果就正常了。

但是以下遇到的问题仍未解决.
julia脚本如下:

module JuliaClass
	export calculate

	function calculate(a::Float64, b::Float64)::Float64
		return a * pi + b^2
	end

	function calcMore(a, b)
		return ones(a, b)::Array{Float64,2};
	end

	function calc3par(a, b, c)
		return (a + b + c)::Float64;
	end

	function calcArray(a::Array{Float64,1})
		return (a[0] + a[1] + a[2])::Float64;
	end
end

C#脚本如下:


        //====================================================================
		[DllImport("kernel32.dll")]
		static extern bool SetDllDirectory(string lpPathName);

		[DllImport("libjulia.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
		public static extern void jl_init__threading(string path);

		[DllImport("libjulia.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr jl_eval_string(string input);

		[DllImport("libjulia.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr jl_box_float64(double value);

		[DllImport("libjulia.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
		public static extern double jl_unbox_float64(IntPtr value);

		[DllImport("libjulia.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr jl_get_global(IntPtr func, string name);

		[DllImport("libjulia.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr jl_call(IntPtr func, IntPtr[] v1, int v2);

		[DllImport("libjulia.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr jl_call1(IntPtr func, IntPtr v1);

		[DllImport("libjulia.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr jl_call2(IntPtr func, IntPtr v1, IntPtr v2);

		[DllImport("libjulia.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr jl_call3(IntPtr func, IntPtr v1, IntPtr v2, IntPtr v3);

		[DllImport("libjulia.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
		public static extern void jl_atexit_hook(int a);
		//====================================================================
		
        private void button_CsCallJulia_Click(object sender, EventArgs e)
		{
			///加载julia环境路径
			string julia_home_dir = Application.StartupPath + "\\Julia-1.8.5\\bin";//我的WinForm是32位的,所以安装的是32位的julia
			SetDllDirectory(julia_home_dir);
			jl_init__threading(julia_home_dir);
			///加载julia脚本路径
			string jl_ScriptPath = "./JuliaScript/test_julia.jl";
			string jl_include = "include(\"" + jl_ScriptPath + "\")";
			///等效于julia>include("./JuliaScript/test_julia.jl")
			IntPtr jl_DoString = jl_eval_string(jl_include);
			///执行julia脚本中JuliaClass模块的calculate函数
			IntPtr jl_ret = jl_eval_string("JuliaClass.calculate(2.0,4.0)");///julia函数返回值给C#中的IntPtr类型
			double cs_val = jl_unbox_float64(jl_ret);///julia拆箱转换为C#的double数据类型
			Console.WriteLine("result1={0}", cs_val);
			///C#传递参数给julia
			IntPtr jl_function = jl_eval_string("JuliaClass.calculate");
			IntPtr jl_par1 = jl_box_float64(3.0);///C#参数1装箱
			IntPtr jl_par2 = jl_box_float64(4.0);///C#参数2装箱
			jl_ret = jl_call2(jl_function, jl_par1, jl_par2);///传入到julia指定模块中的函数,以及参数,注:jl_call2只能传递1个函数和2个参数
			cs_val = jl_unbox_float64(jl_ret);///julia拆箱转换为C#的double数据类型
			Console.WriteLine("result2={0}", cs_val);

			jl_function = jl_eval_string("JuliaClass.calc3par");
			jl_par1 = jl_box_float64(12.0);
			jl_par2 = jl_box_float64(13.0);
			IntPtr jl_par3 = jl_box_float64(14.0);
			jl_ret = jl_call3(jl_function, jl_par1, jl_par2, jl_par3);
			cs_val = jl_unbox_float64(jl_ret);
			Console.WriteLine("result3={0}", cs_val);

        }

问题就是:
1.我不知道如何在C#里通过jl_call传递一个数组到julia中的calcArray函数,
我尝试->

			jl_function = jl_eval_string("JuliaClass.calcMore");
			IntPtr[] jl_par = new IntPtr[3] { (IntPtr)11, (IntPtr)12, (IntPtr)13 };
			jl_ret = jl_call(jl_function, jl_par, 3);
			Console.WriteLine("result4={0}", jl_ret);

运行这段代码它报错如下:

result1=22.2831853071796
result2=25.4247779607694
result3=39

引发的异常:“System.AccessViolationException”(位于 RobotTestingAPP.exe 中)
“System.AccessViolationException”类型的未经处理的异常在 RobotTestingAPP.exe 中发生
尝试读取或写入受保护的内存。这通常指示其他内存已损坏。
因此我不知道在C#中如何正确使用jl_call,搜索全网找不到答案!
2.如何在C#中使用jl_base_module等函数,如果是用C++调用,则是很方便,但我对C++并不了解。

这里搞过这个的估计很少,尽量用mmap zeromq之类的吧