问题 在语言层面,究竟是什么`ccall`?


我是朱莉娅的新手,我试图在语言层面理解什么 ccall 是。在语法级别,它看起来像一个普通的函数,但它显然在它的参数采用方式上的行为方式不同:

请注意,参数类型元组必须是文字元组,而不是a   元组值变量或表达式。

另外,如果我评估一个绑定到Julia REPL中的函数的变量,我会得到类似的东西

julia> max
max (generic function with 15 methods)

但是,如果我尝试做同样的事情 ccall

julia> ccall
ERROR: syntax: invalid "ccall" syntax

显然, ccall 是一种特殊的语法,但它也不是一个宏(没有 @ 前缀和无效的宏使用会产生更具体的错误)。所以, 它是什么?它是用语言编写的东西,还是我可以用一些我不熟悉的语言构造来定义自己的东西?

如果它是一些烘焙的语法,为什么它决定使用函数调用符号,而不是将其实现为宏或设计更可读和不同的语法?


11296
2018-02-15 23:33


起源



答案:


在当前的夜间(因此,即将发布的0.6版本),您观察到的许多特殊行为 已被删除 (看到 这个拉动请求)。 ccall 不再是保留字,因此它可以用作函数或宏名称。

然而 还有一点点奇怪之处:定义一个叫做3或4参数的函数 ccall 是允许的,但实际上调用这样的函数会给出错误 ccall argument types (其他数量的参数都可以)。原因直接针对您的问题:

那么,它是什么?是它融入语言的东西吗?

是, ccall虽然它不再是0.6中的关键字,但仍然以几种方式“融入”该语言:

  • :ccall([four args...]) 表达形式被认可和 特别处理 语法降低期间。这个降低步骤做了几件事,包括在调用中包装参数 unsafe_convert,允许从Julia对象到C兼容对象的自定义转换;以及拉出可能需要的论点  防止在引用期间引用对象的垃圾收集 ccall。 (看到 code_lowered 输出,或尝试 expand 功能;有关编译器的更多信息 这里)。
  • ccall 要求 广泛的处理 在代码生成后端,包括:在指定的共享库中查找请求的函数名称,以及生成LLVM call 指令 - 最终由LLVM Just-In-Time编译器转换为特定于平台的机器代码。 (参见不同的阶段 code_llvm 和 code_native)。

如果它是一些烘焙的语法,为什么决定使用它   函数调用表示法,而不是将其实现为宏或   设计更可读和更独特的语法?

由于上面详述的原因, ccall 需要特殊处理,无论它看起来像宏还是函数。在 这个邮件列表线程,Julia创作者之一(Stefan Karpinski)评论了为什么不把它变成一个宏:

我想我们可以重新实现它作为一个宏,但那真的只是将魔力进一步推向下方。

至于“更可读和更独特的语法”,也许这是一个品味问题。我不清楚为什么其他一些语法会更好(除了方便LuaJIT / CFFI样式的内联C语法解析,我是其中的粉丝)。我唯一强烈的个人愿望 ccall 将相邻的参数和类型输入(例如 ccall((:foo, :libbar), Void, (x::Int, y::Float))),因为使用较长的参数列表可能不方便。在0.6中,可以将此表单实现为宏!


10
2018-02-16 04:03



该 ccall 特殊形式也早于语言中的宏: github.com/JuliaLang/julia/commit/... 与 github.com/JuliaLang/julia/commit/...。有一个早期的宏观系统(github.com/JuliaLang/julia/commit/...)但我不认为它能够表达什么 ccall 需要。直到詹姆森纳什最近的工作“去魔法”ccall,这可能是不可能的。 - StefanKarpinski


答案:


在当前的夜间(因此,即将发布的0.6版本),您观察到的许多特殊行为 已被删除 (看到 这个拉动请求)。 ccall 不再是保留字,因此它可以用作函数或宏名称。

然而 还有一点点奇怪之处:定义一个叫做3或4参数的函数 ccall 是允许的,但实际上调用这样的函数会给出错误 ccall argument types (其他数量的参数都可以)。原因直接针对您的问题:

那么,它是什么?是它融入语言的东西吗?

是, ccall虽然它不再是0.6中的关键字,但仍然以几种方式“融入”该语言:

  • :ccall([four args...]) 表达形式被认可和 特别处理 语法降低期间。这个降低步骤做了几件事,包括在调用中包装参数 unsafe_convert,允许从Julia对象到C兼容对象的自定义转换;以及拉出可能需要的论点  防止在引用期间引用对象的垃圾收集 ccall。 (看到 code_lowered 输出,或尝试 expand 功能;有关编译器的更多信息 这里)。
  • ccall 要求 广泛的处理 在代码生成后端,包括:在指定的共享库中查找请求的函数名称,以及生成LLVM call 指令 - 最终由LLVM Just-In-Time编译器转换为特定于平台的机器代码。 (参见不同的阶段 code_llvm 和 code_native)。

如果它是一些烘焙的语法,为什么决定使用它   函数调用表示法,而不是将其实现为宏或   设计更可读和更独特的语法?

由于上面详述的原因, ccall 需要特殊处理,无论它看起来像宏还是函数。在 这个邮件列表线程,Julia创作者之一(Stefan Karpinski)评论了为什么不把它变成一个宏:

我想我们可以重新实现它作为一个宏,但那真的只是将魔力进一步推向下方。

至于“更可读和更独特的语法”,也许这是一个品味问题。我不清楚为什么其他一些语法会更好(除了方便LuaJIT / CFFI样式的内联C语法解析,我是其中的粉丝)。我唯一强烈的个人愿望 ccall 将相邻的参数和类型输入(例如 ccall((:foo, :libbar), Void, (x::Int, y::Float))),因为使用较长的参数列表可能不方便。在0.6中,可以将此表单实现为宏!


10
2018-02-16 04:03



该 ccall 特殊形式也早于语言中的宏: github.com/JuliaLang/julia/commit/... 与 github.com/JuliaLang/julia/commit/...。有一个早期的宏观系统(github.com/JuliaLang/julia/commit/...)但我不认为它能够表达什么 ccall 需要。直到詹姆森纳什最近的工作“去魔法”ccall,这可能是不可能的。 - StefanKarpinski


在朱莉娅0.5和更早。 它不是一个功能,它不是一个宏。 这确实是语言特有的东西。 这是一种内在的。 在朱莉娅0.6这改变了

它在很多方面更像是宏而不是函数调用。 但在其他方面它不是 - 它不会返回AST。 它确实调用了一个函数,并且在足够低的级别上它看起来类似于调用julia函数。

它为什么看起来像它的方式的历史超出了我,你需要听取一个致力于最早的语言代码的人。 现在它无处不在,而且是难以改变的事情之一 - 但并非不可能。 它会引发3年的自行车运动:-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不共享。 只要结果回来就没关系。 在这些情况下,理论上可以替换 ccall 有某种 safe_ccall 它将在一个单独的进程中运行被调用的库,如果库被称为segfaulted,则不会使julia发生段错误。 但到目前为止,还没有人写过这样的方法/包。

运用 ccall 对于FFI甚至在Base中完成,就像访问一样 MPFR定义BigFloat。 但这不是主要原因 ccall 用于 Base

获取语​​言的内涵。

ccall 真正是推动大部分计划“做事”的原因。 它在整个过程中使用 基础,从中调用函数 SRC

为了这, ccall 基本上在编译级别触发函数调用,将指令指针直接转换为编译后的代码 ccall编辑功能。就像调用函数一样,如果整个事情都是用C语写的。

你可以看到 碱/ threadingconstructs.jl ccall用于管理线程上的工作 - 从中​​触发代码 SRC / threading.c

它用于将磁盘的一部分映射到内存。 mmap.jl。 - 显然不能从另一个过程完成。

它用于制作一段代码 非intruptable

它是用来打电话的 的LibC 做的事情 malloc 分配内存(虽然现在这主要用作FFI的一部分)。

有你可以做的技巧 ccall 至 #undef 已经分配后的变量。 ccall 在许多方面是语言的“主”关键。

结论

我已经描述过了 ccall 这里有两件事,FFI函数和语言“运行时”的核心部分。这种二元性并不真实,并且有很多重叠,比如文件处理(是FFI吗?)。 许多人期望ccall的行为来自其FFI用途。 这里ccall可能只是一个功能。 它实际上的行为来自它作为语言的核心部分 - 连接标准库的julia代码 基础 来自的低级C代码 SRC。 允许直接控制朱莉娅过程的运行。


4
2018-02-16 02:56



这个答案可以通过展示什么来增强 ccall 变成了LLVM和ASM。 - Lyndon White