跳至內容

Lush/編譯

來自華夏公益教科書

Lush 是一種解釋型語言,但單個函式和類方法可以編譯,要麼是為了加快速度,要麼是為了啟用內聯 C/C++ 程式碼。

編譯純 Lush 函式

[編輯 | 編輯原始碼]

函式和類方法可以編譯,只需進行一些修改。以下是一個我們想要編譯的簡單函式

 ;; 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++ 程式碼

[編輯 | 編輯原始碼]

您可以使用 #{ ... #} 結構將 C/C++ 程式碼插入 Lush 函式中。您可以非常密集地交織 Lush 和原生程式碼

從 C/C++ 程式碼訪問 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

不同的編譯函式

[編輯 | 編輯原始碼]

dhc-make-with-libs

[編輯 | 編輯原始碼]

dhc-make-with-c++

[編輯 | 編輯原始碼]

編譯函式中的 C 程式碼

[編輯 | 編輯原始碼]

您可以將預處理器語句和其他 C 程式碼直接放入編譯函式中

#include 和 #define

[編輯 | 編輯原始碼]

呼叫 Lush 函式的 C 函式

[編輯 | 編輯原始碼]

如果有很多這樣的函式,您可以將它們放在 .h 和 .c 檔案中,以提高可讀性

 (dhc-make ()
           #{
           #include "c_funcs.h"
           #}
           
           lush-func-1
           lush-func-2
           ''...''
           
           #{
           #include "c_funcs.c"
           #})

連結到庫

[編輯 | 編輯原始碼]

libload 可能返回 null;請注意。

編譯和連結 C/C++ 原始檔

[編輯 | 編輯原始碼]

lushmake。正確的 lushmake 語法?我使用的方式是,它不理會依賴關係。

隱藏引數的問題

[編輯 | 編輯原始碼]

將lush函式編譯成C程式碼的一個問題是,你在函式中例項化的任何lush物件都會成為C版本函式中的引數。這些只在lush函式的C版本中出現的引數被稱為“隱藏引數”。lush函式的簽名(例如它接受的引數數量和型別)可能會發生變化。這是因為

布林值

[編輯 | 編輯原始碼]

沒有void 返回型別

[編輯 | 編輯原始碼]

返回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(); } #} )

棧還是堆?:

沒有cout

[編輯 | 編輯原始碼]

包裝的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++編譯器錯誤更加不透明,尤其是在涉及宏的情況下。
  • 除錯能力的損失
華夏公益教科書