Lush/編譯
Lush 是一種解釋型語言,但單個函式和類方法可以編譯,要麼是為了加快速度,要麼是為了啟用內聯 C/C++ 程式碼。
函式和類方法可以編譯,只需進行一些修改。以下是一個我們想要編譯的簡單函式
;; Prints the size, sum, and mean of a vector
(de print-mean (vec)
(let* ((v-size (idx-dim vec 0))
(v-sum (idx-sum vec)
(v-mean (/ v-sum v-size))
(printf "Sum = %f, size = %d, mean = %f\n" v-sum v-size v-mean)
())
在下面,我們添加了一些型別宣告來使函式可編譯,並在其後添加了編譯命令
;; Prints the size, sum, and mean of a vector
(de print-mean (vec)
((-idx1- (-double-)) vec)
(let* ((v-size (idx-dim vec 0))
(v-sum (idx-sum vec)
(v-mean (/ v-sum v-size))
((-int-) v-size)
(printf "Sum = %f, size = %d, mean = %f\n" v-sum v-size v-mean)
())
; compile "print-mean"
(dhc-make () print-mean)
要使 Lush 函式可編譯,必須
- 宣告任何型別不明確的物件的型別。
- 僅使用可編譯函式和表示式。
- 在原始檔末尾的編譯語句中列出該函式。
在上面的函式中,緊隨引數之後的幾行聲明瞭引數型別。請注意,該函式沒有宣告其他任何變數的型別。這是因為您只需要為函式引數和非雙精度數值型別宣告型別。 這是因為除非指定,否則函式引數型別在編譯時並不明顯,而 Lush 假定所有數字都是 double,除非另行說明。
所有型別宣告都採用以下形式
((type_name) var_name)
例如
((-int-) my-int)
以下列出了可編譯 Lush 型別的型別名稱,以及它們相應的 C++ 等效型別
| Lush 型別 | C++ 型別 | 描述 |
|---|---|---|
-int-
|
int
|
32 位整數 |
-float-
|
float
|
32 位實數 |
-double-
|
double
|
64 位實數 |
-short-
|
short
|
16 位整數 |
-byte-
|
char
|
8 位整數 |
-ubyte-
|
unsigned char
|
8 位無符號整數 |
-gptr-
|
void*
|
型別無關指標(大小取決於平臺)。 |
-gptr- "std::string"
|
std::string*
|
型別特定指標(大小與 -gptr-/void* 相同) |
-str-
|
?
|
Lush 字串 |
-idx- (-double-)
|
?
|
Lush 張量(-double- 可以是任何基本型別名稱。) |
-obj- (htable)
|
?
|
任何其他類(htable 是 Lush 雜湊表的類名)。 |
* 張量不能包含型別特定指標。換句話說,以下情況不允許
;; NOT ALLOWED: a tensor of char* pointers ((-idx1- (-gptr- "char")) my-char-pointer-tensor)
但以下情況可以
;; A tensor of void* pointers ((-idx2- (-gptr-)) my-pointer-tensor)
不幸的是,並非所有 Lush 函式或表示式都可編譯。大多數不可編譯的函式是那些涉及列表操作的函式。更一般地說,Lush 的任何“Lisp 式”特性(lambda 表示式、鉤子、程式碼操作)通常都不可編譯。要檢視特定函式是否可編譯,請使用 compilablep 函式
? (compilablep double-matrix)
= t
? (compilablep range)
= ()
請注意,compilablep 只能接受單個函式和宏名稱,不能接受複雜的 Lush 表示式。
雖然列表操作通常不可編譯,但包含列表的表示式不一定不可編譯。例如,idx-transclone 函式必須接受列表作為其引數之一,但仍然可編譯。
在使您的函式適合編譯後,您必須在原始檔末尾呼叫編譯函式才能實際編譯它們。以下是一個函式和兩個類方法,後面是編譯函式 dhc-make
(de some-func (...)
... )
(de my-class object
... )
(defmethod my-class method-1 (...)
... )
(defmethod my-class method-2 (...)
... )
(dhc-make ()
(some-func)
(my-class method-1
method-2))
編譯函式實際上執行 3 件事
- 將函式/方法轉錄成等效的 C 程式碼。
src-dir/src-file.lsh檔案的 C 原始檔將位於src-dir/C/src_file.c中。 - 使用 gcc 將此 C 原始檔編譯成目標檔案。
- 將生成的目標檔案連結到記憶體中。
只有當 C 原始檔比 Lush 原始檔舊時,編譯函式才會執行。
您可以使用 #{ ... #} 結構將 C/C++ 程式碼插入 Lush 函式中。您可以非常密集地交織 Lush 和原生程式碼
下表顯示瞭如何從 C/C++ 程式碼訪問不同的 Lush 物件
| 型別 | Lush 名稱 | C/C++ 名稱 |
|---|---|---|
| 基本型別 | my-double
|
$my_double
|
| 張量型別 | my-float-tensor
|
float* raw_data = IDX_PTR( $my_float_tensor, float )
|
| 字串 | my-str
|
char* c_string = $my_str->data
|
| 物件槽 | :my-obj:some-slot
|
$my_obj->some_slot
|
| 物件方法 | (==> my-obj some-method arg1)
|
$my_obj->vtbl->M_some_method($arg1)
|
| 全域性函式 | (add-numbers num1 num2)
|
C_add_numbers($num1, $num2)
|
| 全域性常量 | @MY_CONSTANT
|
MY_CONSTANT
|
您可以將預處理器語句和其他 C 程式碼直接放入編譯函式中
如果有很多這樣的函式,您可以將它們放在 .h 和 .c 檔案中,以提高可讀性
(dhc-make ()
#{
#include "c_funcs.h"
#}
lush-func-1
lush-func-2
''...''
#{
#include "c_funcs.c"
#})
libload 可能返回 null;請注意。
lushmake。正確的 lushmake 語法?我使用的方式是,它不理會依賴關係。
將lush函式編譯成C程式碼的一個問題是,你在函式中例項化的任何lush物件都會成為C版本函式中的引數。這些只在lush函式的C版本中出現的引數被稱為“隱藏引數”。lush函式的簽名(例如它接受的引數數量和型別)可能會發生變化。這是因為
返回nil(也稱為())的lush函式將在其C化身中返回一個char。當您確實需要您的函式不返回任何內容時,這可能是一個問題,例如當將其用作某些期望void返回函式的C API 的回撥函式時
在my-src.lsh 中返回nil 的函式
(de my-callback ()
...
())
...在C/my_src.c 中變成了一個返回char 的函式
char* C_my_callback(){
...
}
在這種情況下,您最好的選擇是在一個返回void的C函式中包裝lush函式。那麼問題是:如何編譯這個包裝函式?最簡單的方法是在my-src.lsh 結尾的編譯函式中直接放入它
(dhc-make ()
#{ void callback_wrapper(); #}
my-callback
''... other lush functions & methods ...''
''...''
''...''
#{ void callback_wrapper(){ C_my_callback(); } #} )
棧還是堆?:
包裝的C++類的動態分配:型別轉換錯誤
在決定是否真的要編譯lush程式碼之前,有幾個問題需要考慮。
to be compiled can be more of a hassle than writing the same code in c/c++, for a number of reasons:
- 可編譯的Lush不如C++功能豐富或視覺上整潔
- Lush編譯器錯誤可能比C/C++編譯器錯誤更加不透明,尤其是在涉及宏的情況下。
- 除錯能力的損失