在 Julia 0.5 及更早版本中。
它不是函数,也不是宏。
这确实是一种特殊的语言。
这是一个内在的。
In julia 0.6 this changes
它在很多方面更像是宏而不是函数调用。
但在其他方面它不是——它不返回 AST。
它确实调用了一个函数,并且在足够低的级别上看起来类似于调用 julia 函数。
它为什么看起来如此的历史我无法理解,您需要听听一位从事该语言最早代码的人的意见。
现在它无处不在,是最难改变的事情之一——但并非不可能。 It would trigger up for 3 years of bikeshedding though :-P .
我喜欢将ccall 视为两件事。
- 外来函数接口,用于 C 和其他编译语言(例如 Fortran、Rust 显然可以工作)
- 访问语言“运行时”原始内容的方法。
外来函数接口(FFI)
大多数时候,当一个人在一个包中使用ccall 时,想调用编译库中的一些代码。从这个意义上说,它是 C-Call,就像 R-Call 或 Py-Call。
我认为mlewe/BlossomV.jl 是一个很好的紧凑示例。
更激烈的例子oxinabox/SLEEF.jl。
作为 FFI,它不必与 julia 共享内存空间/进程—— PyCall.jl 可以,RCall.jl 和 Matlab.jl 不必。
只要结果回来就没有关系。
在这些情况下,理论上可以用某种safe_ccall 替换ccall,这将在一个单独的进程中运行被调用的库,并且如果库被称为segfaulted,则不会出现segfault julia。
但到目前为止,还没有人编写过这样的方法/包。
使用 ccall 进行 FFI 甚至在 Base 中完成,例如访问 MPFR to define BigFloat。
但这并不是在Base 中使用ccall 的主要原因。
深入了解语言。
ccall 确实是驱动大部分程序“做某事”的原因。
它在整个Base 中使用,用于调用src 中的函数。
为此,ccall 基本上会在编译级别触发函数调用,将指令指针直接转移到 ccalled 函数的编译代码中。就像调用一个函数一样,如果整个事情都是用 C 语言编写的。
您可以在 base/threadingconstructs.jl 中看到 ccall 被用于管理线程上的工作——这会触发来自 src/threading.c 的代码。
它用于将磁盘的一部分映射到内存。 mmap.jl。 -- 显然不能从另一个进程中完成。
用于制作一段代码non-intruptable
它用于调用LibC 来执行诸如malloc 之类的事情来分配内存(尽管目前这主要用作FFI 的一部分)。
在分配变量后,您可以使用ccall 到#undef 执行一些技巧。
ccall 在很多方面都是该语言的“主”钥匙。
结论
我在这里将ccall 描述为两件事,一个 FFI 函数和语言“运行时”的核心部分。这种二元性是不真实的,并且有很多重叠,比如文件处理(是 FFI 吗?)。
许多人期望 ccall 的行为来自其 FFI 用途。
这里 ccall 可能只是一个函数。
它实际具有的行为来自它作为语言的核心部分的使用——将Base 中标准库的julia 代码链接到src 中的低级C 代码。
允许直接控制 julia 进程的运行。