ディレクトリ
4スクリプト言語
この章では、スクリプト言語の拡張プログラミングやスクリプト言語の通訳がCおよびC ++コードにアクセスするメカニズムの簡潔な概要を提供します。
この章では、アクセスするスクリプトプログラミング言語拡張のメカニズムの概要だけでなく、スクリプト言語インタプリタCおよびC ++コードを提供します。
二つの言語の概要4.1
スクリプト言語は、Cプログラムを制御するために使用される場合、結果として得られるシステムは、次のように見える傾向があります。
Cプログラムを制御するためのスクリプト言語を使用している場合、システムは次のように頻繁に生成されます。
C / C ++プログラムの基本機能は、特別なスクリプト言語を介してアクセスされるのに対し、このプログラミングモデルでは、スクリプト言語インタプリタは、高レベルの制御のために使用される「コマンド」。あなたがあなた自身の簡単なコマンドインタプリタを書くことを試みている場合は、その高度な実装するスクリプト言語のアプローチを表示することがあります。あなたは、このようなMATLABやIDLなどのパッケージを使用している場合は同様に、それは非常によく似たモデルである - インタプリタは、ユーザーコマンドやスクリプトを実行します。しかし、基本的な機能のほとんどは、CやFortranのような低レベルの言語で書かれています。
それは、各言語の強みを活用するためのコンピューティングの2言語モデルは、非常に強力です。C / C ++は、最大のパフォーマンスとタスクをプログラミングする複雑なシステムのために使用することができます。スクリプト言語は、ラピッドプロトタイピング、インタラクティブなデバッグ、スクリプト、および高レベルのデータ構造このような連想配列へのアクセスのために使用することができます。
このプログラミングモデル、高度な制御のためのスクリプト言語の通訳、および基本的な機能C / C ++プログラムでは、特別なスクリプト言語を介してアクセスされる「コマンド。」あなたがあなた自身の簡単なコマンドインタプリタを書くしようとした場合、それは、高度な実装方法にみなし言語をスクリプトすることがあります。あなたは、このようなMATLABやIDLなどのソフトウェアパッケージを使用した場合同様に、それは非常によく似たモデルである - インタプリタは、ユーザーコマンドやスクリプトを実行します。しかしながら、そのようなCやFortranのような根本的な機能のほとんどは、低レベルの言語を使用しています。
それはそれぞれの言語を利用していますので、バイリンガル計算モデルは非常に強力です。C / C ++は、パフォーマンスと複雑なシステムのプログラミング作業を最大化するために使用することができます。スクリプト言語は、ラピッドプロトタイピング、インタラクティブなデバッグ、スクリプト、ならびにアクセス(例えば連想配列など)、高度なデータ構造のために使用することができます。
4.2どのようにスクリプト言語のCを呼び出すには?
スクリプト言語は、コマンドやスクリプトを実行する方法を知っているパーサを中心に構築されています。このパーサの中では、コマンドを実行し、変数にアクセスするためのメカニズムがあります。通常、これは言語の組み込み機能を実装するために使用されます。しかし、インタプリタを拡張することで、新しいコマンドや変数を追加することが通常可能です。これを行うには、ほとんどの言語は、新しいコマンドを追加するための特別なAPIを定義します。さらに、これらの新しいコマンドはインタプリタにフックすることになっている方法を定義するインタフェースの特別な外国機能。
一般的に、あなたは二つのことを行う必要がありインタプリタスクリプトに新しいコマンドを追加するときに、まず、あなたは通訳とその下にあるC関数の間の接着剤としての役割を果たす特別な「ラッパー」関数を記述する必要があります。次に、等々の関数、引数、およびの名前についての詳細を提供することで、ラッパーについてのインタプリタの情報を提供する必要があります。次のいくつかのセクションでは、プロセスを説明します。
スクリプト言語パーサの周りにコマンドやビルドスクリプトを実行する方法を知っています。このパーサでは、コマンドとアクセス変数を実行するためのメカニズムがあります。通常、これは言語の組み込み関数を実装するために使用されます。しかし、インタプリタを拡張することにより、通常は新しいコマンドや変数を追加することができます。このため、ほとんどの言語は、新しいコマンドを追加するための特別なAPIを定義します。また、特殊な外部関数インタフェースは、これらの新しいコマンドはインタプリタにリンクする方法を定義します。
スクリプトインタプリタに新しいコマンドを追加する場合、通常、あなたは二つのことを行う必要があります。まず、あなたは通訳とその下にあるC関数の間の接着剤として機能し、特別な「ラッパー」関数を記述する必要があります。その後、あなたは通訳のラッパーに情報を提供することで、関数名やパラメータに関する詳細な情報を提供する必要があります。次のいくつかのセクションでは、プロセスを説明します。
4.2.1ラッパー関数
あなたはこのような通常のCの関数があるとします。
次のようにあなたの最初のC関数を仮定すると:
int fact(int n) {
if (n <= 1)
return 1;
else
return n*fact(n-1);
}
スクリプト言語からアクセスこの機能のためには、スクリプト言語とその下にあるC関数との間の接着剤として機能する特別な「ラッパー」関数を作成する必要があります。ラッパー関数は、3つのことを行う必要があります。
- 関数の引数を収集し、それらが有効であることを確認してください。
- C関数を呼び出します。
- 戻り値は、スクリプト言語によって認識形式に変換します。
例として、のためのTclラッパー関数fact()
の例上記の機能は、次のようになります。
スクリプト言語がこの機能にアクセスするためには、底部と、スクリプト言語のC関数との間の結合剤として機能する「ラッパー」特殊を用意する必要があります。機能をパッケージ化する3つのことを行う必要があります。
- コレクション関数のパラメータと彼らが有効であることを確認します。
- C関数を呼び出します。
- 戻り値はスクリプト言語認識の形式に変換されます。
例えば、上記の例で
fact()
Tclのラッパー関数は、次の関数であってもよいです。
int wrap_fact(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) {
int result;
int arg0;
if (argc != 2) {
interp->result = "wrong # args";
return TCL_ERROR;
}
arg0 = atoi(argv[1]);
result = fact(arg0);
sprintf(interp->result, "%d", result);
return TCL_OK;
}
あなたがラッパー関数を作成したら、最後のステップは、新しい機能についてのスクリプト言語を伝えることです。これは通常、モジュールがロードされている言語によって呼び出される初期化関数で行われます。例えば、Tclインタプリタに上記機能を追加すると、次のようなコードを必要とします。
あなたがラッパー関数を作成した後、最後のステップは、スクリプト言語の新機能についての情報を伝えることです。ロードモジュールの初期化機能は、言語によって呼び出されたときにこれは通常行われています。例えば、上記の機能はTclインタプリタに追加され、次のコードを必要とします。
int Wrap_Init(Tcl_Interp *interp) {
Tcl_CreateCommand(interp, "fact", wrap_fact, (ClientData) NULL,
(Tcl_CmdDeleteProc *) NULL);
return TCL_OK;
}
実行すると、Tclは今「と呼ばれる新しいコマンドがありますfact
あなたが他のTclコマンドのように使用できることを」。
Tclのに新しい機能を追加するプロセスを例示してきたが、手順はPerlやPythonのためにほとんど同じです。両方が書き込まれる特殊なラッパーとの両方が必要追加の初期化コードが必要です。唯一の具体的な詳細が異なります。
実行すると、Tclは名付けていた
fact
新しいコマンドを、あなたは他のTclコマンドは、それを使用していた使用することができます。Tclの、PerlやPythonが、プロセスに新しい機能を追加する唯一のプロセスはほぼ同じですが。特別なラッパーを記述する必要があり、追加の初期化コードを必要と両方。唯一の異なる詳細。
4.2.2変数リンク
可変リンクは、スクリプト言語インタプリタに変数にC / C ++グローバル変数のマッピングの問題を指します。たとえば、次の変数を持っていたとします
可変リンクは、変数インタプリタスクリプト言語を発行するC / C ++のグローバル変数をマッピングするために参照します。たとえば、次の変数があるとします。
double Foo = 3.5;
次のように(Perlのために示す)スクリプトからアクセスすることがいいことかもしれません。
スクリプトからアクセスするために、以下に示すように(Perlのように示す)かなり良いになります:
$a = $Foo * 2.3; # Evaluation
$Foo = $a + 2.0; # Assignment
このようなアクセスを提供するには、変数は一般のget / set関数のペアを使用して操作されています。変数の値が読み出されるたび例えば、「取得」関数が呼び出されます。変数の値が変更されるたびに同様に、「セット」関数が呼び出されます。
多くの言語では、get / setの関数への呼び出しは、評価と代入演算子に取り付けることができます。したがって、のような変数を評価する$Foo
かもしれないと、暗黙のうちにget関数を呼び出します。同様に、タイピングが$Foo = 4
値を変更するには、基礎となる集合関数を呼び出します。
操作量に一般に取得/セット関数のペアを使用して、そのようなアクセスを提供するために。変数の値が読み出されるたび例えば、それは「取得」関数を呼び出します。同様に、限り、変数の値の変化として、それは「セット」関数を呼び出します。
多くの言語では、/セット機能を取得するための呼び出しは、評価と代入演算子に追加することができます。したがって、このような評価の
$Foo
月のような変数は、暗黙のうちにget関数を呼び出します。同様に、入力する$Foo = 4
基本的なセットの値を変更するために呼び出す機能を。
4.2.3定数
多くの場合、Cプログラムやライブラリは、定数の大規模なコレクションを定義することができます。例えば:
多くの場合、Cプログラムやライブラリは、定数の数を定義することができます。例えば:
#define RED 0xff0000
#define BLUE 0x0000ff
#define GREEN 0x00ff00
定数を使用できるようにするには、それらの値は、次のようなスクリプト言語変数に格納することができ$RED
、$BLUE
と$GREEN
。事実上すべてのスクリプト言語は、定数をインストールするように、変数を作成するためのC関数を提供し、通常はささいな運動です。
作るために利用できる定数とその値は、例えば、スクリプト言語の変数に格納することができ
$RED
、$BLUE
そして$GREEN
。事実上すべてのスクリプト言語は、このように定数を置く、変数を作成するためのC関数を提供することは、通常は問題ではありません。
4.2.4クラスと構造
スクリプト言語には何の問題単純な関数や変数へのアクセスを持っていませんが、アクセスするC / C ++の構造体とクラスは別の問題を提示します。構造の実装は、主にデータ表現やレイアウトの問題に関連しているためです。さらに、特定の言語機能は、インタプリタにマップすることは困難です。たとえば、PerlインタフェースでC ++の継承の平均は何でしょうか?
構造物を処理するための最も簡単な手法は、構造物の基礎となる表現を隠しアクセサ関数のコレクションを実装することです。例えば、
そこに簡単な関数と変数にアクセスする一切のスクリプト言語の問題はありませんが、C / C ++の構造体とクラスへのアクセスは別の問題をもたらすであろう。これは主に、実装構造とデータ表現やレイアウトの問題です。また、特定の言語インタプリタにマップすることは困難であります。たとえば、C ++の継承に相当するものをPerlインタフェースへ?
処理構造を達成するための最も直接的な手法は、図の構造を隠すためにアクセサ関数のセットの下です。たとえば、
struct Vector {
Vector();
~Vector();
double x, y, z;
};
関数の次のセットに変換することができます。
これは、関数の以下のセットに変換することができます。
Vector *new_Vector();
void delete_Vector(Vector *v);
double Vector_x_get(Vector *v);
double Vector_y_get(Vector *v);
double Vector_z_get(Vector *v);
void Vector_x_set(Vector *v, double x);
void Vector_y_set(Vector *v, double y);
void Vector_z_set(Vector *v, double z);
次のように今、インタプリタからこれらの機能が使用されることがあります。
次のように今、これらの機能は、インタプリタから使用することができます。
% set v [new_Vector]
% Vector_x_set $v 3.5
% Vector_y_get $v
% delete_Vector $v
% ...
アクセサ関数は、オブジェクトの内部にアクセスするためのメカニズムを提供するので、インタプリタは、実際の表現について何を知っている必要はありませんVector
。
アクセサ関数はメカニズムを提供するので、インタプリタは、について知る必要がないように、オブジェクトの内部にアクセスするための
Vector
情報のいずれかの実際の表現。
4.2.5プロキシクラス
ある場合には、また、シャドウクラスとして知られているプロキシクラスを作成するために、低レベルのアクセッサ関数を使用することが可能です。プロキシクラスは、元の構造のように見えるが(あること、それは本当のC ++クラスをプロキシ)という方法でアクセスC / C ++クラス(または構造体)へのスクリプト言語で作成されるオブジェクトの特別な種類です。たとえば、次のC ++の定義を持っている場合:
いくつかのケースでは、あなたはまた、シャドウクラスとして知られているプロキシクラスを作成するために、低レベルのアクセス制御機能を使用することができます。プロキシクラスは、オブジェクトの特殊なタイプ、アクセスC / C ++クラス(または構造)に元の構造のように見えますが(すなわち、それはC ++のクラスの真の薬剤である)ような方法で、それを作成するためのスクリプト言語です。たとえば、次のC ++の定義を持っている場合:
class Vector {
public:
Vector();
~Vector();
double x, y, z;
};
プロキシクラス分けメカニズムを使用すると、インタプリタからより自然な方法で構造体にアクセスできるようになります。たとえば、Pythonでは、あなたはこれをしたいかもしれません。
クラシファイドメカニズムを使用すると、アクセス構造から、より自然な方法を説明することができます。たとえば、Pythonでは、あなたはこれをしたいかもしれません。
>>> v = Vector()
>>> v.x = 3
>>> v.y = 4
>>> v.z = -13
>>> ...
>>> del v
同様に、Perl5の中であなたはこのような仕事へのインタフェースをしたいことがあります。
同様に、Perl5の中で、あなたはこのような作品をインタフェースする場合があります:
$v = new Vector;
$v->{x} = 3;
$v->{y} = 4;
$v->{z} = -13;
最後に、Tclの中:
最後に、Tclの中:
Vector v
v configure -x 3 -y 4 -z -13
スクリプト言語の1、および基礎となるC / C ++オブジェクト - プロキシクラスが使用されている場合は、2つのオブジェクトが仕事で実際にあります。操作は均等に両方のオブジェクトに影響を与え、あなたは、単にC / C ++のオブジェクトを操作しているかのようにすべての実用的な目的のために、それが表示されます。
基本となるオブジェクト内のC / C ++でのスクリプト言語とその他で - プロキシクラスを使用する場合は、実際の機能で2つのオブジェクトがあります。同様に二つのオブジェクトの動作に影響を与え、そしてすべての実用的な目的のために、それが動作のみでC / C ++オブジェクトかのように見えます。
4.3ビルドスクリプトの拡張子
あなたのC / C ++アプリケーションとスクリプト言語を使用しての最後のステップは、スクリプト言語自体に拡張機能を追加しています。これを行うための2つの主要なアプローチがあります。好ましい技術は、共有ライブラリの形で動的にロード可能な拡張モジュールを構築することです。また、あなたはそれに追加あなたの拡張子を持つスクリプト言語インタプリタを再コンパイルすることができます。
C / C ++アプリケーションでのスクリプト言語を使用しての最後のステップは、スクリプト言語自体の拡張機能を追加することです。二つの主な方法があります。好ましい技術は、機能拡張を動的にロードすることができ構築するために、共有ライブラリの形式に基づいています。また、スクリプト言語インタプリタを再コンパイルし、拡張子を追加することができます。
4.3.1共有ライブラリと動的にロードされます
共有ライブラリやDLLを作成するには、多くの場合、あなたのコンパイラとリンカのマニュアルページを見てする必要があります。しかし、いくつかの一般的なプラットフォームのための手順を以下に示します。
共有ライブラリやDLLを作成するには、通常は手動コンパイラとリンカをチェックする必要があります。ただし、次のようにいくつかの一般的なシステムプロセスであります:
# Build a shared library for Solaris
gcc -fpic -c example.c example_wrap.c -I/usr/local/include
ld -G example.o example_wrap.o -o example.so
# Build a shared library for Linux
gcc -fpic -c example.c example_wrap.c -I/usr/local/include
gcc -shared example.o example_wrap.o -o example.so
共有ライブラリを使用するには、単にスクリプト言語で対応するコマンドを使用します(負荷、輸入、使用、等...)。これはあなたのモジュールをインポートし、あなたがそれを使用して起動することができます。例えば:
共有ライブラリを使用するには、適切なコマンドスクリプト言語(使用
load
、import
、use
など)。これはあなたのモジュールをインポートし、あなたがそれを使用して起動することができます。例えば:
% load ./example.so
% fact 4
24
%
C ++コードで作業する場合、共有ライブラリを構築するプロセスはより複雑にすることができる - これは主にC ++モジュールが正しく動作するために追加のコードを必要とするかもしれないという事実に。多くのマシン上では、上記の手順に従って、共有C ++モジュールをビルドすることができますが、次のようにリンク行を変更します:
C ++コードを使用する場合は、共有ライブラリを構築するプロセスは、より複雑である - C ++モジュールが正常に実行するために追加のコードを必要とするかもしれない主な理由。多くのマシン上では、上記の手順に従ってC ++モジュールを共有構築することができますが、リンク行は、次のように変更されました:
c++ -shared example.o example_wrap.o -o example.so
4.3.2リンクの共有ライブラリ
共有ライブラリとして拡張モジュールをビルドするとき、あなたのマシン上の他の共有ライブラリに依存しているためにあなたの拡張のために珍しいことではありません。仕事への拡張のためには、実行時にこれらのライブラリのすべてを見つけることができるようにする必要があります。そうしないと、次のようなエラーが出ることがあります。
状況が共有ライブラリの拡張建物で、拡張子は、コンピュータ上の他の共有ライブラリに依存することは珍しいことではありません。仕事への拡張のためのためには、実行時にすべてのこれらのライブラリを見つけることができる必要があります。そうしないと、次のエラーを受け取ることがあります。
>>> import graph
Traceback (innermost last):
File "<stdin>", line 1, in ?
File "/home/sci/data1/beazley/graph/graph.py", line 2, in ?
import graphc
ImportError: 1101:/home/sci/data1/beazley/bin/python: rld: Fatal Error: cannot
successfully map soname 'libgraph.so' under any of the filenames /usr/lib/libgraph.so:/
lib/libgraph.so:/lib/cmplrs/cc/libgraph.so:/usr/lib/cmplrs/cc/libgraph.so:
>>>
何このエラー手段は「SWIGで作成された拡張モジュールと呼ばれる共有ライブラリに依存することがありlibgraph.so
、システムが見つけることができなかったこと」。この問題を解決するには、あなたが取ることができるいくつかのアプローチがあります。
- あなたの拡張機能をリンクして、明示的に必要なライブラリが置かれているリンカを教えてください。多くの場合、これはのような特殊なリンカフラグを使用して行うことができ
-R
、-rpath
これは標準的な方法で実装されていない、などのように、共有ライブラリの検索パスを設定する方法についての詳細を見つけるためにあなたのリンカのmanページをお読みください。 - 実行ファイルと同じディレクトリに共有ライブラリを入れてください。この技術は、時には非Unixプラットフォーム上で正しく動作するために必要です。
- UNIX環境変数を設定し
LD_LIBRARY_PATH
、共有ライブラリはPythonを実行する前に置かれているディレクトリに。これは簡単な解決策ですが、それが推奨されていません。代わりに、リンカオプションを使用してパスを設定することを検討してください。
このエラーは、拡張モジュールを作成するために、SWIGを意味する名前の依存
libgraph.so
共有ライブラリがシステムに見つかりません。この問題を解決するには、いくつかの方法を取ることができます。
- 必要な場合は、明示的にリンカに伝えるライブラリの内線と場所をリンクします。典型的には、これは、例えば、完全に特別なリンカーフラグを使用することができ
-R
、-rpath
そして上のように。これは、標準的な方法で実装されていないので、共有ライブラリの検索パスを設定する方法の詳細については、リンカのマニュアルをお読みください。- 実行可能ファイルと同じディレクトリにある共有ライブラリ。非Unixプラットフォーム上で正しく動作するには、この技術を必要とするかもしれません。
- Pythonの、UNIX環境変数を実行する前に
LD_LIBRARY_PATH
、共有ライブラリのディレクトリに。が、これは簡単な解決策であるが、これは推奨されません。パスを設定するには、リンカオプションを使用することを検討してください。
4.3.3スタティックリンク
静的リンクを使用すると、拡張子を持つスクリプト言語インタプリタを再構築します。このプロセスは通常、言語にカスタマイズしたコマンドを追加し、インタプリタを起動する短いメインプログラムをコンパイルする必要。あなたは、新しいスクリプト言語の実行可能ファイルを生成するためのライブラリを使用してプログラムをリンクします。
静的リンクは、すべてのプラットフォームでサポートされていますが、これは、スクリプト言語の拡張を構築するための好ましい技術ではありません。実際には、これを行うための非常にいくつかの実用的な理由がある - 代わりに、共有ライブラリを使用することを検討してください。
静的リンクを使用して、あなたはスクリプト言語のインタプリタを再構築する拡張機能を使用することができます。通常は短いメインプログラムをコンパイル含むプロセスは、プログラム言語にカスタムコマンドを追加し、インタプリタを起動します。その後、プログラムは、新しいスクリプト言語の実行可能ファイルを生成するためのライブラリとリンクします。
すべてのプラットフォームは、静的にリンクされたサポートしていますが、これは好みのビルドスクリプト言語拡張技術ではありませんが。実際には、この少しの実際の理由は - 共有ライブラリを使用することを検討してください。