Emueraについての補足¶
元となったページ
eratohoまとめ V3 Emueraについての補足
開発者のためのEmuera講座¶
Emueraは動作環境であると同時に、開発者にとって非常に有益な機能を持った構文チェッカでもある。
しかし、最近の状況を見るにつけ、どうもこの便利な機能がうまく使われていないという感じなので、
簡単に開発者向けのEmueraの使い方をまとめてみる
1.デフォルト設定で使うべからず¶
Emueraのデフォルト設定はプレイオンリーの人向けのものであり、
構文チェック等の機能等が生かされないのだが、この設定でエラーが出ない=問題ないと勘違いしてしまいやすい。
実際はデフォルト設定では構文チェックの重要部分の一部がスキップされるので、見落とされる基本的な構文エラーは少なくはない。
(Lv2の警告すら余裕で見落としになる)
そこで、開発者向けとして、以下の設定を推奨する。
- 解析タブ
ロード時にレポートを表示する
にチェック
ロード時に引数をチェックする
:常に行う
or更新されていれば行う
(なるべく前者を推奨だが、好み)
表示する最低警告レベル
:0
(エラーとかではないが、基本的な構文ミス等もないにこしたことはない)
関数の呼び出し系は最近のCALLFORM系が多用されるコードでは重要ではないので、好みでOK
- 環境タブ
関連づけるテキストエディタ
:自分が普段使ってるテキストエディタのパス
コマンドライン引数
:そのエディタで行指定してファイルを開く時のコマンドライン引数(使ってるソフトのヘルプ等を参照のこと)
これでエラーが出たときにエラーが出た行の所をクリックすれば、自動的にエディタ上でその場所が開くようになる
2.解析モードというオプション¶
Emueraには解析モードという特殊な起動モードがある。
これはERBファイルorフォルダをEmuera.exe
にドラックアンドドロップすることで立ち上がるものだが、
上で書いたような開発者向け設定で自動的にチェックを行い結果をAnalysis.log
に出力してくれるモードである。
バリアント上で行えばcsvのデータも勘案した上で解析するので、基本的な構文チェックには十分な機能である。
普段使いでは起動の速いオプションにしたいが、開発の時にはしっかりと構文チェックをしたい人にはおすすめのモードである。
なお、解析モードでは、普段の起動にはない、ERBファイル毎の関数名一覧出力も行ってくれる
3.最近の構文解釈周りの強化¶
1807v5以降ではEmueraの構文チェック機能と開発者向け機能が強化されている。
- A.関数呼び出しスタック
実行中エラーでは、今いる行のある関数から順に呼び出し元の行、さらに呼び出し元の行のある関数の呼び出し元…
と、どのように関数が呼び出されたかを表示するようになった。
汎用の式中関数中でのエラーなど、どこから呼び出された時に問題が起こったかとかの洗い出しに便利である。
- B.起動時の変数チェックでの配列要素外参照のチェック追加
起動時に変数の引数の内定数のものについては、その時点で要素範囲内かのチェックを行う処理を追加した。
これで、定数の場合にはミスで範囲外を指しているのを起動時に報告してくれるようになった。
- C.エラーを投げる
THROW
THROW
命令はその行に到達した時点で、強制的にエラーを投げる命令である。
デバッグでの特定条件の確認等使い道は様々である。
以上のようにEmueraをうまく使うことで、簡単な構文エラーが残ったままのコードをうpしてしまうといったことはほぼなくせるので、
バリアント作者、口上作者、パッチ作者問わず、しっかりと使いこなしてほしい。
4._fixed.config
の使い方¶
まず最初に、バリアント側で何かしら動作環境の設定を行いたい場合、emuera.config
を使ってはいけない。
何故かといえば、emuera.config
はユーザー側が自身の環境を定義するためのものであり、
ユーザーによって上書きされることが前提だからである。
(ユーザー側は他のバリアントからコピペする等の運用が想定される)
では、バリアント側で設定を固定したいときにどうするか?というと、CSVフォルダにある_fixed.config
の出番となる。
_fixed.config
で指定された設定はユーザー側で上書きすることができず、常に_fixed.config
の指定が優先される。
これは、描画方法やセーブデータ形式など、設計レベルと言える設定に対して非常に効果的である。
似たファイルに_default.config
があるが、こちらはユーザー設定がない場合のデフォルト値を指定する仕様となっており、
現実的にはあまり出番がないものとなっている。
Q&A¶
Q:なぜEmueraは起動に時間がかかるのか¶
A:Emueraは起動時に全てのコードを読みIF~ENDIF
やCALL
等の対応のチェックをしています。そのため起動に時間を要します。
しかし、このおかげで実際のスクリプトの処理はeramakerより速くなっています。
(描画処理が律速なので実感することはできませんが)
さらに起動時にエラーチェックできるのもこの実装のおかげです
ここらへんは完全に起動速度と起動後の処理速度等がトレードオフになってます。
1.735以降の文字列読み込みパース処理の高速化で起動時間は大幅に改善されました
Q:Emueraの描画遅すぎ¶
A:実のところC#はあまりグラフィックの描画に優れた言語ではありません。そのためこれは半ば仕様と言えます。
現在のGDI・GDI+ではなくOpenGLやDirectXを使えば速くなる「かも」しれませんが、そこまで手を出す余力は作者にはありません。
挑む勇気のある方がいればやってみるといいかもしれません。
以下、Workaround:
多くの場合はデフォルトの設定でそれなりの速度になるはずですが、RadeonHD系はPowerPlayの仕様によってGDIの描写が遅いので、GDI+を使うGraphics+イメージバッファ使用の設定にした方が速くてちらつきの少ない描写になるようです。
(HD4XX0系ならCCCでクロック固定という方法もあるが、素人にはおすすめできない)
また、古いマシンではFPS設定を落とすのも効果的です。
Q:_Repalace.csv
でできる内容と書式は?¶
A:以下の通りです
- お金の単位
- 内容:所持金の表示でのお金の単位を指定
- 書式:お金の単位, <単位に使う文字>
- 単位の位置
- 内容:所持金の単位を数値の前に付けるか後ろに付けるか
- 書式:単位の位置, <前or後>
- ファイル読み込み中の表示
- 内容:起動読み込み時に詳細表示しない場合に表示される文字列を指定
- 書式:起動時簡略表示, <表示する文字列>
- SHOPで販売アイテムとして認識するアイテム数
- 内容:SHOPで販売アイテムとして取り扱うITEMの数値の上限を指定
- 書式:販売アイテム数, <上限ITEM番号>
- DRAWLINEに使う文字
- 内容:DRAWLINEの線に使う文字(列)を指定
- 書式:DRAWLINE文字, <使う文字列>
- BARで使う文字の指定
- 内容:BARに使う文字の指定(文字列を用いた場合、正常動作を保証しません)
- 書式:BAR文字1, <値の範囲内の表示に使う文字>
BAR文字2, <値の範囲外の表示に使う文字>
- システムメニュー文字列
- 内容;読み込み終了後のシステムメニューの文字列を指定
- 書式:システムメニュー0, <最初からはじめるを置き換える文字列>
システムメニュー1, <ロードしてはじめるを置き換える文字列>
- COM_ABLE初期値
- 内容:@COM_ABLEXXがない場合にその返り値をどうするかを指定
- 書式:COM_ABLE初期値, <0 or 1>
- 汚れの初期値
- 内容:STAIN変数の初期値を指定
- 書式:汚れの初期値, <各配列要素の初期値(/で区切る)>
- TINPUT系の時間切れ時の表示内容
- 内容:TINPUT系で時間切れになったときに表示する文字列
- 書式:時間切れ表示, <表示する文字列>
- PALAMLVの初期値
- 内容:PALAMLV変数の初期値を指定
- 書式:PALAMLVの初期値, <初期値"/"区切りで>
- EXPLVの初期値
- 内容:EXPLV変数の初期値を指定
- 書式:EXPLVの初期値, <初期値"/"区切りで>
- PBAND:0変数の初期値を指定
- 内容:PBAND:0変数の初期値を指定
- 書式:PBANDの初期値, <値>
Emueraの仕様の補足¶
REPEAT~REND
(FOR~NEXT
)の変てこな仕様¶
eramakerでは以下のコードではループを回さない仕様になっている
GOTO $TEST
REPEAT 10
;~以下処理~
$TEST
REND
では、以下はどうなるか?
REPEAT 10
;~以下処理~
$TEST
REND
COUNT = 0
GOTO $TEST
答えは、「REPEAT
を通ってないにも関わらずループが回る」である。
eramakerの仕様として、
「一度でもループに入れば、それ以降はGOTO
でREPEAT
を通さずに中に飛び込ませてもループを回す」
という実に面妖なものが存在しているのである。
Emueraでもこれは当然再現されている。間違っても使うべき処理ではないことだけは間違いないが。
CONTINUE
の処理に関して¶
ループ処理に欠かせないCONTINUE
とBREAK
の2命令
しかし、ループから強制的に抜けるBREAK
の単純明快さの一方、
CONTINUEの処理は意外と勘違いされている。
この勘違いはDO~LOOP
構文で出現する。
DO
;(処理)
CONTINUE
LOOP 0
このコード、CONTINUEがあるので一見無限ループにも見えるが、
実はDO~LOOP
の中は1回しか実行されない。
これはCONTINUE
が本質的にはループの最後に飛ぶという仕様だからである。
つまり、このループの処理は
CONTINUE
→LOOP 0
の判定→判定結果は偽なのでループを抜ける
ということになっている。
文字列とRETURNF
に関する面妖な仕様¶
ユーザー定義の式中関数では識別子#FUNCTIONS
を宣言することで
RETURNF
で文字列を返す関数にすることができる。
ところが、このRETURNF
文字列に関してはやや面妖な仕組みになっている。
RETURNF
で文字列を返す場合の書式は、
PRINT
系での表示や文字列代入等とは異なるルールになっている。
具体的には以下のとおりになる。
文字列の種類 | 書式 | 例 |
---|---|---|
単純文字列 | "文字列" | RETURNF "テスト" |
文字列変数 | 変数名 | RETURNF STR:0 |
FORM文字列 | @"FORM文字列構文" | RETURNF @"%STR:1%{A:2}\@(LOCAL) ? あ # い\@" |
最後のFORM文字列については@"~"を使わないとエラーになったりするので、
特に注意が必要である。
3次元配列とその制限¶
Emueraでは3次元配列を用意している。
TA:XX:XX:XX および TB:XX:XX:XX
100×100×100
で計100万個である。~~なお、制限として配列サイズを変えることができない~~
1732aより100万個を上限に配列サイズを変更可能に
インクリメント、デクリメントの拡張¶
前置・後置、文中での使用等色々対応してあります。
ほぼ普通のCプログラムと同様の感覚で使えるはずです。
文字列演算¶
最近のEmueraでは文字列演算で*=
を使えるようになっていたりする。
使い道があるかどうかは実装した本人にも謎。
PRINTCPERLINE()
の意味¶
configにPRINTCを並べる数
という項目があるが、実はこれは調教コマンドの表示ぐらいでしか使われていない。
つまり、スクリプトからPRINTC
系を使った場合は設定に応じた自動的な改行は行われない。
それでもUSERCOM
のコマンドなんかを調教コマンドと同じように表示したい!という場合にこのコマンドの出番となる。
ようは、このコマンドで設定を読み取り、それにあわせて改行を行うようなコードを書けばよいのである。
DO~LOOP
命令¶
書式:
DO
;(命令)
LOOP (条件)
補足:
WHILE~WEND
ループは最初にWHILE
の条件を満たさないと中の処理は一切行われない。
それに対し、DO~LOOP
では1回ループ内の処理を行った後LOOP
の行でループするか否かを判定する。
最低でも1回は処理が行われていることが確定しているなら、DO~LOOP
を使った方がスマートなコードになる。
REUSELASTLINE
の仕様の変化¶
REUSELASTLINE
は最初に実装された[私家改造]1.52q rev.2
と本家に取り込まれた1.60
以降で仕様が異なっている。
具体的には元々は、
- 前の行を消して、次の行を追加する時に消去される行を表示する
だったものが
- 次の行を追加する時に消去される行を表示する
に変わっている。
そのため、INPUT
に対し、無効な入力の時に入力した値を消そうとした場合、
元の仕様であれば、
REUSELASTLINE (警告文)
のみでよかったが、現在は
CLEARLINE 1
REUSELASTLINE (警告文)
とする必要がある。
なお、@USERXXX
系の処理では今までどおり
REUSELASTLINE_
(_は半角スペース)
のみで動作するようになっている
(この場合の処理は内部的に行われるのでCLEARLINEが必要ない)
なお、この仕様の違いについては、また変えるのも混乱を招くということで元に戻さないことが決まっている(作者同士の直接対話によって決定)
Bit演算系命令¶
使えると便利だが、とにかくややこしいことに定評のあるBit演算。
Emueraでは1pN
のような2進法表記が実装されたり、多くのBit演算向け演算子が追加されたりと格段にBit演算をやりやすい実装にはなっている。
とはいえ、プログラミング経験者ならともかく、そうでなければこの実装でもつらいというのが実際の所。
そこで、以上の方法とは別に、GETBIT
、SETBIT
、CLEARBIT
、INVERTBIT
を用意している。
参考:リファレンス/BIT操作系
書式:
GETBIT (操作する変数)、(ビット位置)
;対象変数の2^(ビット位置)のビットを取得
;(GETBITは文中関数として使える)
SETBIT (操作する変数)、(ビット位置)
;対象変数の2^(ビット位置)のビットを1にする
CLEARBIT (操作する変数)、(ビット位置)
;対象変数の2^(ビット位置)のビットを0にする
INVERTBIT (操作する変数)、(ビット位置)
;対象変数の2^(ビット位置)のビットを反転(0→1、1→0)にする
この方法の利点は、
- いちいち対象になるビットの値を計算する必要がない。
- ビット位置の指定は1pNと違い変数が使えるので処理の一元化を狙える。
欠点は
- わかってる人からすればまだるっこしい。
- 演算子等を使った方が格好良く見える
である。
Shift-JISとUnicodeでの文字列処理の違い¶
Emueraでは基本的に文字列長取得の命令が3つ用意されている。
STRLEN (文字列)
STRLENS (文字列式)
STRLENFORM (FORM構文)
これらはいずれも、Shift-JISとしての文字列長を取得している。
これに対して、
STRLENU (文字列)
STRLENSU (文字列式)
STRLENFORMU (FORM構文)
はユニコードとして文字列長を取得する。
最大の違いはShift-JISは漢字1文字を2文字とカウントするのに対して、ユニコードでは漢字も1文字でカウントするところである。
LOCAL変数であっても避けるべきこと¶
LOCAL
は非常に使い勝手のよい変数であるが、それでもERBの仕様上避けるべきである事象は存在する。
- 同じイベント関数間を跨いだ使用
イベント関数はERBの仕様により複数定義してよいことになっている。
これはLOCAL
も全ての関数で共用になることを意味しており、
同じイベント関数間を跨ぐ形のLOCAL
に依存する処理は
他の同名関数の割り込みによって破綻する恐れが極めて高い
こういう目的にはTFLAG
等を使うことをおすすめする
LOCAL@~
の使用
LOCAL
変数はその関数の外からLOCAL@
で参照ができるようにはなっている
しかし、これは「デバッグのため」の措置であり、
値を確認するだけの場合であれば特に問題にはならないが、
代入し始めるとバグの要因にもなるし、デバッグも難しくするため
通常のコードでこれを使うことは全く薦められない
(将来的にはLOCAL@~
は読み込み専用にしたいところ)
それをしないで済む実装系を考えることをオススメする
- static変数として使うこと
LOCAL
は関数が終わっても値が保持され続ける、
つまりCでいうstatic(静的)な変数に近い挙動をする
だが、だからといってLOCAL
をstaticな変数として使うことは避けるべき
これは以下のようなコードを考えるとわかる
@TEST
IF LOCAL == 0
;~
ELSEIF LOCAL == 1
;~
ELSE
;~
ENDIF
LOCAL = (何かしらの条件に依存して決定)
このコードは最後に設定したLOCAL
の値で次の挙動が決まる
LOCAL
はstaticなのだから一見問題なさそうに見えるが
このコード、セーブデータのロードを挟むと破綻する恐れがある
LOCAL
はセーブデータに保存されない変数であり、
- タイトルに戻る
- セーブデータのロード
等によって値は破棄される
つまり、前回設定した値に依存する処理は簡単にバグの原因になるのである
LOCAL
はあくまでもその1回の処理で閉じる使い方をするべきである
IF
を使うべきかSELECTCASE
を使うべきか¶
IF
とSELECTCASE
はA = RAND:5
後にA
で分岐といった場合には
どちらも使っても同様なコードが書けるが、
その処理速度には大きな開きがあり、
IF
の方がSELECTCASE
の倍ほどの処理時間を要する
上の例のような単純な値分岐の場合には
IF
よりもSELECTCASE
を使う方が効率がよいと言える
なお、口上で同じ事をやる場合にはPRINTDATA
系を使う方が
コードが短く、処理も速い場合があることを明記しておく
CALLF
と疑似セッター¶
CALLF
命令は
式中関数を呼び出し、その返値は無視する
と使い道がなさげな命令である。
しかし、次のような使い方が存在する
@SET_VALUE(ARG, ARG:1)
#FUNCTION
RETURNF VALUE("SET", ARG, ARG:1)
@GET_VALUE(ARG)
#FUNCTION
RETURNF VALUE("GET", ARG)
@VALUE(ARGS, ARG, ARG:1)
#FUNCTION
IF ARGS == "GET"
RETRUNF LOCAL:ARG
ELSEIF ARGS == "SET"
LOCAL:ARG = ARG:1
ENDIF
こうすると、関数@VALUE
のLOCAL
にLOCAL@
を使わずに参照可能になる。
一文字変数を使わずに複数の関数をまたいで配列を参照したい場合に有用ではある。
色々小難しい小話¶
私家改造でのスクリプト処理高速化の裏話¶
最近の私家改造では、スクリプト処理の高速化をはかったわけだが、
その中身は非常にトリッキーなものである。
これまでのスクリプト処理は簡単に書くとこんな感じである
(メインルーチン)→(スクリプト実行開始)→(処理準備)
→(スクリプト実処理)→(無限ループ判定ルーチン)
→(メインルーチン)
これが1行ごとに繰り返されていたのが既存のコードである。
見ればわかるように、これは非常に効率の悪い処理である。
これを現在の私家改造では以下のように作り替えた。
(メインルーチン)→(スクリプト実行開始)
→{(処理準備)→(スクリプト処理)→(無限ループ判定)}×n
→(メインルーチン)
ポイントは2行目。こちらの処理ではメインルーチンに戻る必要が生じない限り、
スクリプトの実行を延々とループさせることで、関数の呼び出し回数を減らすことに成功している。
さらに、一部の使用頻度の高い処理については特別な処理パスにすることで、
より処理時間を減らすことでREPEAT~REND
などの高速化に成功している。
Emueraのエラーチェックの内容¶
Emueraは起動後コードを読み込み、エラーがあればそれを表示してくれる。
この表示には2パターンあり、
- ファイル読み込み時に表示されるエラー
*****.ERB読み込み中…
の下に表示されるエラー
- ファイル読み込み後の構文チェックで表示されるエラー
- ファイル読み込み終了後、タイトル表示までに表示されるエラー
であり、これはコードチェックのタイムスケールの違いによって区分される。
具体的には以下のように順番にエラーをチェックしている。
- ファイル読み込み時のエラーチェック
- 1 解釈可能な行かの確認におけるエラーチェック
- a.
[SKIPSTART]
や#
で始まる行- 正しく用いられているか確認、エラーならエラー表示
- b.関数宣言行
- 宣言の書式が正しいか確認、エラーならエラーを表示
- c.
+
or-
で始まる行- 前置のインクリメント・デクリメントであるか確認、そうでないならエラー表示
- d.命令・変数で始まるはずの行
- まず、最初の来る文字列が命令もしくは変数かを確認
- どちらでもなければエラー表示
- そもそも最初に来る文字列が不正(
\
や$
などのあるはずのない記号を含んでいる)な場合は例外を吐いてここで読み込み打ち切り(1731t以降では打ち切らないように変更)
- e.変数への代入行と予想される場合
- 代入の形になっているかチェック、なってなければエラーを表示
- a.
- 2 ファイル読み込み後の構文チェックで行われるエラーチェック
- a.宣言された関数の中身のチェック
#FUNCTION
宣言された関数なら、使えない命令がないかのチェック- 「起動時に引数を解析する」場合は引数が正しい書式になっているかチェック
IF~ELSEIF~ELSE~ENDIF
やREPEAT~REND
などの対となって使われる命令の対応関係のチェックCALL
などの飛び先チェック(CALLFORM
のような実行時に飛び先が決定される命令はチェックせず)
- a.宣言された関数の中身のチェック
- 1 解釈可能な行かの確認におけるエラーチェック
PRINT
系とPRINTFORM
系の処理量の違い¶
変数の中身を含まない平文を表示するときにはPRINT
系とPRINTFORM
系どちらが内部処理量が少ないか。
答えは(問題にするまでもなく予想はつくだろうが)PRINT
系である。
PRINT
系は引数をそのまま表示するため非常に処理量が少ない。
-
PRINT系
引数チェック:引数をそのまま文字列として代入
命令実行内容:引数を取り出し、そのまま表示用関数に渡すだけ -
PRINTFORM系
引数チェック:引数を取り出し、変数の存在などをチェックしstring.Format書式に対応した文字列として保存
命令実行内容:引数を取り出し、書式に変数の中身を入れる処理を行い、表示用文字列を作成して表示用関数に渡す
このようにPRINT
系とPRINTFORM
系では平文だとしても処理量には雲泥の差がある。
ちなみに1755aでPRINTFORM
系に平文を与えた場合の処理速度が大幅に改善されたため、差はかなり縮まったと思われる(それでも明らかに遅いだろうが)
IF文の条件判定の順番¶
次のようなIF文を考える
IF A && (B || (C && D))
(簡単化のため短絡評価は考えないものとする)
単純に考えれば()内が優先されるので、C→D→B→Aとなると思われるが、実際のEmueraの処理ではA→B→C→Dの順で判定される。
なぜこのようになるかと言うと、これはEmueraでの構文の処理法に由来する。
上の構文はEmueraでは次のように解釈される。
1, Y = C && Dとして、
IF A && (B || Y)
1. X = B || Yとして、
IF A && X
で、実際の処理は次のようになる。
1. A && Xを評価
1a. 左辺のAを評価
1b. 右辺のXを評価→X = B || YなのでB || Yの評価へ進む
1. B || Yを評価
2a. 左辺のBを評価
2b. 右辺のYを評価→Y = C && DなのでC && Dの評価へ進む
1. C && Dの評価
3a. 左辺のCを評価
3b. 右辺のDを評価
以上より判定される順番はA→B→C→Dとなる。
考えればわかるが、判定結果は正しくなるため、この方法には何の問題はないし、短絡判定は左辺優先なのでむしろこうした方が合理的なのである。
(そもそも左辺が偽なら右辺の()内は見る必要がないので処理が減るし、左辺が真の場合でも行われる処理は右辺の()内を先に評価した場合と変わらないため、総合的に見れば処理量は減ることになる。
そういう意味ではEmueraのみを想定しているコードではエラーにならない範囲で最も成立しにくい条件を最初に持ってくるというのは極限論的には無意味なコーディングではない。
もちろん現実的な話をするなら、Q&Aに書いたようにEmueraの処理の律速は描画処理なので、ここにこだわったところで処理速度に有意な差が出ることはない。)
なぜVARSET
は速いのか¶
変数配列全体を指定した値で初期化するVARSET命令は同様の実装である
REPEAT N(配列の大きさ)
A:N = 0
REND
に比べて、処理速度にして軽く見積もっても1万倍以上は速い。
(VARSET
は配列要素数が100万個でもほぼ一瞬で処理が終わる)
これにはちゃんとした理由がある。
VARSET
は呼び出されると、内部で与えられた配列を呼び出し、指定された値を代入するループ処理を行う。
この内部で行われるループ処理は実行時に最適化され、非常に高速に実行される。
一方、REPEAT~REND
の場合は以下のような処理が行われる。
1. REPEATに入る
(最初ならCOUNTを0にセット、
RENDから飛んできたらCOUNTに1を追加し終了判定)
2. A:COUNT = 0を実行
3. RENDの行は何もせずにREPEATの行に戻る
(この1~3が設定された回数繰り返される)
このようにREPEAT~REND
の場合は変数への値代入処理だけでなく、3行のコードを駆動するための処理が増える。
結果、単純に処理量が増えるだけでなく、処理の複雑化により実行時の最適化も難しくなるため非常に時間がかかってしまう。
一般に、ERBで書かれたスクリプトと同じ処理をする内部命令を実装すれば、内部命令の方が格段に速くなることがほとんどである。
(特に配列に対してループを回すような処理であれば)
ただし、有意な差が出るような長く重いコード自体少ないのでこの違いを体感できる事例はそれほど多くはなかったりする。
コード読み直しの仕様とメモリ消費量の増加¶
今のEmueraにはERBファイル再読込の機能があるが、これは使う度にメモリ消費量が増加していく。
これには避けられない要因がある。
EmueraのERBコードの処理では、起動時に全てのコードを一行ずつ読み込み、クラスに押し込めて管理しているが、このとき、今いる行の次の行の情報を保持するようになっている。
さて、ERBの再読込はシステム側の入力待ちだけでなくINPUT
などの入力待ち命令中でも行えるが、
この時今いるINPUT
を含む関数が記述されているファイルが再読込されてコードの情報が置き換えられるといったいどうなるか?
答えは簡単である。
Emueraは実行するコードを見失いエラーを出す。
そのため、再読込時には既存のコードを捨てることができない。
よって、すでに読み込んであるコードとは別に新たにコードをスタックする仕組みになっている。
そして、関数の定義情報を書き換えることで新規コードの方を実行するようになっている。
この仕様により動作中の関数は一旦終了し再度呼ばれるまでは古いコードに従い動作することになるが、これは防ぎようがない事象である。
また、再読込後の古いコードであるが、再読込ファイルのコードの追加後は新旧の区別が困難であるため、消さずに放置している。そのため、再読込の度にコードのスタックが増大しメモリ消費量が増えていくことになる。
そのため、開発中等で必要になる場合以外は使わない方が安全な機能ではある。