簡體   English   中英

在語言層面,究竟是什么`ccall`?

[英]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_llvmcode_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視為兩件事。

  • 外部函數接口,用於C和其他編譯語言(例如Fortran,Rust顯然可以工作)
  • 訪問語言“運行時”的原始內容的方法。

外部函數接口(FFI)

大多數情況下,當一個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一樣 但這不是ccallBase使用的主要原因。

獲取語​​言的內涵。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM