发信人: tanshuai(天水-S.TS) 
整理人: workingnow(2002-09-30 11:55:47), 站内信件
 | 
 
 
		强强联合——Perl 外部程序语言子程序与类的编程
 Author:天水-S.Tanshuai  Email:[email protected] [email protected]
 OICQ: 66552 ICQ: 25856530 URL: Http://Www.Tanshuai.Net Location: FS, LN, P.R.C 
 Date: 2001-5-19 20:07 Edition: Manuscript Version: 1.0 Current Language: Simplified Chinese
 
 简介
 安装
 程序设计语言方法实现:
 	C
 	C++
 	Java
 	Python
 	其它语言
 联合使用
 另一种方式——CPR
 结语
 
 简介
   我们知道Perl是目前最古老最强大的脚本程序语言,它的特点显而易见,就是快速开发应用和拥有广域的扩展性。Perl是一门高级,它和Java、Python一样在处理某些特殊任务的时候,仍显得力不从心,需要C/C++这些底层语言的支持,这就出现了扩展功能,例如Perl需要XS,Java需要JNI。然而实现这些方法并非易事。首先每一种扩展都会有它的特性,有它的不同,必须按照扩展对象的规定进行相应编写。如果你写了一个扩展程序,那么如果要用在Perl和Java上,你就要分别去写不同的接口。因此出现了很多更好的解决方案,这些不仅仅是对程序语言的扩展,我们知道,每一种程序语言都有它们的优点与其弱点。然而常常又因为开发周期和成本而无法用一种语言进行解决,因为这样很浪费时间。同时,不是每个人都对同一语言都是那么了解,每个人有自己擅长的程序语言。在Win32系统上,有ActiveX控件,和COM的实现方法,来解决这样的问题。然而这些仍然不是那么完善,Perl在Win32做控件,也是一种可行的方式。然而在Unix-Base系统上如何呢?而且控件仍然有其局限。
   我想很多程序员也有和我同样的想法,如果流行的(当然所有的更好)程序语言都能够在一种环境下进行完美的编写那么该多好啊?但是这个想法在Perl语言中实现起来并不难。
   另外我想说,本文除了对Perl程序员有很大的帮助之外,也绝对适合C、Java和Python程序员。因为不论在控制结构和函数调用方面都非常相似,甚至是一样的。这些程序员最多用一天就可以了解Perl的基本编写方法。如果你正在为多语言联合编程犯愁,那么用这个方法也不失为一种解决方案。
 	//C Programming
 	#include <stdio.h>
 	main (){
 		char *str = "C 语言\n"
 		printf("%s",str);
 	}
 	
 	//Java Programming
 	class Hello {
 		public void static main(String argvs[]) {
 			String Str = " Java 语言\n";
 			System.out.print(str);
 		}
 	}
 	
 	#Python Programming 
 	String = "Python 语言\n"
 	print String
 	
 	//Perl Programming Perl6开始支持该种注释方法
 	$String = "Perl 语言\n";
 	print $String
 	//
   这样,感觉起来有些不可思议,但是的确可以做到,这里面都是实现的方法(void模式),但是我们一般编程都需要函数见的数值传递,这里虽然没有体现到但是仍然可以做到。
   Perl强大的嵌入功能,在这个应用上又一次体选出来了。Perl嵌入HTML 编程已经不是什么新鲜事务,我们也可以PerlDoc和Perl.COM的文章中找到如何把Perl嵌入在C中(利用Perl API),已经如何把C嵌入在Perl 里面(PerlXS),以及Perl发布代码中也包括如何把Perl嵌入在Java代码中(JPL),用Perl的语法调用Java对象等。但是这些方法非常复杂,兼容能力也很差,有一定的特性,所以利用起来也不是那么顺手。
 
 安装
   Perl这一外部程序语言子程序编程的功能也是最近才发布,所以Perl本身的版本是没有附带的,需要到CPAN下载该功能模块。这个模块叫做"Inline"。可以在任何操作系统平台上应用。
   你需要有一个Perl5的解析器,当然最好是最新版本的Perl5.6.1 Stable。然后下载核心模块Inline,继而根据需求不同的程序语言来下载相应的子模块。目前Inline支持汇编(ASM)、C、C++、Java、Python、TCL程序语言,其中还包括一个CPR的Perl嵌入C的特殊实现模式。
 下载地址:
 核心模块(默认包含C的嵌入功能):http://www.cpan.org/authors/id/I/IN/INGY/Inline-0.34.tar.gz
 子模块
 	汇编语言(ASM):http://www.cpan.org/authors/id/N/NE/NEILW/Inline-ASM-0.02.tar.gz
 	C++语言:http://www.cpan.org/authors/id/N/NE/NEILW/Inline-CPP-0.20.tar.gz
 	Java语言:http://www.cpan.org/authors/id/P/PA/PATL/Inline-Java-0.21.tar.gz
 	Python语言:http://www.cpan.org/authors/id/N/NE/NEILW/Inline-Python-0.14.tar.gz
 	TCL语言:http://www.cpan.org/authors/id/R/RR/RRS/Inline-Tcl-0.08.tar.gz
 	CPR功能:http://www.cpan.org/authors/id/I/IN/INGY/Inline-CPR-0.11.tar.gz
 	
   另外,你必须保证每个语言的编译器或解析器的存在。例如,你想要使用C/C++嵌入,你必须保证你拥有C++编译器,Win32是CL(VC),Unix是CC或者GCC;Java当然需要JDK;Python需要Python解析器。而且你也要保证需求模块、类、库等调用对象的存在,例如C的函数库,Java的类库,Python的模块等。
 
   下载相应模块后,进行编译安装,你必须拥有一个C语言的编译器,如果是ActivePerl,可以利用PPM。首先安装的是核心模块。所有的Perl模块的安装流程都是一样的:
 	perl Makefile.PL<Win32/Unix>
 	这个时候一般自动完成,并结束,但是有时候需要你输入一些量,例如安装目录等,核心模块是会问你是否默认安装C嵌入功能。
 	nmake <Win32> make<Unix>
 	进行编译,如果没有PerlXS,直接建立目的文件的目录结构。
 	nmake test<Win32> make test<unix>
 	这个步骤一般不需要做,但是如果你希望检查是否编译正确。
 	nmake install<Win32> make install<unix>
 	进行安装,把编译好的和建立的目录结构一同复制于目的目录中。
 	
 程序设计语言方法实现
   一切安装配置就需有,就可以正式进行多语言编程了。本文选择几种常用语言进行讲解,其它语言读者可根据需求,阅读相应文档,也可以直接咨询与我。
 
 	C语言:
 	  把C语言直接写入Perl代码中,轻而易举,我们知道在源代码中如果想把一个数据写入变量,是需要遵循一定的规则,例如:
 	char *t = "XXX";是正确的,然而,我们平时写程序的时候,变量内容不一定是这样的,例如Web编程时,就需要使用HTML,那么实现起来	是非常费力的,我们需要在遇到“"”的时候,添加控制符号“\”,即使在Java 也没有充分解决这个问题,然而Perl就不需要。因为Perl的字符处	理功能实在是太强大了。
 	  简单的实现:
 		#!perl
 		greet('天水');#是Perl语言。
 		greet('Sam Tanshuai');
 		use Inline C => <<'END_OF_C_CODE';
 			#include <stdio.h>
 			void greet(char* name) {
 				printf(您好: %s!\n", name);
 			}   
 		END_OF_C_CODE
 	        输出结果是:
 	        D:\Documents\Tests\Perl>perl InC.pl
 		您好: 天水!
 		您好: Sam Tanshuai!
 	       ============================
 	  我们注意到,C代码是在调用语句的后面,按照一般的编写原理,这样是不可以的,但是use是在使用的时候是最高优先级,所以		use所属语句具有优先执行的能力,否则的话,必定出错。你发现了,在Perl里面的调用方法,不需要什么改动哦。这也就是我们为什么说“		Perl 外部程序语言子程序编程”,而不是多语言联合编程。它就是把一种语言当作自己的子程序去执行。
 	  上面我们介绍的是向C语言子程序传递函数,进行操作,如果需要C语言返回函数,该如何去做呢?方法很简单:
 		#!perl
 		$add = add(1, 1);
  		$subtract = subtract(9, 100);
  		print "一加一等于:$add\n";
  		print "九减去一百等于:$subtract\n";
 		
 		use Inline C => <<'END_OF_C_CODE';
 			    #include <stdio.h>
 			    int add(int x, int y) {
       				return x + y;
     			   }
 			    int subtract(int x, int y) {
       			      return x - y;
 			    }
 		END_OF_C_CODE
 		输出结果是:
 		D:\Documents\Tests\Perl>perl InC.pl
 		一加一等于:2
 		九减去一百等于:-91
 		=============================
 	  至此程序编写的void方式和return方式都已经告知大家了,C可以使用指针,但是Perl不能引用C的指针,所以这是一个“遗憾”,因为如果	实现这种方法是存在难度的。
 	  环境定义配置:
 	  我们知道在编译C 的时候需要各种参数,例如INC和Lib的位置。因为嵌入编程,是外部调用的方式,这种方式类似于JSP,是编译后调用	。一般情况,直接perl就可以,但是有些时候考虑到移植等特殊问题,就需要稍微改动,这些功能可以在Perl初始化Inline的时候完成。
 		AUTO_INCLUDE : 自动使用 include功能,代替在C语言中使用的#include语句
 		use C => Config => AUTO_INCLUDE => '#include "yourheader.h"';
 		CC :你希望使用哪一个C语言编译器,一般情况下,它会使用编译Perl时候的编译器,如果你的系统有多个编译器,希望指定,可	以使用该方法。
 		INC: 也就是 C 头文件的搜索路径。
 		use C => Config => INC => '-I/inc/path';
 		LIBS: 库文件搜索路径。
 		use C => Config => LIBS => '-lyourlib';
 		TYPEMAPS :指定使用额外的typemape文件
 		use C => Config => TYPEMAPS => '/your/path/typemap';
 	  目前在Perl Inline-C的模式中支持一下数据类型:
 		 - int
 	 	- long
  		- double
  		- char*
  		- void
  		- SV* Perl的API数组
 		
 	C++:
 	  这里的C++和C没有太大的差异。只是要在编译的时候进行具体分析:
 		use Inline CPP => <<'END';
    			int doodle() { }
    			class Foo {
      			public:
        				Foo();
        				~Foo();
 					int get_data() { return data; }
        					void set_data(int a) { data = a; }
      					private:
        					int data;
    			};
    			Foo::Foo() { cout << "创建Foo()" << endl; }
    			Foo::~Foo() { cout << "删除 Foo()" << endl; }
 		END
 	  在这里涉及到了面向对象的程序设计的方法,所以需要特别注意。
 		#!perl
 		use Inline CPP;
 		my $q = new Queue;#创建对象 Queue,这个对象是C++的。
 		$q->q(50);#访问对象Queue的q函数,并传送整数"50"。
 		$q->q("我是谁?");#访问对象Queue的q函数,并传送字符串。
 		$q->q("我就是我。");
 		print "一共有", $q->size, "查询项目。\n";
 		while($q->size) {
 		     print "关于:  ", $q->peek, "\n";
 		     print "实际: ", $q->dq, "\n";
 		}
 		
 		my $s = new Stack;
 		$s->push(42);
 		$s->push("什么?");
 		print "一共有 ", $s->size, "查询项目。\n";
 		while($s->size) {
 		     print "关于 :    ", $s->peek, "\n";
 		     print "实际: ", $s->pop, "\n";
 		}
 		
 		
 		__END__
 		__CPP__
 		
 		
 		class Queue {
 		   public:
 		     Queue(int sz=0) { q = newAV(); if (sz) av_extend(q, sz-1); }
 		     ~Queue() { av_undef(q); }
 		     int size() {return av_len(q) + 1; }
 		     int q(SV *item) { av_push(q, SvREFCNT_inc(item)); return av_len(q)+1; }
 		     SV *dq() { return av_shift(q); }
 		     SV *peek() { return size() ? SvREFCNT_inc(*av_fetch(q,0,0)): &PL_sv_undef;}
 		   private:
 		     AV *q;
 		   };
 		
 		   class Stack {
 		   public:
 		     Stack(int sz=0) { s = newAV(); if (sz) av_extend(s, sz-1); }
 		     ~Stack() { av_undef(s); }
 		     int size() { return av_len(s) + 1; }
 		     int push(SV *i) { av_push(s, SvREFCNT_inc(i)); return av_len(s)+1; }
 		     SV *pop() { return av_pop(s); }
 		     SV *peek() { return size() ? SvREFCNT_inc(*av_fetch(s,size()-1,0)) : &PL_sv_undef; }
 		    private:
 		     AV *s;
 		};
 	  在这里我们不难发现,Perl调用C++ 的对象和调用Perl的对象是一样的,是不是感觉还顺手呢?不知道什么时候也可以在其它程序语言中	这样做。
 
 	Java:
 	  我除了喜欢Perl之外,其次就是Java啦。Java的确很不错,但是在编程的速度上,的确不如Perl,Java可以用好多不同的方法,实现一个功	能,的确很灵活,但是也非常麻烦。我用Java读一个文件的所有内容赋值到一个变量上,都搞了半天。
 	  Java也是一个面向对象的程序设计语言,所以我们又要涉及对象概念了,我们按照上面C的例子,用Java来实现:
 		#!perl
 		my $java = new java();#创建新的对象
 		$add = $java->add(1, 1);#返回输出数据计算结果
  		$subtract = $java->subtract(9, 100);#返回输出数据计算结果
  		
  		$java->prt("一加一等于:$add");#利用Java函数进行输出
  		$java->prt("九减去一百等于:$subtract");#利用Java函数进行输出
 		
 		use Inline Java => <<'END_OF_JAVA_CODE';
       		class java {
          			public java(){
          			}
         			public int add(int i, int j){
             				return i + j ;
          			}
          			public int subtract(int i, int j){
             				return i - j ;
          			}
          			public void prt(String s){
          				System.out.println(s);
          			}
       		}   
 		END_OF_JAVA_CODE
 	  方法,我们知道创建一个对象的时候后,可以向该对象发送初始化的信息。在Perl中:
 		#!perl
 		use XXX;
 		$ooXX = new XXX (Name=>xxx, Value=>0000);
 		$ooXX->do();
 	        在Java中也可以实现这种方式,调用方法也是一样的:
 		use Inline Java => <<'END';
 		class javaMethod {
          			public Foo() {
             				...
          			}
          			public Foo2(int i, String str, Foo k) {
             				...
          			}
       		}
    		END
    		my $obj = new Foo() ;
    		my $obj2 = new Foo2(001, "String", $obj) ;
 	  环境定义配置:
 	    Java也需要进行相应的配置,当然一般的默认情况下,都可以完成的。
 	   	BIN:设置Java二进制执行文件目录路径,放置java解析器和javac编译器文件的位置。
 	   	CLASSPATH:设置Java类的所在位置,一般情况通过系统环境变量也可以完成。
 	   	一个例子: 
 	   	 use Inline {
       			Java => 'DATA',
       			BIN => '/usr/jdk1.3/bin/',
       			CLASSPATH=>'/yourhome/path/;anyjarfile';
    		) ;
 
 
 	Python:
 	  Python是一个很好的脚本程序语言,它的语法非常简洁,就好像写一篇文章一样,类似本文,但是也因此,有的时候程序总是出现莫名	其妙的错误,原来只是因为一个制表符 TAB。4月1日愚人节那天,Perl.com和Python.org发布了一条新闻,声称Perl和Python语言进行合并,	并且取名为"Parrot",但是我都被骗了。
 	  不过即使没有"Parrot",Perl也可以完全适用Python。
 	  按照上面的程序继续下来,我们来些Python版本的加加减减:
 		#!perl
 		$add = add(1, 1);
  		$subtract = subtract(9, 100);
  		print "一加一等于:$add\n";
  		print "九减去一百等于:$subtract\n";
    		use Inline Python => <<'END_OF_PYTHON_CODE';
    		def add(x,y): 
       			return x + y
    		def subtract(x,y):
       			return x - y
    		END_OF_PYTHON_CODE
 	  方法和C是一样的,但是Python也涉及面向对象的程序设计。这里使用Data::Dumper模块来输出数据结构。
 		#!perl
 		 use Inline Python => <<'END';
 		 class Foo:
       		 def __init__(self):
          		 print "new Foo object being created"
          		 self.data = {}
       		 def get_data(self): return self.data
       		 def set_data(self,dat): 
          		 self.data = dat
    		 END
    		use Data::Dumper;
    		my $obj = new Foo;
    		print Dumper $obj;
    		print Dumper $obj->get_data();
    		$obj->set_data({string => 'hello',
                    		number => 0102,
                    		array => [1, 2, 3],
                   		});
    		print Dumper $obj->get_data();                  
 	  输出结果为:
    	         D:\Documents\Tests\Perl>perl Python.pl
    	         $VAR1 = bless( do{\(my $o = 135870536)}, 'main::Foo' );
    	         $VAR1 = {};
    	         $VAR1 = {
                             	'string' => 'hello',
              		'array' => [
                           	'1',
                           	'2',
                           	'3'
                         	],
              		'number' => '102'
            	          };
            	         ==========================================
            	          Eval:
            	            因为Python和Perl一样是一个很纯脚本语言,所以它们都支持eval功能,这是一个很强大的功能,在perl中也可以直接使用python的	          eval来完成相应功能。
            	            方法如下:
 	           	            $SC = "print 'hello'";
            		                  eval_python("$SC")
 	           	                  eval_python("<perl 包>", "<python 函数>", <参数>...)
    			eval_python("<perl 包>", "<python 方法>", <对象>,  <参数>...)
    		实现例程:
    			use Inline::Python qw(eval_python);
    			eval_python("def test(): return {'第一': 1, '第二': 2}"); # 返回一行内容
    			eval_python("test()"); #返回整数 1。
  	  Python的功能越来越强大,也受到商业的支持,如果你在Perl编程方面已经有很好的造诣,那么学习Python决非难事,一般情况下两三天  之内就可以掌握。
 	其它语言:
 	  因为我目前擅长的语言中,只有C、Java、Perl和Python,所以其它语言在此就不详细见解,例如:汇编、TCL等并不流行,再此我将讲	解Inline模块的通用使用方法,大家再根据以上例子进行对照,就很容易掌握。
 	  下载相应的语言子模块,使用Inline进行调用:
 		use Inline <语言名称>
 		例如:use Inline C;#C语言
 		            use Inline CPP;#C++语言
 		            use Inline Java;#Java语言
 		            .....
 		 程序结构:
 		 	标准结构:
 		 		use Inline <语言名称> => <<'END_OF_XXX_CODE';
 		 		<相应程序代码主体>
 		 		END_OF_XXX_CODE
 		 	简明结构:
 		 		use Inline <语言名称>; 
     				<Perl调用代码主体>
 				__END__
     				__ <语言名称>__
     				<相应程序代码主体>
     			复合规范:
     				use Inline C;
     				use Inline Java;
     				use Inline Python;
     				<Perl应用代码主体>
     				__C__
     				<C 语言代码主体>
     				__C__
     				<第二个 C语言代码主体>
     				__JAVA__
     				<Java语言代码主体>
     				__Python__
     				<Python语言代码主体>
     				
 	  使用选项:
 	    我们在使用其它程序语言的时候,需要对编译、解析等方法进行定制,这里介绍如何定制某个语言的选项。
 		C选项的例子:
 		   use Inline (C => Config =>
 		  	DIRECTORY => './inline_dir',
                 			LIBS => '-lfoo',
                 			INC => '-I/foo/include',
                 			PREFIX => 'XXX_',
                 			NOWARN => 1,
                			);
     		use Inline C => <<'END_OF_C_CODE';
     		
 联合应用
   我们把话题转回最初的设想,把Perl、C、Java、Python这四个语言,写在一个程序内。
 	#!perl
 	use Inline C;
 	use Inline Java;
 	use Inline Python;
 	
 	#Perl Programming Perl6开始支持该种注释方法
 	$String = "Perl 语言\n";
 	print $String
 	#
 	
 	__C__
 	//C Programming
 	#include <stdio.h>
 	main (){
 		char *str = "C 语言\n"
 		printf("%s",str);
 	}
 	
 	__JAVA__
 	//Java Programming
 	class Hello {
 		public void static main(String argvs[]) {
 			String Str = " Java 语言\n";
 			System.out.print(str);
 		}
 	}
 	
 	__Python__
 	#Python Programming 
 	String = "Pthyon 语言\n"
 	print String
 	输出结果为:
 	Perl 语言
 	C 语言
 	Java 语言
 	Python 语言
 	
 	  这是一个简单的联合例子,让我们在复杂一些,用这四种语言做不同分工,由C语言计算加法,Java语言计算减法,Python语言计算乘法	,最有由Perl进行除法后输出。
 	
 	#!perl
 	use Inline C;
 	use Inline Java;
 	use Inline Python;
 	my $java = new subtract();
 	my $C =  add(5,6);
 	my $J = $java->do($C,3);
 	my $P = x($J,10);
 	my $R = $P/3;
 	print "最终结果: $R\n";
 	
 	__C__
 	//C Programming
 	#include <stdio.h>
 	int add (int x,int y){
 		return x+y;
 	}
 	
 	__JAVA__
 	//Java Programming
 	class subtract {
 		public int do(int x,int y) {
 			return x-y;
 		}
 	}
 	
 	__Python__
 	#Python Programming 
 	def x(x,y): 
       		return x * y
 	输出结果为:
 	最终结果:26.6666666666667
 	==============================
 
 另一种方式——简单的Perl嵌入C
    CPR(C Perl Run)是另一种的功能,是把Perl代码放入C中,不同的是这个C是不能成为正式的编译的C。而是一种Script脚本模式。但是它仍然比利用API方法嵌入的C方便得多,在这里是一个eval函数功能。
   方法如下:
      	int main(void) {       
         		printf("Hello World, I'm running under Perl version %s\n",
                		CPR_eval("use Config; $Config{version}")
               		); //CPR_eval就是使用了Perl的语言方式。
         		return 0;
     	}
 
   它和Perl Inline的标准模式不一样,不使用Perl进行解析,而是输入cpr xxx.cpr就可以运行。但是该程序不支持Win32系统平台。
   目前CPR只能在以下函数原型运行:
 	int main(void);
   不可以在以下方式运行:
 	int main(int argc, char* argv[]);
   
 结语
   C语言是采用API动态加载模式运行的,即启动Perl解析器后自动加载API扩展。开始我以为是启动了另一个进程做线程通讯,但是我通过Windows“任务管理器”查看进程的时候,只有一个Perl,说明Perl在处理嵌入式多语言方面的能力的确强大。
   正是因为Perl有用强大的字符串处理能力,才可以让不同格式代码写入在一个程序中。之前我们一般使用COM CORBA这些技术来进行不同程序语言的通讯,它们有它们的好处。但是作为一个小型应用开发,是不可能启动一两个服务器端进行调用,而且仍然要涉及到平台移植的问题。
   Perl强大的功能,将会是您提高开发效率的有力工具。
   小生未尽之处,可以发送信息给我,我将会尽力解答你问题。我的电子邮件:[email protected] [email protected] ICQ:25856530 QQ:66552
 
  ---- ☆CGI技术版主★
 
 天水——Sam Tanshuai~小帅~
 <img src="http://uh1.gz.163.com photo?name=tanshuai"> 
 小型应用程序设计师
 E-Mail:[email protected]
 Homepage:www.tanshuai.net  
  OICQ:66552 ICQ:25856530  
 游子久伫立,极目望一径。蜿蜒复曲折,隐于丛林中。我选另一途,合理亦公正。草密人迹罕,正待人通行。
 足迹踏过处,两路皆相同。两路林中伸,落叶无人踪。我选一路走,深知路无穷。我疑从今后,能否转回程。 
 数十年之后,谈起常叹息。林中两路分,一路人迹稀。我独进此路,境遇乃相异。与众历不同,人生何精彩?  | 
 
 
 |