■【ライブラリのパッケージ化】 と、 サブルーチン や 変数の 【名前空間】 について。


	  ◇このHPでは、 一般的となったPerl5を 前提としています。


1.予備知識


  自分のCGI 中の 変数名/サブルーチン名 と“同名”の記述が ライブラリの中に存在したら どうするか?。



ライブラリの代表格として、【jcode.pl】 と 【cgi-lib.pl】を比較してみて下さい。


jcode.pl の方は 先頭に package jcode; と記述されています。(パッケージ宣言)
私の手元にある cgi-lib.plでは そのような記述はありません。 cgi-lib.pl はパッケージではないのです。

あなたの複数のCGIの内、いくつかには 【require 'cgi-lib.pl';】といった一文が含まれていると思います。
実行中のプログラムの一部として ライブラリ(cgi-lib.pl)を追加で読み込む動作です。

“パッケージ宣言されていないライブラリ”を requireした場合、
requireした側と requireされた側で、 サブルーチン名や グローバル変数の名前が 重複しないようにする必要があります。

	※cgi-lib.pl の場合は 標準ライブラリとしての地位を確立していますので、
	  プログラムを作る人達は cgi-lib.plとの名前の重複が起きないように意識しているのでしょう。

普通は、requireした側には、package という記述はありませんが、
そのような場合は、デフォルトで【mainパッケージ】と認識されます。


analysis.txt(analysis.cgi)の終りの方に 【サブルーチン make_DefaultFile】がありますが、
ここに、
local ($makefile) = $_[0];
という記述があります。
これは、 サブルーチンmake_DefaultFile が呼ばれた時の“第1引数”の値を
$makefileというlocal変数(を定義して)に 代入する・・・という意味です。
サブルーチンの中で local宣言された変数は、その変数名が、local宣言されていない変数と同一の名前であっても“別物”として扱われます。
例えば、上の  local ($makefile) = $_[0];  を例に挙げれば、
他のサブルーチンの内外で  local宣言されていない$makefileという同名変数が存在しても、local変数$makefileとは お互いに影響しません。

  ※analysis.cgiの例で言えば、$lockkeyという変数は local宣言されていません。これをグローバル変数と言います。
   (大域変数とも言う)
  ※通常は グローバル変数の名前 と local変数の名前を同じ名前にはしません。
   (ソースコードを検討・修整する時に人間が間違えやすい)

【サブルーチンの中で local宣言された変数(local変数)】の“値”が有効な範囲は、
そのサブルーチン内部と、“そのサブルーチンから呼ばれたサブルーチン”の内部です。
これに対して、
グローバル変数の場合は、どこからでも その値を参照できますし、どこからでも その値を変更できます。

	※グローバル変数/local変数(ローカル変数)についての規則は、PASCALでも FORTRANでも 大体おなじです。
	  (微妙な相違点もある。C言語は私は知らないが大体おなじらしい)
	  これらの言語の記述では 先頭の方に【宣言部】を作成し、そこに変数名と変数の型を定義します。


Perl5では、ローカルな変数の別種として 【my変数】という型が新たに導入されました。

my変数(my宣言された変数)の有効範囲は、
宣言された地点から その変数を囲む1つのブロック(同じサブルーチン等)の内部だけで 有効です。
つまり、my変数の有効範囲は独立しています。
“my変数を宣言したサブルーチン”の外では そのmy変数は無効です。
“my変数を宣言したサブルーチン”が 子供のサブルーチンを呼んだ場合も、子供のサブルーチン内で そのmy変数は無効です。
これを【情報の隠蔽】と言います。

	※local変数は、 厳密に言えば グローバル変数の仲間ですが、
	  話が ややこしくなるので、ここでは “local変数=ローカル変数の仲間”という事にします。
	  analysis.cgiの例では my変数を使う方が相応しい部分がありますが、
	  「子サブルーチンで参照するかも・・」という意識もあり、PASCAL/FORTRANを使っていた若い頃のクセです。
	  ソースコードも 【PASCAL/FORTRAN/Perl の合の子】といった雰囲気ですが、勘弁して下さい。

以上に述べた【local変数・my変数・グローバル変数】の規則は、
単純なスカラー変数のみならず、配列変数やハッシュにも適用されます。


このページでは Perlについて説明していますが、
Perlでは、“宣言なしに黙って使った変数”は グローバル変数として扱われます。(← Perlの特徴)
Perlでは、どこで変数を宣言しても良く、便利なようですが、グローバル変数の場合は 危険性もはらんでいます。
 (どこで どういう変数を使っているかが、プログラムソースを見てもパッとはわからない)
従って、
ネットで入手したライブラリ(サブルーチン集)を使う時、
その ライブラリが パッケージ宣言(package宣言)されていない場合は、「便利そうだ」〜と安直に使うのは
ちょっと危ない面があります。

じゃ、「サブルーチンの中では 全て local変数を使えば良いのでは?」〜 と感じる人もいるでしょう。
ところが、グローバル変数には それなりの利便性もあるのです。
例えば、analysis.cgiの例で言えば、
$lockflag や $lockCount を local変数にしますと、ソースコード記述が面倒になり、読みにくくなります。
$lockflag や $lockCount を local変数にしますと、
サブルーチン側では これを 必ず【戻り値】として返す必要があり、それを受け取る“呼び出し側”の処理も面倒になります。

また、
ライブラリを requireした時、 読み込んだライブラリの中に 本体と同じ名前のサブルーチンが 存在するかも・・・
という点に注意する必要もあります。


では、どうすれば良いか・・・。 ここに、【ライブラリをパッケージ化】する事の利点があります。


【ライブラリをパッケージ化】する作業は 簡単です。 文法的には、ライブラリの先頭に パッケージ宣言するだけです。
ライブラリの先頭に、
package abcde;
という1行を追加するだけです。(package宣言。 abcde がパッケージ名)
	※他に、
	  パッケージファイルであろうとなかろうと、ライブラリファイルの最後は 必ず 1; で終らせます。
	  あなたの手持ちの plファイルは そうなっているハズです。
これだけで パッケージ化されます。
ただし、正常動作するかどうかは、ライブラリ内部でスクリプト文法ミスがあれば 話は別です。
また、そのライブラリが 元々 行儀の悪い記述方法をしている場合も 話は別です。(後述)

注意点として、
【“パッケージ宣言したライブラリ”内のサブルーチン】を呼ぶ時は、
【“パッケージ宣言していないライブラリ”内のサブルーチン】を呼ぶ場合と比べて、サブルーチンの呼び方が変わります。

Perl4の場合 ⇒ &パッケージ名'サブルーチン名;
Perl5の場合 ⇒ &パッケージ名::サブルーチン名;

	※Perl4・Perl5 共に、サブルーチン名を記述する時に 引数が必要ならば サブルーチン名(引数); と記述する。
	  つまり、 Perl5の場合なら、 &パッケージ名::サブルーチン名(引数); と記述する。
	※Perl5環境では、Perl4の書式も許されます。

abcde.pl というライブラリファイルを パッケージ化する時、
abcde.plファイルの先頭で 【package fghij;】という風に 別名でパッケージ宣言しても構いませんが、間違いの元です。
そういう ヒネた宣言の仕方をすると、
require する時は require './abcde.pl'; と記述し、その内部のサブルーチンを呼ぶ時は &fghij::サブルーチン名; と記述する事になり、
頭が混乱します。


さて、 具体的な作業を行う前に、 ↓ 【パッケージの独立性 / 名前空間】 を読んで下さい。






2.【ライブラリの パッケージ化】の基本と、 【パッケージの独立性 / 名前空間】


パッケージの独立性を簡単に言えば こうなります。↓
		requireしたライブラリがパッケージであれば、
		requireした側と requireされた側で、 サブルーチン名や グローバル変数の名前が 重複しても構いません。
		(ファイルハンドル名も。)

パッケージ機能を使用すると、
変数名・サブルーチン名・ファイルハンドル名 などを パッケージ単位に 独立的に Perlが管理してくれます。
これら↑の名前と その指し示すポインタが、各パッケージ用のシンボルテーブルに格納されます。
シンボルテーブルは“ハッシュ”の形態で記憶・管理されています。

	※my変数は シンボルテーブルに格納されない。

【1.】で説明した グローバル変数(大域変数)の有効範囲は、 1つのパッケージ内で “全体的に有効”です。
別のパッケージ内に 同名のグローバル変数が使用されていても、お互いに影響しません。

	※あるパッケージ内から 別のパッケージのグローバル変数の値を参照する方法はありますが、
	  テーマから外れるので触れません。


実行するパッケージを切り換えると、シンボルテーブルも切り換わります。これを【名前空間の切り換え】と言います。

	※名前空間がパッケージごとに個別に管理される・・とも言います。
	  それぞれのパッケージが個別に名前空間を保有する・・とも言います。

Aパッケージと Bパッケージとで 同じ名前の変数・サブルーチンが使われていても、お互いに影響しません。

つまり、
パッケージ機能を使用する事によって、
変数で local変数・my変数を使った場合と同様の効果が、 メモリ空間全体に実現します。

例外を除いて、 1つのパッケージ空間は、package宣言された箇所から 次のpackage宣言まで です。

	※analysis.cgiでは package宣言していませんが、宣言しても構わないのです。
	  宣言していないので、デフォルトで 【mainパッケージ】となります。

実行型のパッケージを requireすれば、パッケージ空間が切り換わって そのまま実行されます。
 (【require 〜〜.cgi】というのを見た事があるでしょ。)
サブルーチン型のパッケージ(ライブラリ)を requireすれば、そのパッケージ空間のサブルーチンが実行可能な状態となります。



例題として、 analysis.cgiの どこかの部分で 別のライブラリ(サブルーチン集)を requireしたと想定します。


analysis.cgi の先頭は #! /usr/local/bin/perl から始まっています。
	※この行だけは、先頭が # でも有効な行であり、
	  この行は、【CGIを実行してくれる Perl】のインストールディレクトリをサーバーに認識させる行です。

analysis.cgiでは package宣言していませんから、デフォルトで 【mainパッケージ】であり、
以下の説明となります。

===========================
【analysis.cgi】

#! /usr/local/bin/perl
# 	デフォルトで 【mainパッケージ】
〜〜〜〜
sub lock {
    〜〜〜〜
}
〜〜〜〜
〜〜〜〜
===========================

どこかで abcde.pl という ライブラリパッケージを requireすると、 analysis.cgi は、次の形に変身します。
(概念上の説明)
		※abcde.plファイルの先頭行で、【package abcde;】と宣言していると想定。
===========================
【analysis.cgi】

#! /usr/local/bin/perl
# 	デフォルトで 【mainパッケージ】
〜〜〜〜
実行文
〜〜〜〜
sub lock {
    〜〜〜〜
}
〜〜〜〜
〜〜〜〜
package abcde; 		# ← package宣言。 abcde がパッケージ名。
〜〜〜〜
sub lock {
    〜〜〜〜
}
〜〜〜〜
1;
===========================

変数名も サブルーチンも、 パッケージごとに独立的に管理されていますから、
上記のように、 2つのパッケージに 同名のサブルーチンが 存在していても問題ありません。

しかし、もし、abcde.pl というライブラリが package宣言していなければ、
1つのmainパッケージ(analysis.cgi)の中に 同名のサブルーチンが2つ存在する事になります。
実行途中で、多分 サーバーエラーとなるでしょう。

	※package宣言していないライブラリの場合は、
	  そのライブラリをrequireすると、requireした側のパッケージの空間内に同居します。
	  そして、その場合、そのライブラリのサブルーチンを呼べば そのパッケージ空間で実行されます。
	  analysis.cgiは package宣言していませんから、
	  上記の例で言えば、package宣言していないライブラリをrequireすると、mainパッケージ内に同居します。


要するに、requireするという事は 合体する事です。(インクルードする〜と言います。)
package宣言されたライブラリをrequireするという事は、
即ち、
最初から 1つのプログラムの中に【packageコマンド】を利用して 1つのプログラムの中に“2つのメモリ空間”を独立的に存在させる
のと ほぼ同じです。

【mainパッケージ(デフォルト)】から abcdeパッケージのlockサブルーチンを呼ぶ時は、
mainパッケージの中に 次のように記述します。

&abcde::lock;

	※abcdeパッケージのサブルーチンを呼ぶ前に  require './abcde.pl'; を実行してある事が条件。(Perl4・Perl5共通)
	※Perl4では、別パッケージ内のサブルーチンを呼ぶ時の書式が異なる。


mainパッケージ(analysis.cgi)の中で、単に &lock; と呼べば、 mainパッケージ(analysis.cgi)自身の lockサブルーチンが 呼ばれます。

	※require文は、同一のライブラリに対して 2回以上 require実行しても、問題ありません。
	  “読み込み済み”であるかどうか・・を自動的にチェックしてくれます。



サブルーチンの呼び方は、その時の条件で 色々あります。

	※説明を単純にするために、“パッケージ宣言されていないライブラリ内のサブルーチン”を呼ぶ場合を例に挙げます。
	  ‘パッケージ宣言されたライブラリ内のサブルーチン”を呼ぶ場合は、サブルーチン名の前に &パッケージ名 を付記します。
	  下の例で言えば、 &abcde::lock; とか、 &abcde::lock($hikisuu1, $hikisuu2); という記述になります。
	  その場合、【サブルーチン名の直前の & 】の文字が パッケージ名の直前に移動することに注意して下さい。

sub lock {
    〜〜 何かの処理 〜〜
}
という サブルーチン名を呼ぶ・・と仮定して、 次の呼び方が許されます。

	($hikisuu1 $hikisuu2 は引数。 $modoriti は戻り値。)

do lock;
&lock;
lock();
lock($hikisuu1, $hikisuu2);
$modoriti = &lock($hikisuu1, $hikisuu2);
$modoriti = &lock();
$modoriti = &lock;
lock;
	等 ですが、
lock; という呼び方( & も ()も付けない )が許されるのは、
サブルーチンを呼ぶ前に  sub lock; という前方宣言が行われていた場合に限ります。





3.【行儀の悪い記述方法】 をしてある ライブラリ


さて、 ライブラリを パッケージ化してある効能は大きいのですが、
サブルーチンの処理の内容によっては、 パッケージ化してない方が便利なケースもあるかも知れません。
その辺りの事情で cgi-lib.pl はパッケージ化してないのかも知れません。
私は Perlの経験が浅いので 明確な事は言えません。


ライブラリを パッケージ化して 弊害が生じるケースは非常に少ないでしょう。
だって、
基本的に、パッケージ化されて困るのであれば、
サブルーチンの“戻り値”以外に 【サブルーチン内部で変更したグローバル変数の値】を 呼び出し側で参照させる・・・
という 利用の仕方を、
呼び出し側に 強制するライブラリである。・・・という事になります。
そのようなライブラリをパッケージ化する場合は、 “ライブラリ側の戻り値”を追加する必要があります。

ライブラリをパッケージ化されて困る状況として もうひとつ想定される事は、
そのライブラリが、“自分自身を呼ぶmainパッケージのグローバル変数”を 無条件に参照している場合です。
実行型の plファイルなら それもあり得るでしょうが、
ライブラリは サブルーチン集なのですから、“自分自身を呼ぶmainパッケージのグローバル変数”を 無条件に参照する事は無いのが普通です。
ライブラリ内のサブルーチンを実行する時に設定値が必要なら 【引数】として引き渡す形で サブルーチンを呼ぶようになっているハズです。


	※ロック処理の場合は、パッケージ化してない方が便利だし、
	  他の処理の変数と競合する事がほとんど無く、
	  パッケージ化すると 利用の仕方が不便になる。
	  多くのロック処理サブルーチンは、別パッケージではなく mainと同一パッケージ空間で実行するようになっている。


書き込みを行うサブルーチンの場合であれば、
書き込みデータを指定して、書き込みファイルを指定するだけの話ですから、パッケージ化した方が【有利・安全】です。

また、
【perl-lib.pl に含まれる“書き込みサブルーチン”】の場合は、 パッケージ化するにあたって 何の不都合もありません。


という訳で、 次は いよいよ、 【ライブラリの パッケージ化】の応用 と、 sclrDataSave.pl に関して 説明します。





4.【ライブラリの パッケージ化】 の応用 と、 sclrDataSave.pl



about_sclrDataSave.txt



私が提唱する 多重ロックは、 一般に行われている 【ファイル処理全体のロック】 の仲間です。

【perl-lib.pl に含まれる書き込みサブルーチン】 は、 ファイル単位でのロックに便利です。
これは非常に強力で、 ファイル単位での書き込みロックでは信頼性が高いと言えます。
ただし、 “複数のファイルを 必ず同期させるロック処理” という目的では 少し不便さが残ります。
 (欠点ではなく、 元々のロック処理の分類が異なるので仕方がありません。)
従って、
【ファイル処理全体のロック】 と  【ファイル単位でのロック】を併用すれば 最高でしょう。

sclrDataSave.pl は、
【perl-lib.pl の書き込みサブルーチン】 を、 上書き/追記 の両用モードにし、状況に応じて 一般的なロックも併用するための 引数 を追加しただけです。

著作権は、あくまでも 【Try The HomePage http://www.tryhp.net】 にあります。