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++编译器错误更加不透明,尤其是在涉及宏的情况下。
- 调试能力的损失