yidabu 2007-4-28 18:25
用D语言编写Ruby扩展
用D语言编写Ruby扩展知识若不分享 实在没有意义 http://www.d-programming-language-china.org 20070428点击下面网址查看原文:http://www.d-programming-language-china.org by: qiezi from: http://qiezi.javaeye.com/blog/26632 关键字: ruby 扩展 Ruby语言的官方解释程序是使用C语言开发的,一般用C语言来编写扩展。D语言和C语言是二进制兼容的,所以可以使用D语言编写Ruby扩展。 一、移植C库到D的一般过程 C使用头文件来处理符号依赖,在D里面链接外部库文件时,要使用extern (C)声明来引入符号,这是一个转换过程。 如何转换一个C头文件到D文件?D文档的htomodule.html有详尽说明。一般的转换过程如下: 1、运行预处理程序处理掉头文件里面的宏。 2、删除经过预处理以后的多余信息。由于C的头文件包含,每个头文件经过预处理以后都会包含一些重复内容,我们需要剔除这部分内容,通过查找行号即可完成。( 本文出处: http://www.d-programming-language-china.org ) 3、转换相应声明到D声明,这一步可以使用一个c2h程序来完成。 注意预处理程序处理完毕以后,宏函数以及宏定义的常量会被去除,这可能不是你想要的,所以最好的办法可能是手工转换。 另一种调用是在D里面调用动态链接库,这需要你使用implib工具从动态链接库产生一个.lib导入库文件,然后生成D声明,再编译链接即可。如果是在linux下使用共享库,则只需要编写D声明文件,然后直接链接即可。 二、调用C库 转换完毕以后,就可以调用了。如果你只是要测试一下,就可以只声明使用过的外部函数、变量即可。( 本文出处: http://www.d-programming-language-china.org ) 例如我们要编写Programming Ruby里面的一个Ruby Extension例子,相应的D代码如下: [Copy to clipboard] [ - ]CODE: // test.d module test; import ruby; extern (C) VALUE t_init(VALUE self) { VALUE arr = rb_ary_new(); rb_iv_set(self, " @arr " , arr); return self; } extern (C) VALUE t_add(VALUE self, VALUE anObject) { VALUE arr = rb_iv_get(self, " @arr " ); rb_ary_push(arr, anObject); return arr; } extern (C){ VALUE cTest; alias VALUE( * func_type)(); export void Init_Test() { cTest = rb_define_class( " Test " , rb_cObject); rb_define_method(cTest, " initialize " , cast(func_type) & t_init, 0 ); rb_define_method(cTest, " add " , cast(func_type) & t_add, 1 ); } } // extern(C) 和C代码很相似。由于我们只使用了几个外部函数、变量,所以只需要声明这几个符号即可: [Copy to clipboard] [ - ]CODE: // ruby.d module ruby; extern (C){ alias ulong VALUE; VALUE rb_cObject; VALUE rb_ary_new (); VALUE rb_ary_push (VALUE, VALUE); VALUE rb_iv_set (VALUE, char * , VALUE); VALUE rb_iv_get (VALUE, char * ); VALUE rb_define_class ( char * ,VALUE); void rb_define_method (VALUE, char * ,VALUE( * )(), int ); } 三、生成动态链接库(Windows DLL)或共享库(Linux so文件) D语言在Windows上编写DLL,除了要有D源文件以外,还要有DLL定义文件: [Copy to clipboard] [ - ]CODE: // test.def LIBRARY Test DESCRIPTION ' Test written in D ' EXETYPE NT CODE PRELOAD DISCARDABLE DATA PRELOAD SINGLE 这是一个通用的格式,只是一些描述信息,因为D中可以使用export关键字导出符号,所以不需要在这里声明导出函数,只有在编写COM DLL时才会增加其它一些信息。( 本文出处: http://www.d-programming-language-china.org ) 另外由于D语言要初始化GC以及其它一些信息,所以还需要在DllMain里面调用初始化及终止代码。由于不同平台的初始化过程不完全相同,这里我简单封闭了一下不同平台的初始化代码:( 本文出处: http://www.d-programming-language-china.org ) [Copy to clipboard] [ - ]CODE: // os/library.d module os.library; extern (C){ void gc_init(); void gc_term(); version(Windows) void _minit(); void _moduleCtor(); void _moduleDtor(); void _moduleUnitTests(); version(linux) void _STI_monitor_staticctor(); version(linux) void _STI_critical_init(); version(linux) void _STD_monitor_staticdtor(); version(linux) void _STD_critical_term(); } extern (C) void d_init() { // writefln("Start init D runtime"); version(linux) _STI_monitor_staticctor(); version(linux) _STI_critical_init(); gc_init(); version(Windows) _minit(); _moduleCtor(); _moduleUnitTests(); // writefln("init finished"); } extern (C) void d_fini() { // writefln("Start term D runtime"); _moduleDtor(); gc_term(); version(linux) _STD_critical_term(); version(linux) _STD_monitor_staticdtor(); // writefln("term finished"); } 现在可以为Windows编写初始化及终止代码: [Copy to clipboard] [ - ]CODE: // os/dll.d module os.dll; private import os.library; private import std.c.windows.windows; HINSTANCE g_hInst; extern (Windows) BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved) { switch (ulReason) { case DLL_PROCESS_ATTACH: d_init(); break ; case DLL_PROCESS_DETACH: d_fini(); break ; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: // Multiple threads not supported yet return false ; } g_hInst = hInstance; return true ; } 由于Linux共享库并没有标准的入口函数(或是我不知道它),这里使用gcc扩展的初始、终止代码,不过是以C语言实现的: [Copy to clipboard] [ - ]CODE: // os/so.c #include < ruby.h > static void so_init( void ) __attribute__((constructor)); static void so_fini( void ) __attribute__((destructor)); extern void d_init( void ); extern void d_fini( void ); void so_init( void ) { d_init(); } void so_fini( void ) { d_fini(); } 现在可以尝试编译链接,在Linux上编译命令如下: [Copy to clipboard] [ - ]CODE: gcc - o os / so.o - c os / so.c - I / usr / lib / ruby / 1.8 / i686 - linux gdc - o Test.so test.d os / so.o ruby.d - shared - fPIC - lruby 你可以在irb下测试:( 本文出处: http://www.d-programming-language-china.org ) require ' Test ' test = Test. new test.add( 1 ) test.add( 2 ) test.add( " a " ) 可以看到add总是返回一个array,与期望结果一致。 使用gdc是因为dmd在linux上无法生成共享库。 在Windows上的编译命令如下: dmd - oftest.dll test.d test.def ruby18.lib os / dll.d os / library.d ruby18.lib是使用implib从msvcrt-ruby18.dll导出的,这个编译过程很顺利,不过不幸的是它运行有一些问题,大概是一些初始值错误,我暂时还没有找到原因。或许也应该尝试一下gdc,不过我不知道如何从.DLL文件导出一个gdc支持的ELF格式的导入库文件。( 本文出处: http://www.d-programming-language-china.org ) 四、项目打算 打算建立一个rubyd项目,除了转换ruby头文件以外,还要作一些扩展,比如转换D类到ruby类,这方面已有借鉴,比如dsource.org上的pyd项目。 由于以前在建立项目方面有过失败经历(asgard项目由于兴趣转移和其它原因比如语法丑陋等而未能进行),这次还是保守一些,先完成D声明的转换,我已经使用工具转换了所有头文件,不过正如前面所说,宏函数和宏常量都丢失了,所以需要重新手工转换。 五、其它问题 1、如何从.DLL文件导出一个gdc支持的ELF格式的导入库文件?如果你知道可以告诉我,先谢过了。( 本文出处: http://www.d-programming-language-china.org ) 2、dmd生成可执行文件问题不大,生成动态链接库或共享库有很大缺陷,特别是不能生成共享库,我可不想再找一个只能再Windows上正常运行的编译器。如何让它改进这些方面? 3、gdc使用dmd前端和gcc后端,应该会成熟一些,不过一般会比dmd前端版本稍低,而且gdc发布版本不是很频繁,大概4-5个dmd版本才会有一个gdc版本(初略计算),所以一些新特性不能够及时加入进来。 最后更新:2006-11-28 17:56 06:17 | 永久链接 | 浏览 (366) | 评论 (0) | 收藏 | D | 发布在 D语言 圈子( lastupdate:20070428 最新文章请访问http://www.d-programming-language-china.org )关于一大步成功社区:yidabu提倡在交流中学习,在分享中提高收集感兴趣的知识,写下心得,通过网络与别人一起分享理解一点就实践一步,收获什么就分享什么,成功就是这样一点点一步步累积起来的网络只是一个工具,只有自己身心提高才是实实在在的。d-programming-language-china.org为大家提供一个学习交流各种知识的平台