CLisp 28:调用C程序详细指导
来源:互联网 发布:淘宝购物车加不进去 编辑:程序博客网 时间:2024/05/22 03:07
首先,介绍一下快速编译C代码生成DLL的方法,使用命令行。VC自带的工具Visual Studio .NET Tools > Visual Studio .NET 2003 Command Prompt。在这里可以使用程序cl.exe,在普通命令行窗口中用不了。因为是学习,为了方便只使用一个C文件,取名lisp.c,生成lisp.dll。命令很简单 cl.exe /LD lisp.c , /LD 表示生成Release版本的DLL文件,如果要定义一些宏,可以用 /DMacroName=Value,定义多个宏时写多个/D指令即可,这里用不上。
使用变量或函数,都需要C中导出,然后在LISP中导入。导出很简单,在变量或函数前加上__declspec(dllexport)前缀即可。例如:
__declspec(dllexport) int x;
__declspce(dllexport) int add(int x, int y) { return x + y;}
使用基本类型,这些基本类型看名称就知道什么意思:
nil 就是 void
boolean 对应于C的int类型
character 是字符类型
char uchar short ushort int uint long ulong在LISP内部都是integer
uint8 sint8 uint16 sint16 uint32 sint32 uint64 sint64是确定位数的,推荐使用
single-float对应float,单精度浮点数
double-float对应double,双精度浮点数
以浮点数为例,介绍如何使用基本类型,C代码:
__declspec(dllexport)
double x = 0.0;
__declspec(dllexport)
double getAndSet(double d)
{
double temp = x;
x = d;
return temp;
}
LISP代码,导入变量x和函数getAndSet,各参数的作用一看就明白。因为LISP对大小写不敏感,而C语言对大小写敏感,所以最好指定:name参数,表明其在C语言中的名称。
(use-package :ffi)
(setq dll "d:\\MyProjects\\teststh\\lisp.dll")
(default-foreign-language :stdc)
(def-c-var x (:type double-float) (:library dll))
(def-call-out getAndSet (:name “getAndSet”)
(:arguments (d double-float))
(:return-type double-float)
(:library dll))
(getAndSet 3.14159d0)返回0.0d0,再查看x的值返回3.14159d0,也可以用(setf x 2.71828d0)改变x的值。简单类型使用起来还是很方便的。
导入变量时,LISP会自动打开并锁定DLL文件,使得你不能重新编译。可以在LISP中强行关闭DLL文件,执行(ffi:close-foreign-library “file name of dll”),返回#<INVALID FOREIGN-POINTER #x10000000>,意思是LISP中指向这个DLL的句柄变成无效指针了。修改C代码后重新编译,在LISP中无需重新导入变量或函数,只要使用变量或函数,就会自动重新打开DLL文件。当然,也可以主动调用(ffi:open-foreign-library “file name of dll”)打开DLL文件,返回#<FOREIGN-POINTER #x10000000>。如果DLL文件不存在,打开操作不会返回NIL,而是抛出一个异常。重复打开,或重复关闭,不会出错,第二次调用的返回和第一次是一样的。
接下来介绍如何使用数组,当然是简单类型的数组。在LISP中使用C语言的数组,或者将LISP的数组作为函数参数传给C语言,或者C语言将数组作为返回值。
C语言的代码:
__declspec(dllexport)
int arr[10];
__declspec(dllexport)
int sum1(int a[10])
{
int s = 0;
int i;
for (i = 0; i < 10; i++)
s += a[i];
return s;
}
LISP语言的代码,在基本类型外部包一层c-array就得到数组类型。(c-array type N)相当于 type[N],(c-array type (N M L))相当于 type[N][M][L]。
(def-c-var arr (:type (c-array int 10)) (:library dll))
查看arr的值返回#(0 0 0 0 0 0 0 0 0 0)。修改元素的值(setf (element arr 9) 9),element是FFI提供的函数。修改元素值时必须用element获取元素,不能使用aref。查看元素的值,可以用element,也可以用aref,例如(aref arr 9)返回数字9。
(def-call-out sum1 (:return-type int)
(:arguments (a (c-ptr (c-array int 10))))
(:library dll))
(setq arrLisp #(1 2 3 4 5 6 7 8 9 10))
(sum1 arrLisp)返回55
注意,参数类型前面包装了c-ptr,表示是一个指针。如果不加c-ptr会是什么样子的呢,LISP会将数组中的每个参数压入堆栈中。相应的C代码应该这样写,不解释了:
__declspec(dllexport)
int sum2(int a1)
{
int * ptr = &a1;
int s = 0;
int i;
for (i = 0; i < 10; i++)
s += ptr[i];
return s;
}
如果在C语言中修改传入的数组元素,会不会生效?修改sum1的代码,在函数内修改a[i]的值,发现没有效果。把参数改成:in-out模式试一下,其中一行改成(:arguments (a (c-ptr (c-array int 10)) :in-out)),再执行sum1,返回两个值,第一个是求和结果55,第二个是长度为10的数组,也就是修改后的数组,但是LISP里的作为输入参数的数组没有改变。
[16]> (sum1 arrLisp)
55 ;
#(2 4 6 8 10 12 14 16 18 20)
[17]> arrLisp
#(1 2 3 4 5 6 7 8 9 10)
使用定长数组并不是编程的好习惯,通用做法是输入数组长度和首地址。
C语言代码:int sum3(int size, int * arr);
(def-call-out sum1 (:return-type int)
(:arguments (size int) (arr (c-array-ptr int)))
(:library dll))
(sum3 (length arrLisp) arrLisp)
接下来看看怎么使用字符串,字符串和数组的本质是一样的。LISP的FFI中,c-string就是字符串类型。
例1,字符串作为输入参数:
__declspec(dllexport)
void cprint(char * str) { puts(str); }
(def-call-out cprint
(:arguments (str c-string))
(:library dll))
(cprint “how do you do?”)
例2,字符串常量作为返回值:
__declspec(dllexport)
char const * askName() { return “My name is HanMeiMei.”; }
(decl-call-out askName (:name “askName”)
(:return-type c-string)
(:library dll))
(askName) > “My name is HanMeiMei.”
例3,字符串作为输入参数和返回值:
__declspec(dllexport)
char * upCase(char * str)
{
static char buffer[128];
char * pch = buffer;
while (*str != 0)
{
if ('a' <= *str && *str <= 'z')
*pch = *str - 'a' + 'A';
else
*pch = *str;
++pch;
++str;
}
*pch = 0;
return buffer;
}
(def-call-out upCase (:name “upCase”)
(:return-type c-string)
(:arguments (str c-string))
(:library dll))
(setq x (upCase “just do it!”))
(setq y (upCase “Good work!”))
在例3中,函数upCase每次都返回相同的内存地址,但没有出问题,因为LISP在取结果时会重新分配内存。这里没有动态分配内存,因为这是一个很复杂的问题,下一次介绍。
- CLisp 28:调用C程序详细指导
- CLisp 29:调用C程序之回调函数
- CLisp 30:调用C程序之外部变量
- CLisp 31:调用C程序之外部函数
- CLisp 14:在LISP中用FFI调用C语言的程序-成功了
- CLisp 13:使用包FFI在CLisp中调用C语言写的函数
- Linux编写C程序指导
- CLisp 21:模块化构建程序
- C语言下程序的堆栈调用(详细,图示)
- Java调用c程序动态库详细案例
- CLisp 22:单步跟踪LISP程序
- Clisp的前生,程序编辑器选择
- 在控制台编译运行java程序详细指导
- 汇编程序调用c程序
- c调用外部程序
- Matlab调用c程序
- java调用c程序
- MATLAB调用C程序
- 单线程模型中Message、Handler、Message Queue、Looper之间的关系
- UVA 120 - Stacks of Flapjacks
- Hadoop RPC源码分析之Client
- 蒙提霍尔问题&箱子问题
- jffs2文件系统——MTD驱动挂载
- CLisp 28:调用C程序详细指导
- Ubuntu 10.04 搭建Android开发环境
- GPRS管理与创建APN拨号连接
- 井深结构图
- 回文串判断(先错后对,OJ系统好严格啊)
- nsbundle
- Hibernate--什么是持久化?
- [实习纠错日记]关于java.util.ConcurrentModificationException的错误
- 关于[datePicker date]不正确显示当地时间的问题