perl C/C++拡張(五)
32566 ワード
perlのC++拡張は、カスタムタイプの値を返します.
perl C/C++拡張(三)では、perlにc++のクラスを認識させる方法を紹介しましたが、前の紹介では、私が参考にしたブログも含まれています.http://chunyemen.org/archives/493を参照してください.開発者にとって,戻り値がベースタイプにすぎないと,拡張に対する開発意欲が大幅に低下する.スレ主は万難を排して、ついに『高級perlプログラミング(第2版)』.(美)simon cozensという本の第18章と第20章で少し啓発された.
遊び方をご紹介します.
まずCatという新しいプロジェクトを作成します
プロジェクトを作成してCatディレクトリに入ります
mylibディレクトリを作成し、c++コードをmylibディレクトリにコピーします.
Cat.h
Cat.cpp
Animal.h
Animal.cpp
mylibディレクトリの下にMakefile.PLファイルを作成する
mylib/Makefile.PL
注意:mylib/Makefile.PLの16 17行前はスペースキーではなく、tableキーです.ここはMakefileのコードなので、Makefile形式で書かないと、make操作がエラーになります.
Catディレクトリに戻る
typemapファイルを作成し、次のように書きます.
typemap
このtypemapファイルはxsに新しい定義を識別させるクラスです.(3)ではMakefile.PLファイルにperlobjectが指定されている.mapのファイルは、実は内容がこれとあまり違わないので、そんなに多くの別名を定義する必要はありません.
Make f ile.PLの修正
赤いコードは、コードを追加または変更します.注意:26行のコードの前にtableキーがあり、通常のスペースではありません.
関数MY::postmableはMakefileファイルに挿入されたコードで、Catの拡張ライブラリをコンパイルする前に、まず依存するlibanimal.soパッケージをコンパイルする役割を果たします.
残りの追加コードは、主にg++を使用するコンパイルを定義します.もう一度注意してください.第1行コードuse 5.018002に注釈する必要があります.
Cat.xsファイルの変更
Cat.xs
フォーマットは前の(3)と同じで、ここではもう展開しません.
Makefileファイルの生成、コンパイル
##########################################################
第2部では、Animalのプロジェクトを作成します.
Catエンジニアリングのmylibソフトリンクを
typemapの作成と編集
実は、ここが今回のブログの核心です.
AnimalエンジニアリングのtypemapとCatエンジニアリングのtypemapの違いをよく観察した.AnimalエンジニアリングのtypemapにCATが1つ増えましたOBJECTの定義、CAT_OBJECTのOUTPUT(9行目)は、Catを指すと明記されたクラスです.
前のCatエンジニアリングコンパイルコマンドをよく見ると
Cat.cこのファイルはmakeコマンドの実行時に生成されたファイルで、Cat.cファイルを開いて見ると面白いものが見つかります
上のコードは実際にCat::new()のリアルコードです.CLASSの変数は、文字列配列であることがわかります.実験中にCLASS文字列を印刷したところ、Catのエンジニアリング名「Cat」が記録されていたことがわかりました.
ここまで分析すると、戻り値がカスタムクラスである場合は、「CLASS」フィールドを独自のエンジニアリング名に書くだけで、簡単に推測できます.
興味のある学生も、なぜCLASSが現在のエンジニアリング名を自動的に認識するのか、(3)のperlobject.mapファイルの他の遊び方と深く掘り下げることができます.
後のことは簡単ですが、Makefile.PLとAniaml.xsファイルを修正します.
Makefile.PL
赤い部分は内容を変更します.
注意:27行のコードの前は通常のスペースではなくtableキーです.
Animal.xs
Makefileを生成してコンパイルする
テストコードの作成test.pl
環境変数の追加
Animalの拡張パッケージの動的ライブラリはAnimalにあります エンジニアリング blib/arch/auto/Animalディレクトリ、pmファイルはAnimalエンジニアリングのlibディレクトリ
同様に、Catの拡張パッケージダイナミックライブラリはCatエンジニアリングのblib/arch/auto/Animalディレクトリの下にあり、pmファイルはCatエンジニアリングのlibディレクトリの下にある.
同時にlibanimal.soファイルをLD_LIBRARY_PATH環境変数に追加する必要があります.
テストプログラムを実行します
出力:
テストに成功しました.
perl C/C++拡張(三)では、perlにc++のクラスを認識させる方法を紹介しましたが、前の紹介では、私が参考にしたブログも含まれています.http://chunyemen.org/archives/493を参照してください.開発者にとって,戻り値がベースタイプにすぎないと,拡張に対する開発意欲が大幅に低下する.スレ主は万難を排して、ついに『高級perlプログラミング(第2版)』.(美)simon cozensという本の第18章と第20章で少し啓発された.
遊び方をご紹介します.
まずCatという新しいプロジェクトを作成します
h2xs -A -n Cat
プロジェクトを作成してCatディレクトリに入ります
cd Cat
mylibディレクトリを作成し、c++コードをmylibディレクトリにコピーします.
mkdie mylib
Cat.h
#ifndef INCLUDE_CAT_H
#define INCLUDE_CAT_H 1
#include <iostream>
class Cat
{
public:
Cat(char *,int);
Cat(const Cat &);
void display();
char * getName();
int getAge();
~Cat();
private:
char * name;
int age;
};
#endif
Cat.cpp
#include "Cat.h"
Cat::Cat(char * name, int age)
{
this->name = name;
this->age = age;
}
Cat::Cat(const Cat & incat)
{
name = incat.name;
age = incat.age;
}
void Cat::display()
{
std::cout<<"~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~"<<std::endl;
std::cout<<"name="<<name<<"\tage="<<age<<std::endl;
}
char * Cat::getName()
{
return name;
}
int Cat::getAge()
{
return age;
}
Cat::~Cat(){}
Animal.h
#ifndef INCLUDE_ANIMAL_H
#define INCLUDE_ANIMAL_H 1
#include <iostream>
#include "Cat.h"
class Animal
{
public:
Animal(char *, int);
void display();
Cat * getAnimal();
Cat * setAnimal(Cat *);
bool haveAnimal();
~Animal();
private:
Cat * cat;
};
#endif
Animal.cpp
#include "Animal.h"
Animal::Animal( char * name, int age):
cat( new Cat(name, age) )
{}
void Animal::display()
{
std::cout<<"~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~
";
cat->display();
}
Cat * Animal::getAnimal()
{
return cat;
}
Cat * Animal::setAnimal(Cat * lcat)
{
//delete cat;
cat = new Cat(*lcat);
return cat;
}
bool Animal::haveAnimal()
{
//return false;
return 1;
//throw 1;
}
Animal::~Animal()
{
//delete cat;
}
mylibディレクトリの下にMakefile.PLファイルを作成する
mylib/Makefile.PL
1 use ExtUtils::MakeMaker;
2 $Verbose = 1;
3 WriteMakefile(
4 NAME => 'Animal::mylib',
5 SKIP => [qw(all static static_lib dynamic dynamic_lib)],
6 clean => {'FILES' => 'libanimal.so'},
7 'CC' => 'g++',
8 );
9
10 sub MY::top_targets {
11 '
12 all :: static
13 pure_all :: static
14 static :: libanimal.so
15 libanimal.so: $(C_FILES)
16 $(CC) -shared -fpic -g -Wall -o libanimal.so $(C_FILES)
17 $(RANLIB) libanimal.so
18 ';
19 }
注意:mylib/Makefile.PLの16 17行前はスペースキーではなく、tableキーです.ここはMakefileのコードなので、Makefile形式で書かないと、make操作がエラーになります.
Catディレクトリに戻る
cd ../
typemapファイルを作成し、次のように書きます.
typemap
TYPEMAP
Cat * ANIMAL_OBJECT
OUTPUT
ANIMAL_OBJECT
sv_setref_pv($arg, CLASS, (void *) $var);
INPUT
ANIMAL_OBJECT
$var = ($type) SvIV((SV*) SvRV($arg));
このtypemapファイルはxsに新しい定義を識別させるクラスです.(3)ではMakefile.PLファイルにperlobjectが指定されている.mapのファイルは、実は内容がこれとあまり違わないので、そんなに多くの別名を定義する必要はありません.
Make f ile.PLの修正
1 #use 5.018002;
2 use ExtUtils::MakeMaker;
3 # See lib/ExtUtils/MakeMaker.pm for details of how to influence
4 # the contents of the Makefile that is written.
5 $CC = 'g++';
6 WriteMakefile(
7 NAME => 'Cat',
8 VERSION_FROM => 'lib/Cat.pm', # finds $VERSION
9 PREREQ_PM => {}, # e.g., Module::Name => 1.1
10 ($] >= 5.005 ? ## Add these new keywords supported since 5.005
11 (ABSTRACT_FROM => 'lib/Cat.pm', # retrieve abstract from module
12 AUTHOR => 'chen <chen@>') : ()),
13 LIBS => ['-Lmylib -lanimal'], # e.g., '-lm'
14 DEFINE => '', # e.g., '-DHAVE_SOMETHING'
15 INC => '-Imylib', # e.g., '-I. -I/usr/include/other'
16 # Un-comment this if you add C files to link with later:
17 # OBJECT => '$(O_FILES)', # link all the C files too
18 'XSOPT' => '-C++',
19 'CC' => $CC,
20 'LD' => '$(CC)',
21 );
22
23 sub MY::postamble {
24 '
25 mylib/libanimal.so: mylib/Makefile
26 cd mylib && $(MAKE) $(PASSTHRU)
27 ';
28 }
赤いコードは、コードを追加または変更します.注意:26行のコードの前にtableキーがあり、通常のスペースではありません.
関数MY::postmableはMakefileファイルに挿入されたコードで、Catの拡張ライブラリをコンパイルする前に、まず依存するlibanimal.soパッケージをコンパイルする役割を果たします.
残りの追加コードは、主にg++を使用するコンパイルを定義します.もう一度注意してください.第1行コードuse 5.018002に注釈する必要があります.
Cat.xsファイルの変更
Cat.xs
#ifdef __cplusplus
extern "C"{
#endif
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif
#include "ppport.h"
#include "mylib/Cat.h"
MODULE = Cat PACKAGE = Cat
Cat *
Cat::new(char * name, int age)
void
Cat::display()
char *
Cat::getName()
int
Cat::getAge()
void
Cat::DESTROY()
フォーマットは前の(3)と同じで、ここではもう展開しません.
Makefileファイルの生成、コンパイル
perl Makefile.PL && make
##########################################################
第2部では、Animalのプロジェクトを作成します.
h2xs -A -n Animal
Catエンジニアリングのmylibソフトリンクを
ln -sf /home/chen/learn/perl_c/Cat/mylib /home/chen/learn/perl_c/Animal/mylib
typemapの作成と編集
1 TYPEMAP
2 Animal * ANIMAL_OBJECT
3 Cat * CAT_OBJECT
4
5 OUTPUT
6 ANIMAL_OBJECT
7 sv_setref_pv($arg, CLASS, (void *) $var);
8 CAT_OBJECT
9 sv_setref_pv($arg, "Cat", (void *) $var);
10
11 INPUT
12 ANIMAL_OBJECT
13 $var = ($type) SvIV((SV*) SvRV($arg));
14 CAT_OBJECT
15 $var = ($type) SvIV((SV*) SvRV($arg));
実は、ここが今回のブログの核心です.
AnimalエンジニアリングのtypemapとCatエンジニアリングのtypemapの違いをよく観察した.AnimalエンジニアリングのtypemapにCATが1つ増えましたOBJECTの定義、CAT_OBJECTのOUTPUT(9行目)は、Catを指すと明記されたクラスです.
前のCatエンジニアリングコンパイルコマンドをよく見ると
g++ -c -Imylib -D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fstack-protector -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -O2 -g -DVERSION=\"0.01\" -DXS_VERSION=\"0.01\" -fPIC "-I/usr/lib/perl/5.18/CORE" Cat.c
Cat.cこのファイルはmakeコマンドの実行時に生成されたファイルで、Cat.cファイルを開いて見ると面白いものが見つかります
XS_EUPXS(XS_Cat_new)
{
dVAR; dXSARGS;
if (items != 3)
croak_xs_usage(cv, "CLASS, name, age");
{
char * CLASS = (char *)SvPV_nolen(ST(0)) ;
Cat * RETVAL;
char * name = (char *)SvPV_nolen(ST(1))
;
int age = (int)SvIV(ST(2))
;
RETVAL = new Cat(name, age);
ST(0) = sv_newmortal();
sv_setref_pv(ST(0), CLASS, (void *) RETVAL);
}
XSRETURN(1);
}
上のコードは実際にCat::new()のリアルコードです.CLASSの変数は、文字列配列であることがわかります.実験中にCLASS文字列を印刷したところ、Catのエンジニアリング名「Cat」が記録されていたことがわかりました.
ここまで分析すると、戻り値がカスタムクラスである場合は、「CLASS」フィールドを独自のエンジニアリング名に書くだけで、簡単に推測できます.
興味のある学生も、なぜCLASSが現在のエンジニアリング名を自動的に認識するのか、(3)のperlobject.mapファイルの他の遊び方と深く掘り下げることができます.
後のことは簡単ですが、Makefile.PLとAniaml.xsファイルを修正します.
Makefile.PL
1 #use 5.018002;
2 use ExtUtils::MakeMaker;
3 # See lib/ExtUtils/MakeMaker.pm for details of how to influence
4 # the contents of the Makefile that is written.
5 $CC = 'g++';
6 WriteMakefile(
7 NAME => 'Animal',
8 VERSION_FROM => 'lib/Animal.pm', # finds $VERSION
9 PREREQ_PM => {}, # e.g., Module::Name => 1.1
10 ($] >= 5.005 ? ## Add these new keywords supported since 5.005
11 (ABSTRACT_FROM => 'lib/Animal.pm', # retrieve abstract from module
12 AUTHOR => 'chen <chen@>') : ()),
13 LIBS => ['-Lmylib -lanimal'], # e.g., '-lm'
14 DEFINE => '', # e.g., '-DHAVE_SOMETHING'
15 INC => '-Imylib', # e.g., '-I. -I/usr/include/other'
16 'CC' => $CC,
17 'LD' => '$(CC)',
18 # Un-comment this if you add C files to link with later:
19 # OBJECT => '$(O_FILES)', # link all the C files too
20 'XSOPT' => '-C++',
21 #'LDDLFLAGS' => '-r',
22 );
23
24 sub MY::postamble {
25 '
26 $(MYEXTLIB): mylib/Makefile
27 cd mylib && $(MAKE) $(PASSTHRU)
28 ';
29 }
赤い部分は内容を変更します.
注意:27行のコードの前は通常のスペースではなくtableキーです.
Animal.xs
1 #ifdef __cplusplus
2 extern "C"{
3 #endif
4
5 #define PERL_NO_GET_CONTEXT
6 #include "EXTERN.h"
7 #include "perl.h"
8 #include "XSUB.h"
9 #ifdef __cplusplus
10 }
11 #endif
12 #include "ppport.h"
13 #include "mylib/Cat.h"
14 #include "mylib/Animal.h"
15
16 MODULE = Animal PACKAGE = Animal
17
18 Animal *
19 Animal::new(char * name, int age)
20
21 void
22 Animal::display()
23
24 Cat *
25 Animal::getAnimal()
26
27 Cat *
28 Animal::setAnimal(Cat * lcat)
29
30 bool
31 Animal::haveAnimal()
32
33 void
34 Animal::DESTROY()
Makefileを生成してコンパイルする
perl Makefile.PL && make
テストコードの作成test.pl
1 #!/usr/bin/perl
2 use Animal;
3 use Cat;
4 $animal = new Animal("chen",123);
5 $animal->display();
6 $cat = $animal->getAnimal();
7 $cat->display();
8
9 $name = $cat->getName();
10
11 print $name;
12
13 $name = "sdjlfa";
14 $animal->display();
15
16 print "~~~~~~###############~~~~~~~~~~~~~~~
";
17
18 $lcat = new Cat("ASKJKLF",889);
19 $lcat->display();
20 print "~~~~~~###############~~~~~~~~~~~~~~~
";
21
22 $tcat = $animal->setAnimal( $lcat );
23 $animal->display();
24
25 $test = $animal->haveAnimal();
26 print "@@@@@@@@@$test@@@@@@
";
27
28
29 $animal2 = $animal;
30
31 $animal2->display();
環境変数の追加
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/home/chen/learn/perl_c/Animal/blib/arch/auto/Animal:/home/chen/learn/perl_c/Animal/mylib:/home/chen/learn/perl_c/Cat/blib/arch/auto/Cat
export PERLLIB=${PERLLIB}:/home/chen/learn/perl_c/Animal/lib:/home/chen/learn/perl_c/Cat/lib
Animalの拡張パッケージの動的ライブラリはAnimalにあります エンジニアリング blib/arch/auto/Animalディレクトリ、pmファイルはAnimalエンジニアリングのlibディレクトリ
同様に、Catの拡張パッケージダイナミックライブラリはCatエンジニアリングのblib/arch/auto/Animalディレクトリの下にあり、pmファイルはCatエンジニアリングのlibディレクトリの下にある.
同時にlibanimal.soファイルをLD_LIBRARY_PATH環境変数に追加する必要があります.
テストプログラムを実行します
perl test.pl
出力:
~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~
name=chen age=123
~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~
name=chen age=123
~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~
name=chen age=123
chen~~~~~~###############~~~~~~~~~~~~~~~
~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~
name=ASKJKLF age=889
~~~~~~###############~~~~~~~~~~~~~~~
~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~
name=ASKJKLF age=889
@@@@@@@@@@@@@@
~~~~~~~~~~~Animal.display()~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~Cat.display()~~~~~~~~~~~~~~~~~~~~
name=ASKJKLF age=889
テストに成功しました.