`
hqs7636
  • 浏览: 220926 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

转换 c 库到 d

阅读更多
------编写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版本(初略计算),所以一些新特性不能够及时加入进来。




。。。。。。。。。。。。。。。
分享到:
评论

相关推荐

    各种进制间转换——c语言

    - **内置函数**:虽然C标准库没有提供直接的进制转换函数,但可以自定义函数实现。例如,可以编写一个函数 `int decimal_to_octal(int dec_num)` 来将十进制数转换为八进制,或者 `char* hex_to_string(unsigned int...

    c语言十进制到任何进制的转换

    "c语言十进制到任何进制的转换" 从给定的文件信息中,我们可以看到,标题为"c语言十进制到任何进制的转换",描述为"c语言十进制到任何进制的转换,代码卸载文档里面,方便实用,适合初学者"。标签为"c语言 进制转换...

    AVR单片机C语言库

    在这个“AVR单片机C语言库”中,我们将探讨23类不同的C语言函数库,这些库为开发者提供了方便的API,以简化在AVR上的编程工作。 1. **基本输入/输出(I/O)**:包括设置端口方向、读写端口、端口位操作等,使得开发...

    C语言中将数字转换为字符串的方法

    当使用sprintf()将整数转换为字符串时,只需将整数按格式说明符(%d)写入到字符数组中即可,示例如下: ```c int num = 100; char str[25]; sprintf(str, "%d", num); ``` 这种方法简单并且符合标准C,因此推荐...

    数制转换(c语言实现)

    1. C语言中的标准库函数:C标准库提供了一些函数,如`printf`和`scanf`,它们支持直接输出或输入不同进制的数字。例如,`%o`格式化标志用于输出八进制,`%x`或`%X`用于输出十六进制。 2. 高精度计算:如果你需要...

    json-c 一个用于c语言的json解析库,很强大

    要使用`json-c`库,开发者首先需要将其编译并链接到自己的C项目中,然后就可以调用提供的API来解析JSON字符串,创建JSON对象,或者将JSON对象转换回字符串进行网络传输或保存到文件。例如,解析JSON字符串的代码可能...

    VC和VS用的C语言图形库

    `Turbo c图形函数.exe`可能是提供了一些与 Turbo C 图形函数相对应的转换工具或教程,帮助那些熟悉 Turbo C 的开发者适应新的图形库。`Setup.hta`是HTML应用程序,可能是图形化的安装向导。`C语言图形编程.pdf`很...

    纯真IP库转换工具转换为其他IP库或者TXT文本

    2. **IP地址分类**:IPv4地址分为A、B、C、D、E五类,其中A、B、C类用于互联网上的主机,D类用于多播,E类保留未用。不同类别的IP地址有不同的网络和主机位数。 3. **IP库的用途**:IP库通常包含IP地址、对应的国家...

    Protel封装库如何转换为Cadence

    这样就完成了从Protel到Cadence的封装库转换。 #### 三、注意事项 - 在转换过程中,需要注意保持Pad的尺寸和位置的准确性,避免因为转换过程中的误差导致后续设计出现问题。 - 尽量利用已有的Pad资源,减少不必要...

    WAV转C格式 声音文件转换器

    转换完成后,你可以将生成的C代码引入到你的项目中,按照C语言的标准音频播放库或自定义播放函数来播放声音。 总之,"WAV转C格式 声音文件转换器"是音频开发中的一个重要工具,它允许开发者将音频数据高效地集成到...

    C语言的各类进制转换

    C语言标准库提供了两个函数来进行进制转换: 1. **`printf()`**:用于格式化输出,可以用来打印不同进制的数值。 2. **`scanf()`**:用于格式化输入,可以读取不同进制的输入。 三、进制转换实例 1. **十进制转...

    C语言函数库!

    这些函数库通常被包含在`&lt;string.h&gt;`和`&lt;stdlib.h&gt;`等头文件中,它们包含了从简单的字符串复制、比较到复杂的字符串解析和转换等功能。 ### C语言字符串处理函数库 #### atof:字符串转浮点型函数 - **函数原型**...

    坐标转换DEMO_C++_四参数_坐标转换_七参数_DEMO

    同时,这个DEMO也可以作为基础,进一步扩展到其他类型的坐标转换,如三参数、八参数等,或者与其他GIS库(如GDAL、GEOS)集成,以实现更复杂的功能。 总之,"坐标转换DEMO_C++_四参数_坐标转换_七参数_DEMO"是一个...

    MJD_YMD.C.rar_MJD to YMD_YMD MJD_mjd_秒转换

    标题"MJD_YMD.C.rar_MJD to YMD_YMD MJD_mjd_秒转换"指出,这个压缩包包含了一个名为"MJD_YMD.C.txt"的文件,它与时间戳转换有关,特别是涉及到从Modified Julian Date(MJD)到Year-Month-Day(YMD)格式的转换,...

    2016年计算机二级c语言题库及答案.doc

    题目涉及到的知识点包括: * 变量的声明和初始化 * 数据类型的转换 * 运算符的优先级 * 控制结构的使用 * 函数的定义和调用 * 数组的声明和操作 * 指针的使用 这些题目涵盖了C语言的基础知识,可以帮助学习者熟悉...

    stm32标准库进制转换.rar

    STM32标准库是ARM公司推出的...总的来说,STM32标准库虽然没有直接提供进制转换的函数,但结合C语言标准库和自定义函数,我们可以轻松实现各种进制之间的转换。理解并熟练掌握这些技巧,对于STM32开发来说至关重要。

    (完整版)C语言考试题库及答案-必考_计算机c语言题库

    因此,正确答案是 **D) a=b+c=1**。 #### 3. 字符常量 **题目示例**: ```c // 合法的 C 语言字符常量 A) '\t' ``` **解析**:此题考察了C语言中字符常量的定义。选项A中的 `\t` 表示一个制表符,是有效的字符常量...

    Keil对lib封装库反汇编成C语言.pdf

    根据提供的文件信息,本文主要探讨了使用Keil工具对lib封装库进行反汇编并转换为C语言的过程,以及在实际应用中的一些经验分享。以下是对该文档标题、描述及部分内容涉及的关键知识点的深入解析: ### Keil对lib...

Global site tag (gtag.js) - Google Analytics