(AutoHotkey)(関数ライブラリ内で 共有したい変数を安全に扱う方法を考えてみる 編)

 今回の事案については まだ現在 試行錯誤中です、 もっと いい方法が見つかるといいな 笑)

 現在 AutoHotkey用の関数ライブラリみたいなものを作っているんですが
その中でふと思ったのが 関数ライブラリ内の 関数同士で 共有したい変数が欲しいというもの
もちろん 「Globalキーワードを 使えば 関数の外の変数にアクセスする事は可能です」
ただ 真っ先に考えられるのが 「変数の重複によって値が書き換えられる」問題
また「重複を避けるようにするには 変数名が長くなってしまう」事が考えられます

 ということで 「関数ライブラリ」なら 関数だけを定義するのが筋でしょうし
ならば 関数を使って 正式な手続きを取る事で その値を 読み書きできれば いいんですよね
その場合 どこに データを保存すべきなのかというと、 レジストリ? INIファイル? 関数内?
単純に考えると レジストリ や INIファイルだと 不揮発性のメモリになるわけで それはそれで 魅力がありますよね

 ただ 外部ファイルに対してアクセスするのも なんか 大げさだし、 不揮発性ならではのデメリットも考えられます
例えば a.ahk が ある値を書き換えたとして、 その時 同時に起動している 同じ値を参照している b.ahk があれば
そちらの方にも影響を与えます、 そういう問題を クリアしようとなると かなり 複雑なものになってしまいますし
使うための 手続きも増えるでしょうし 今回の目的で 外部ファイルに記録する方法は 現実的ではないでしょう
そもそも 変数名の重複回避に対して 見合う手間ではありません

 ということで どうやら 今回の事案は 「揮発性でそのスクリプトが実行中の時のみに有効な物」の方が よさそうですね
それなら 同ライブラリを #Include した a.ahk と b.ahk が 同時に起動していても 各スクリプトごとに 値は独立するはずです
関数を通してからじゃないと 値を変更できない、 つまり 意図的じゃなければ 書き換えることが出来ないわけで
一種の カプセル化的な 効果も期待できそうです

 とりあえず そういうものを作ってみる事にします
変数は 関数内 に Staticキーワードで 変数を宣言すればよさそうですね
有効期間は 「その関数が 最初にアクセスされた時 〜 スクリプトの終了」ということになります

 「一番の問題は 値取得 と 値設定 が 別の関数になってしまってはダメ」 ということです
そりゃそうですよね Static変数は その 関数内だけでしか 有効ではありませんから
「ならばどうするか?」と考えた場合 「引数の内容で動作を変える」のが 正解でしょうね
ただ そこで考えられるのは 各処理内容で 引数の 要素数が変動する問題ですよね
仮に _LibVal(Command, ValName, NewValue ) という 関数を作るとして
値取得の場合は _LibVal("Get", "<設定値名>") ですが
値を設定する時は _LibVal("Set", "<設定値名>", "<新しい値>") となって 引数の要素数が合いません
AutoHotkeyでは ユーザ定義関数に 省略可能 といったオプションは無いようなので
値取得の場合でも _LibVal("Get", "<設定値名>", 0) といった書き方を余儀なくされます
省略可能 ではなく 「必要ない所」まで 引数を書くというのも なんかシャクですよね

な・ら・ば! 引数はきっぱりと 一個にしてしまいましょう 笑)
つまり _LibVal(Expression) として Expression には コマンド文字列を入れて コマンド自体は 関数で解析するようにします
よって 想定する使用例は、 _LibVal("Get, <設定値名>") とか _LibVal("Set, <設定値名>, <新しい値>") になります
具体例としては
「ファイル名を取得する時のバッファのサイズ」 を 取得設定する場合は
取得時 は _LibVal("Get, FNBS") のようにすれば取得できるようにします、 FNBS は FileNameBufferSize の 略です
設定時 は _LibVal("Set, FNBS, 200") のようにすれば 設定できるようにします
他にも _LibVal("Init, FNBS") とか _LibVal("Init, ALL") とかで 再初期化 することもできるでしょうね

 こう考えると この関数は 変数値の読み書きだけにとどまらず いろんな 使い方を想定できますよね
例えば 「設定値」として フォルダ名 を 扱いたい時は
その値を 設定する時に そのフォルダが 実際にあるかどうかの 確認をしたり 最後に「¥」があれば省いたりして 初めて変更したりなど
つまり 「変数」というよりは 「プロパティ」の要素が強い関数が出来そうですよね

[];--------------------------------------------------------------------------------------------------------------------------------[]
[]; _StdLibVal(Expression)      私が作っているライブラリファイルは _Lib_StdLibrary.ahk なので そこから 関数名を命名[]
[];--------------------------------------------------------------------------------------------------------------------------------[]
[]; 同ライブラリ内の 他の関数から参照される 「設定値」を 扱う関数[]
[]; 処理内容は Expression に コマンド文 を 入れる事で指示する (文法については 下記を参照)[]
[];[]
[]; 設定値 自体 は _StdLibVal関数内に Static変数として存在しているが[]
[]; その変数にアクセスするには _StdLibVal関数を コマンドで操作する必要がある[]
[];[]
[]; 設定値 を 変更すると それより後に実行される その設定値を利用する 関数に 反映される[]
[]; 何度でも 変更 や 初期化 を行う事が可能[]
[]; 設定変更の有効範囲は 「変更後〜 そのスクリプト内のみ かつ そのスクリプトが終了するまで」[]
[];[]
[]; 設定値 は スクリプト起動時 に  毎回自動で「初期値で初期化される」ので 初期化する必要は無い[]
[]; 値を取得する場合 は Get、 値を再設定する時 は Set、 値を初期値に戻すの は Init コマンドを使う[]
[];[]
[]; 現在使用出来る「設定値名」は[]
[];     ファイル名を取得するためのバッファサイズ           → FNBS (初期値  500)由来:FileNameBufferSize[]
[];     拡張子を取得するためのバッファサイズ               → ExBS (初期値  100)由来:ExtensionBufferSize[]
[];     フルパスのファイル名を取得するためのバッファサイズ → FPBS (初期値 1000)由来:FullPathBufferSize[]
[];     全ての 設定値が対象になる                          → ALL   現在は Initコマンド時のみ使用可能[]
[];[]
[]; コマンド文 の 文法は[]
[];     "Get,  設定値名"                        設定値名に対応する値 を 取得する[]
[];     "Set,  設定値名, 新しい値"              設定値名に対応する値 を 新しい値 に 変更する[]
[];     "Init, 設定値名"                        設定値名に対応する値 を 初期値に戻す[]
[]; コマンド文は 原則として 一行のみ 途中改行は不可、 コンマ区切りで書いていく[]
[]; Get Set Init には 大文字小文字の区別はない、 それ以降は 「区別される」[]
[]; コンマの後に 空白(半角スペースとTab)が続いても 無視される、 コンマの後が空白無しでも 可[]
[]; _StdLibVal("get, FNBS") や _StdLibVal("gEt,FNBS") や _StdLibVal("GET,      FNBS") は 可[]
[]; ただし _StdLibVal("Get, FNbs") や _StdLibVal("Get, EXBS") のように 設定値名が違うと 不可[]
[];[]
[]; 戻り値[]
[];     「Getの場合」[]
[];          「成功時」[]
[];              「現在の設定値」[]
[];          「失敗時」[]
[];              「値無し(="")」[]
[];     「Setの場合」[]
[];          「成功時」[]
[];              「新しい設定値」[]
[];          「失敗時」[]
[];              「値無し(="")」[]
[];     「Initの場合」[]
[];          「成功時」[]
[];              「初期値」[]
[];          「ALL時」[]
[];              「"InitALL"」[]
[];          「失敗時」[]
[];              「値無し(="")」[]
[]_StdLibVal(Expression)[]
[]{[]
[]	Static FNBS   := 500[]
[]	Static _FNBS_ := 500[]
[]	Static FPBS   := 1000[]
[]	Static _FPBS_ := 1000[]
[]	Static ExBS   := 100[]
[]	Static _ExBS_ := 100[]

[]	Expression := RegExReplace(Expression, ",[ \t]*", ",")[]
[]	Loop, Parse, Expression, CSV[]
[]	{[]
[]		buf%A_Index% := A_LoopField[]
[]	}[]
[]	If ( RegExMatch(buf1, "^[gG][eE][tT]$") )[]
[]	{[]
[]		If ( buf2 == "FNBS")[]
[]			Return FNBS[]
[]		If ( buf2 == "ExBS")[]
[]			Return ExBS[]
[]		If ( buf2 == "FPBS")[]
[]			Return FPBS[]
[]	}[]
[]	If ( RegExMatch(buf1, "^[sS][eE][tT]$") )[]
[]	{[]
[]		If ( buf2 == "FNBS")[]
[]			Return FNBS := buf3[]
[]		If ( buf2 == "ExBS")[]
[]			Return ExBS := buf3[]
[]		If ( buf2 == "FPBS")[]
[]			Return FPBS := buf3[]
[]	}[]
[]	If ( RegExMatch(buf1, "^[iI][nN][iI][tT]$") )[]
[]	{[]
[]		If ( buf2 == "ALL")[]
[]		{[]
[]			FNBS := _FNBS_[]
[]			ExBS := _ExBS_[]
[]			FPBS := _FPBS_[]
[]			Return "InitALL"[]
[]		}[]
[]		If ( buf2 == "FNBS")[]
[]			Return FNBS := _FNBS_[]
[]		If ( buf2 == "ExBS")[]
[]			Return ExBS := _ExBS_[]
[]		If ( buf2 == "FPBS")[]
[]			Return FPBS := _FPBS_[]
[]	}[]
[]	Return ""[]
[]}[]
[];--------------------------------------------------------------------------------------------------------------------------------[]