------编写Ruby扩展
原文:
http://qiezi.iteye.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的头文件包含,每个头文件经过预处理以后都会包含一些重复内容,我们需要剔除这部分内容,通过查找行号即可完成。
3、转换相应声明到D声明,这一步可以使用一个c2h程序来完成。
注意预处理程序处理完毕以后,宏函数以及宏定义的常量会被去除,这可能不是你想要的,所以最好的办法可能是手工转换。
另一种调用是在D里面调用动态链接库,这需要你使用implib工具从动态链接库产生一个.lib导入库文件,然后生成D声明,再编译链接即可。如果是在linux下使用共享库,则只需要编写D声明文件,然后直接链接即可。
二、调用C库
转换完毕以后,就可以调用了。如果你只是要测试一下,就可以只声明使用过的外部函数、变量即可。
例如我们要编写Programming Ruby里面的一个Ruby Extension例子,相应的D代码如下:
// 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代码很相似。由于我们只使用了几个外部函数、变量,所以只需要声明这几个符号即可:
// 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定义文件:
// test.def
LIBRARY Test
DESCRIPTION ' Test written in D '
EXETYPE NT
CODE PRELOAD DISCARDABLE
DATA PRELOAD SINGLE
这是一个通用的格式,只是一些描述信息,因为D中可以使用export关键字导出符号,所以不需要在这里声明导出函数,只有在编写COM DLL时才会增加其它一些信息。
另外由于D语言要初始化GC以及其它一些信息,所以还需要在DllMain里面调用初始化及终止代码。由于不同平台的初始化过程不完全相同,这里我简单封闭了一下不同平台的初始化代码:
// 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编写初始化及终止代码:
// 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语言实现的:
// 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上编译命令如下:
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下测试:
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格式的导入库文件。
四、项目打算
打算建立一个rubyd项目,除了转换ruby头文件以外,还要作一些扩展,比如转换D类到ruby类,这方面已有借鉴,比如dsource.org上的pyd项目。
由于以前在建立项目方面有过失败经历(asgard项目由于兴趣转移和其它原因比如语法丑陋等而未能进行),这次还是保守一些,先完成D声明的转换,我已经使用工具转换了所有头文件,不过正如前面所说,宏函数和宏常量都丢失了,所以需要重新手工转换。
五、其它问题
1、如何从.DLL文件导出一个gdc支持的ELF格式的导入库文件?如果你知道可以告诉我,先谢过了。
2、dmd生成可执行文件问题不大,生成动态链接库或共享库有很大缺陷,特别是不能生成共享库,我可不想再找一个只能再Windows上正常运行的编译器。如何让它改进这些方面?
3、gdc使用dmd前端和gcc后端,应该会成熟一些,不过一般会比dmd前端版本稍低,而且gdc发布版本不是很频繁,大概4-5个dmd版本才会有一个gdc版本(初略计算),所以一些新特性不能够及时加入进来。
。。。。。。。。。。。。。。。
分享到:
相关推荐
设计基于89C51单片机控制的AD转换应用电路,AD转换芯片采用ADC0808,通过ADC0808转换成数字量在数码管上以十进制形成显示出来。
纯字库转换,包含几乎所有汉字,网上现有传播的转换都是unicode编码转换,其中很多字不在字库内,举例:瑛(ying)无法识别,存在很多问题,本代码直接字库转换,且公开文本,没有找到的字还可以添加到字库。...
A:程序需求环境:ASP B :vbb.mdb是vbb版本的数据库, . C:转到DVBBS的版本是ACCESS Dvbbs 7.0.0 + SP2 D:转换完成后默认的dvbbs7.0.0 的超管帐号:User:admin Pass:admin888...
即打开"前提教程.swf" <br> <br> <br>A:程序需求环境:ASP B :phpwind.mdb是phpwind版本的数据库, . C:转到DVBBS的版本是ACCESS Dvbbs 7.0.0 + SP2 D:转换完成后默认的dvbbs7.0.0 的超管帐号:User:...
即可以放到任意位置转换. D:程序转换的时候请先备份雪人论坛论坛的用户库跟论坛数据.因为程序在转换BBS3000论坛目录到DV的无限目录时要用到. E:sf.mdb请从雪人论坛的DATA目录下提出来.只有一个文件.即:sf....
Linux C 函数库参考手册 第1章 字符测试函数 第2章 数据转换函数 第3章 内存配置函数 第4章 时间函数 第5章 字符串处理函数 第6章 数字计算函数 第7章 用户和组函数 第8章 数据加密函数 第9章 数据结构函数 第10章 ...
Emoji - 一个把Unicode字符和名称转换为表情符号图片的库
字符转换为数字: #include atoi();将字符转换为整型 例:char ch1;int i=atoi(ch1);...double d=strtod(str1); strtol(); 将字符串转化为长整型 例:string str2; long int li=strtol(str2); strtoul
Altium继电器库AD原理图库37个,PCB封装库2D3D元件51个,封装均为3D三维视图封装,可以直接应用到你的产品设计中,也可以做为你的设计参考。 原理图库: Library Component Count : 37 Name Description ----------...
酷旋风iPad2/iPad视频格式转换器是一款被iPad爱好者,iPad相关论坛推荐的一款专门针对iPad视频转换软件,完全支持iPad 720P高清视频格式的转换,并且支持最新苹果iPad 2,是一款iPad 2视频格式转换器。 酷旋风iPad2/...
继电器Altium封装库三维视图2D3D封装库原理图库+PCB封装库(AD集成库) 原理图库器件列表: Library Component Count : 31 Name Description ------------------------------------------------------------------...
C.文件合并功能。将诸如 xx01.txt , xx02.txt ... xx??.txt等多个文件合并成一个新文件。 D.对 Txt和 Htm文件进行 GB BIG5内码相互转换。 E.能“批量处理"任意目录下的Txt和Html文件。 注意:PWin98下直接运行...
将C语言转换为Java字节码或可以直接解释执行的编译器 作为一个学习项目 可以解释为执行大多数C或编译为Java字节码 玩具级,未添加许多功能,也没有优化 支持的 支持所有基本陈述 解释器:支持指针,数组,结构和...
chapter 2 数据转换函数 chapter 3 内存配置函数 chapter 4 时间函数 chapter 5 字符串处理函数 chapter 6 数学计算函数 chapter 7 用户和组函数 chapter 8 数据加密函数 chapter 9 数据结构函数 chapter 10 随机数...
【课程列表】 index 共14页.ppt 第01章 简介 共36页.ppt 第02章 数据仓库、商业智能、OLAP和数据挖掘 共64页.ppt 第03章 定义数据仓库概念和术语 共46页.ppt 第04章 业务、逻辑、维度...xpp_D 使用SQL Plus 共34页.ppt
常用芯片AD集成封装库合集ALTIUM库(226个),ALTIUM集成库文件,多年积累,.IntLib后缀文件,拆分后为原理图库和PCB封装库文件。 集成库器件型号列表如下: Library Component Count : 226 Name Description -------...
romanice-转换成罗马数字/从罗马数字转换 一个用于在罗马数字之间进行转换JavaScript库,例如3888 :left-right_arrow: ...标准I , V , X , L , C , D , M const { romanice } = Romanice ; const
《c语言教程(原书第4版)》是一本优秀的c程序设计语言教材,完整描述了ansi c语言及其语法特性,并对c语言的高级特性和应用作了深入阐述,介绍了从c到c++和java过渡的相关知识。《c语言教程(原书第4版)》的一个...
A.Media Player B.Winamp C.RealPlayer D.Dreamweaver 【答案】D 10、以下工具软件中,主要用于制作三维动画的是______。 计算机应用基础统考题库-计算机多媒体技术篇全文共11页,当前为第3页。计算机应用基础统考...
第2章、数据转换函数 第3章、内存配置函数 第4章、时间函数 第5章、字符串处理函数 第6章、数学计算函数 第7章、用户和组函数 第8章、数据加密函数 第9章、数据结构函数 第10章、随机函数 第11章、初级I/O函数 第12...