【请教】C#如何调用Julia?


#1

我是一名C#新手,尝试在C#里调用JL,全网搜索了很多资料,大部分都是英文的,并且JL的版本很低。我现在用的JL1.2版(64)位,参照其中的一个帖子(https://stackoverflow.com/questions/35992240/embedding-julia-in-c-sharp),将JL的安装目录修改为自己的,代码如下:

        [DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void jl_init(string julia_home_dir);

        [DllImport("libjulia.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void jl_eval_string(string str);

        public static void Julia()
        {
            // 下句出现错误:System.EntryPointNotFoundException:“无法在 DLL“libjulia.dll”中找到名为“jl_init”的入口点。”


            **jl_init(@"C:\JuliaPro-1.2.0-1\Julia-1.2.0\bin\");**


            jl_eval_string("print(sqrt(2.0))");

        }

补充:我也已经把JL的安装目录添加到了path。

也看了JL的文档和网上的一些案例,但都是关于C/C++的,实在搞不明白,因此特来向大佬们求教,非常感谢!

已经有大佬写了“ 在Windows下编写C/C++ DLL与Julia调用的正确姿势”,如果有C#调用JL的就更好了~


#2

jl_init 是个宏,C/C++ 里可以这样写,其他语言改用 jl_init__threading

#define jl_init jl_init__threading
—— julia/julia.h · JuliaLang/julia

注意事项

Julia 1.2.0 x64 联用

  • VS2017 + .Net 4.6.1 @x64
  • VS2017 + .Net Core 2.1 @x64
    测试通过
using System.Runtime.InteropServices;

namespace JuliaCall
{
    class Program
    {
        [DllImport("kernel32.dll")]
        static extern bool SetDllDirectory(string lpPathName);

        //const string julia_dll = @"C:\Users\woclass\AppData\Local\Julia-1.2.0\bin\libjulia.dll";
        const string julia_dll = @"libjulia.dll";

        [DllImport(julia_dll, CallingConvention = CallingConvention.Cdecl)]
        private static extern void jl_init__threading();

        [DllImport(julia_dll, CallingConvention = CallingConvention.Cdecl)]
        public static extern void jl_eval_string(string str);

        [DllImport(julia_dll, CallingConvention = CallingConvention.Cdecl)]
        public static extern void jl_atexit_hook(int a);

        static void Main(string[] args)
        {
            const string julia_home_dir = @"C:\Users\woclass\AppData\Local\Julia-1.2.0\bin";
            SetDllDirectory(julia_home_dir);

            // init
            jl_init__threading();

            jl_eval_string("print(sqrt(2.0))");

            jl_atexit_hook(0);
        }
    }
}

ref:


#3

如楼上所说,DLL里没有导出jl_init这个符号,它会被宏改成jl_init__threading
其他语言嵌入Julia的话就按照官方文档操作就好了,Julia使用的是C接口,几乎在哪里都可以用,与C#无关。

正好我在公司,电脑是苹果的,拿PowerShell凑合一下:rofl:

/Users/azure> Add-Type -MemberDefinition @"
>> const string LIB_PATH="/Applications/Julia-1.2.app/Contents/Resources/julia/lib/libjulia.dylib";
>> [DllImport(LIB_PATH,EntryPoint="jl_init__threading")]
>> public static extern void jl_init();
>> [DllImport(LIB_PATH)]
>> public static extern IntPtr jl_eval_string(string str);
>> [DllImport(LIB_PATH)]
>> public static extern void jl_atexit_hook(int status);
>> "@ -Name LibJulia -Namespace "" -PassThru

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    LibJulia                                 System.Object

/Users/azure> [LibJulia]::jl_init()      
/Users/azure> [LibJulia]::jl_eval_string('println("你好,世界")')        
你好,世界                                                                      
4420485128
/Users/azure> [LibJulia]::jl_atexit_hook(0)
/Users/azure>

#4

大佬太强大了:+1::+1::+1:,非常详细,感谢贴心指导!


#5

感谢大佬悉心点拨:+1::+1::+1:!原理性的东西应该是通用的,可以触类旁通。


#6

对于非编程科班的同学来说,完全将Julia嵌入C#里面,还是非常晦涩难懂的。比如有两个非常关键的问题:
1)如何把C#的值作为参数传给JL?
2)C#如何获取JL计算后返回的值?

大佬们如果能继续给一些案例就更好了。


#7

如何把C#的值作为参数传给JL?

jl_value_t *jl_call(jl_function_t *f, jl_value_t **args, int32_t nargs)

可以调用Julia的函数。

C#如何获取JL计算后返回的值?

看到楼上代码的IntPtr了吗?其实他们返回的都是指针,也就是jl_value_t*。如果返回的是bits类型,它们也会被装箱。用之前需要使用jl_typeis或者jl_isa判断一下类型,然后比如你要拿到返回的Float64,那就调用jl_unbox_float64

还有需要注意的就是垃圾回收。Julia在发生内存分配的时候可能会触发GC,然后你的指针就无效了,所以在jl_xxx调用之前最好固定一下拿到的对象。更详细的看我上面发的链接吧,很详细了。

总之,直接使用libjulia比较麻烦,需要考虑很多事情。如果对性能要求不高,不如用命名管道、套接字之类的万金油方式,具体看这篇 Julia如何调用C# dll动态链接库?


#8

谢谢大佬指明方向,尚需一段时间慢慢学习、消化。