Chinaunix首页 | 论坛 | 博客
  • 博客访问: 450618
  • 博文数量: 45
  • 博客积分: 2526
  • 博客等级: 少校
  • 技术积分: 478
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-12 21:04
文章分类

全部博文(45)

文章存档

2014年(1)

2011年(1)

2010年(3)

2009年(22)

2008年(18)

我的朋友

分类:

2009-04-12 08:59:51

1. Introduction
项目中需要测试一组C API,除了开发人员那边的单元测试,我们可能还需要对这组API进行一些功能
测试. 对于我这样好久没写C程序的懒人来说,写C程序来测试API实在是在很痛苦的事情. 感觉写
C程序必须小心翼翼,不然一步小心就内存泄露. 如果可以用perl/python等脚本语言来直接
调用C函数, 问题就解决了.

2. Solution
在Perl中,有三种方式可以实现这样的想法:
1. XS
XS是perl的接口编程语言,可以方便的写出perl的扩展.
2. Inline
此perl模块可以让用户在perl程序中直接内嵌其他语言的代码,这些其他语言的代码会自动编译,然后在
运行的时候自动加载.本质上Inline也是基于XS的.
3. SWIG
SWIG是个帮助使用C或者C++编写的软件能与其它各种高级编程语言进行嵌入联接的开发工具。SWIG能应
用于各种不同类型的语言包括常用脚本编译语言例如Perl, PHP, Python, Tcl, Ruby and PHP.


上述3者的关系可以用下图表示:
_________________________
|       C/C++ Code       |  
|________________________|
| Inline |               |  
|________|_______________|
|     XS    |   SWIG     |  
|___________|____________|
|         Perl Core      |  
|________________________|



3. Inline Example
首先尝试下第二种Inline::C, 相比XS来说,inline的学习曲线可能小些. 但是你还是得研究下列文档:
1)perlapi 2)perlguts 2)perldoc Inline. 内联的一个非常实用的地方是可以依据 C语言的库
编写出快速包装代码并将在 Perl上使用它,这样(就我而言)就可以将 Perl变成一个很好的测试平台.
内联得到您的 C 语言代码,然后在它基础上构建一个 XS 文件,就像一个人工的扩展模块编写器所执行
的一样,接着创建那个模块,然后装载它。接下来的代码调用就会很容易找到以前构建过的那个模块,然
后直接装载它.

总的来说,大体步骤分为步:
a. 在Inline中设置C API的库路径和头文件路径. 这个跟gcc的用法的一致. 例如:
use Inline C => Config => MYEXTLIB => "/home/ray/study/perl/inline/share/lib/libmy_ctags.so",
           INC => "-I/home/ray/study/perl/inline/share/include";
#          LIBS => "-L/home/ray/study/perl/inline/share/include -lmy_ctags";

b. 选择perl程序中C代码的导入方式. 这样有很多方式,最省事的就是用string,
use Inline C => <<'END_CODE';
// Your C source code here
END_CODE
还可以把perl内嵌c程序单独做成一个文件,然后把这个文件内容保存在一个变量中,然后再导入到inline中.
$source_code_string = read(file);
use Inline C => $source_code_string;

c. 编写C API的warpper函数,方便让perl调用.特别需要注意的地方就是合理的映射C和perl的数据结构.
如果某C函数A中需要返回指针, 那么A的wrapper函数就必须返回一个引用,这样perl中才可以直接调用.
如果A中指针的内容是数组,那么wrapper函数中对应就必须有一个数组HV.
如果A中指针的内容是定义的struct,wrapper中就可以映射成一个哈希表AV.

下面写个例子实际操作下,参考的C API就是我的一篇文章.
http://blog.chinaunix.net/u2/68938/showart_718163.html

工作路径如下
|-- MyCtags.pm
|-- ctags.pl
|-- include
|   `-- my_ctags.h
|-- lib
|   `-- libmy_ctags.so
`-- src
    |-- my_ctags.c
    `-- my_ctags.h

这里面src目录是souce code, 生成共享库, 然后放到lib目录下.
gcc -o libmy_ctags.so -shared my_ctags.c
此共享库中,有3个开放的API, 可以创建一个tag list,然后打印出来.
taglist* setup_taglist(char *filename, const char* pattern);
void output_taglist(taglist *list);
int cleanup_taglist(taglist **list);

ctags.pl就是一个测试脚本,简单的测试了Myctags.pm

MyCtags.pm就是此共享库的perl语言绑定, 让用户可以创建一个tag class, 然后打印出来.
列举几个简单的用法, 具体用法参见ctags.pl.
 my $tags_ref = MyCtags->new($filename, $pattern);
 $tags_ref->dump();

最后把所有代码打包出来,与大家共享.
文件:taglist.zip
大小:8KB
下载:下载

[ray@localhost share]$ cat -n MyCtags.pm
     1    package MyCtags;
     2    
     3    use warnings;
     4    use strict;
     5    
     6    use Inline C => Config => MYEXTLIB => "/home/ray/study/perl/inline/share/lib/libmy_ctags.so",
     7     INC => "-I/home/ray/study/perl/inline/share/include";
     8    # LIBS => "-L/home/ray/study/perl/inline/share/include -lmy_ctags";

     9    
    10    sub new
    11    {
    12     my ($class, $filename, $pattern) = @_;
    13    
    14     my $self = {};
    15     $self->{'taglist'} = __setup_taglist($filename, $pattern);
    16    
    17     return bless $self, $class;
    18    }
    19    
    20    sub dump
    21    {
    22     my ($self) = @_;
    23    
    24     __output_taglist($self->{'taglist'});
    25    
    26    }
    27    
    28    sub get_taglist
    29    {
    30     my ($self) = @_;
    31    
    32     if ( $self->{'taglist'}) {
    33    
    34     return $self->{'taglist'};
    35     }
    36     else {
    37     return undef;
    38     }
    39    
    40    }
    41    
    42    sub DESTROY
    43    {
    44     my ($self) = @_;
    45    
    46     __cleanup_taglist($self->{'taglist'});
    47    }
    48    
    49    use Inline C => <<'END_CODE';
    50    
    51    #include "my_ctags.h"

    52    
    53    // List head
    54    static taglist* local_taglist;
    55    
    56    SV* __setup_taglist(char* filename, char* pattern)
    57    {
    58     taglist *loop;
    59     AV* list = newAV();
    60    
    61     printf("call setup_taglist in C API\n");
    62     local_taglist = setup_taglist(filename, pattern);
    63    
    64     //Convirt C structure to PERL data
    65     int i = 0;
    66     for(loop = local_taglist->tag_next; i < local_taglist->tag_num; i++)
    67     {
    68    
    69     //Get every tag info, like func name, line number.
    70     char* funcname = loop->tag.func_name;
    71     char* filename = loop->tag.file_name;
    72     unsigned int linenum = loop->tag.line_num;
    73     loop = loop->tag_next;
    74    
    75     // Store the tag info into a hash of PERL, see perlapi for more help
    76     HV* tag = newHV();
    77     hv_stores(tag, "func_name", newSVpv(funcname, strlen(funcname)));
    78     hv_stores(tag, "file_name", newSVpv(filename, strlen(filename)));
    79     hv_stores(tag, "line_num", newSVuv(linenum));
    80    
    81     // Append the above new tag HASH to an array of PERL
    82     av_push(list, newRV_inc((SV*) tag));
    83     }
    84    
    85     return newRV_inc((SV*) list);
    86    }
    87    
    88    void __output_taglist(SV* list)
    89    {
    90     printf("call output_taglist in C API\n");
    91    
    92     taglist* my_taglist = local_taglist;
    93    
    94     output_taglist(my_taglist);
    95    
    96     my_taglist = NULL;
    97    
    98    }
    99    
   100    void __cleanup_taglist(SV* list)
   101    {
   102     printf("call cleanup_taglist in C API\n");
   103    
   104     taglist* my_taglist = local_taglist;
   105    
   106     cleanup_taglist(&my_taglist);
   107    
   108    }
   109    
   110    END_CODE
   111    
   112    1;

阅读(2858) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

crook2010-04-08 12:43:31

我没找到办法,你找到了发邮件个我。

chinaunix网友2010-03-30 14:54:37

我也发现 使用 LIBS => "-L/home/ray/study/perl/inline/share/include -lmy_ctags" 的方式 perl找不到这个库。 只有指出确切路径才行。这是为什么?有什么办法解决么?