[英]At a language level, what exactly is `ccall`?
我是朱莉婭的新手,我試圖在語言層面理解ccall
是什么。 在語法級別,它看起來像一個普通函數,但它在參數的顯示方式上顯然不同:
請注意,參數類型元組必須是文字元組,而不是元組值變量或表達式。
另外,如果我評估一個綁定到Julia REPL中的函數的變量,我會得到類似的東西
julia> max
max (generic function with 15 methods)
但如果我嘗試用ccall
做同樣的ccall
:
julia> ccall
ERROR: syntax: invalid "ccall" syntax
顯然, ccall
是一種特殊的語法,但它也不是一個宏(沒有@
前綴,無效的宏使用會產生更具體的錯誤)。 那么, 它是什么 ? 它是用語言編寫的東西,還是我可以用一些我不熟悉的語言構造來定義自己的東西?
如果它是一些烘焙的語法,為什么決定使用函數調用符號,而不是將其實現為宏或設計更可讀和不同的語法?
在當前的夜間(以及即將發布的0.6版本)中,您觀察到的大部分特殊行為已被刪除 (請參閱此拉動請求 )。 ccall
不再是保留字,因此可以用作函數或宏名。
但是仍然有一點點奇怪:允許定義一個名為ccall
的3或4參數函數,但實際調用這樣的函數會產生關於ccall argument types
的錯誤(其他數量的參數都可以)。 原因直接針對您的問題:
那么,它是什么? 是它融入語言的東西嗎?
是的, ccall
雖然它不再是0.6中的關鍵字,但仍然以幾種方式“ ccall
”該語言:
:ccall([four args...])
表達式在語法降低期間被識別並專門處理 。 這個降低步驟做了幾件事,包括在對unsafe_convert
的調用中包裝參數,它允許從Julia對象到C兼容對象的自定義轉換; 以及拉出可能需要生根的參數,以防止在ccall
期間垃圾收集被引用的對象。 (請參閱code_lowered
輸出,或嘗試使用expand
函數;有關編譯器的更多信息,請參閱此處 )。 ccall
需要在代碼生成后端進行大量處理 ,包括:在指定的共享庫中查找所請求的函數名稱,以及生成LLVM call
指令 - 最終由LLVM Just-轉換為特定於平台的機器代碼及時編譯器。 (使用code_llvm
和code_native
查看不同的階段)。 如果它是一些烘焙的語法,為什么決定使用函數調用符號,而不是將其實現為宏或設計更可讀和不同的語法?
由於上面詳述的原因, ccall
需要特殊處理,無論它看起來像宏還是函數。 在這個郵件列表主題中 ,Julia創建者之一(Stefan Karpinski)評論了為什么不將它作為一個宏:
我想我們可以重新實現它作為一個宏,但那真的只是將魔法推向更進一步。
至於“更可讀和更獨特的語法”,也許這是一個品味問題。 我不清楚為什么其他一些語法會更好(除了方便LuaJIT / CFFI樣式的內聯C語法解析,我是其中的粉絲)。 我對ccall
唯一強烈的個人願望是將參數和類型相鄰輸入(例如ccall((:foo, :libbar), Void, (x::Int, y::Float))
),因為使用較長的參數列表可能不方便。 在0.6中,可以將此表單實現為宏!
在朱莉婭0.5和更早。 它不是一個功能,它不是一個宏。 這確實是語言特有的東西。 這是一種內在的。 在朱莉婭0.6這改變了
它在很多方面更像是宏而不是函數調用。 但在其他方面它不是 - 它不會返回AST。 它確實調用了一個函數,並且在一個足夠低的級別上它看起來類似於調用julia函數。
它看起來像它的方式的歷史超出了我的范圍,你需要聽取其中一位研究最早的語言代碼的人的意見。 現在它無處不在,而且是難以改變的事情之一 - 但並非不可能。 它會引發3年的自行車運動:-P 。
我想將ccall
視為兩件事。
大多數情況下,當一個ccall
在一個包中使用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,則不會使julia發生段錯誤。 但到目前為止,沒有人寫過這樣的方法/包。
使用ccall
for FFI甚至可以在Base中完成,就像訪問MPFR來定義BigFloat一樣 。 但這不是ccall
在Base
使用的主要原因。
ccall
實際上是推動大部分程序“做事”的原因。 它在整個Base中使用 ,從src調用函數。
為此, ccall
基本上在編譯級別觸發函數調用,將指令指針直接轉移到ccall
ed函數的編譯代碼中。 就像調用函數一樣,如果整個事情都是用C語寫的。
您可以在base / threadingconstructs.jl中看到ccall用於管理線程上的工作 - 從src / threading.c觸發代碼。
它用於將磁盤的一部分映射到內存。 mmap.jl。 - 顯然不能從另一個過程完成。
它用於使一段代碼不可侵犯
它被用來調用LibC來做像malloc
這樣的東西來分配內存(雖然現在這主要用作FFI的一部分)。
在已經分配變量之后,您可以使用ccall
將#undef
變量ccall
到變量中。 ccall
在很多方面都是該語言的“主”鍵。
我在這里描述了ccall
作為兩件事,FFI函數和語言“運行時”的核心部分。 這種二元性並不真實,並且有很多重疊,比如文件處理(是FFI嗎?)。 很多人都期望ccall來自它的FFI用途。 這里ccall可能只是一個功能。 它實際上的行為來自於它作為語言的核心部分 - 將Base中標准庫的julia代碼鏈接到src中的低級C代碼。 允許直接控制julia過程的運行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.