16
|
1 \section{GearsOSのInterfaceの構文の改良}
|
|
2 GearsOSのInterfaceでは、 従来はDataGearとCodeGearを分離して記述していた。
|
|
3 CodeGearの入出力をDataGearとして列挙する必要があった。
|
|
4 CodeGearの入出力として\texttt{\_\_code()}の間に記述したDataGearの一覧と、Interface上部で記述したDataGearの集合が一致している必要がある。
|
|
5
|
|
6 従来の分離している記法の場合、 このDataGearの宣言が一致していないケースが多々発生した。
|
|
7 またInterfaceの入力としてのDataGearではなく、 フィールド変数としてDataGearを使うようなプログラミングスタイルを取ってしまうケースも見られた。
|
|
8 GearsOSでは、 DataGearやフィールド変数をオブジェクトに格納したい場合、 Interface側ではなくImpl側に変数を保存する必要がある。
|
|
9 Interface側に記述してしまう原因は複数考えられる。
|
|
10 GearsOSのプログラミングスタイルに慣れていないことも考えられるが、構文によるところも考えられる。
|
|
11 CodeGearとDataGearはInterfaceの場合は密接な関係性にあるが、 分離して記述してしまうと「DataGearの集合」と「CodeGearの集合」を別個で捉えてしまう。
|
|
12 あくまでInterfaceで定義するCodeGearとDataGearはInterfaceのAPIである。
|
|
13 これをユーザーに強く意識させる必要がある。
|
|
14
|
|
15 golangにもInterfaceの機能が実装されている。
|
|
16 golangの場合はInterfaceは関数の宣言部分のみを記述するルールになっている。
|
|
17 変数名は含まれていても含まなくても問題ない。
|
|
18
|
|
19 \begin{lstlisting}[frame=lrbt,label=src:golang_interface,caption={golangのinterface宣言}]
|
|
20 type geometry interface {
|
|
21 area() float64
|
|
22 perim() float64
|
|
23 }
|
|
24 \end{lstlisting}
|
|
25
|
37
|
26 \section{Implementの型をいれたことによる間違ったGearsプログラミング}
|
|
27 Implementの型を導入したが、 GearsOSのプログラミングをするにつれていくつかの間違ったパターンがあることがわかった。
|
|
28 自動生成されるStubCodeGearは、 goto metaから遷移するのが前提であるため、 引数をContextから取り出す必要がある。
|
|
29 Contextから取り出す場合は、 実装しているInterfaceに対応している置き場所からデータを取り出す。
|
|
30 この置き場所は\texttt{data}配列であり、 配列の添え字は\texttt{enum Data}と対応している。
|
|
31 また各CodeGearからgotoする際に、 遷移先のInterfaceに値を書き込みに行く。
|
|
32
|
|
33
|
|
34 Interfaceで定義したCodeGearと対応しているImplementのCodeGearの場合はこのデータの取り出し方で問題はない。
|
|
35 しかしImplementのCodeGearから内部でgotoするCodeGearの場合は事情が異なる。
|
|
36 内部でgotoするCodeGearは、 Javaなどのプライベートメソッドのように使うことを想定している。
|
|
37 このCodeGearのことをprivate CodeGearと呼ぶ。
|
|
38 privateCodeGearにgotoする場合、 goto元のCodeGearからは\texttt{goto meta}経由で遷移する。
|
|
39 goto metaが発行されるとStub Code Gearに遷移するが、現在のシステムではInterfaceから値をとってくることになってしまう。
|
|
40
|
16
|
41
|
|
42 \section{メタ計算部分の入れ替え}
|
|
43 GearsOSでは次のCodeGearに移行する前のMetaCodeGearとして、 デフォルトでは\texttt{\_\_code meta}が使われている。
|
20
|
44 \texttt{\_\_code meta}はcontextに含まれているCodeGearの関数ポインタを、 enumからディスパッチして次のStub CodeGearに継続するものである。
|
|
45
|
|
46 例えばモデル検査をGearsOSで実行する場合、 通常のStub CodeGearのほかに状態の保存などを行う必要がある。
|
|
47 この状態の保存に関する一連の処理は明らかにメタ計算であるので、 ノーマルレベルのCodeGearではない箇所で行いたい。
|
|
48 ノーマルレベル以外のCodeGearで実行する場合は、 通常のコード生成だとStubCodeGearの中で行うことになる。
|
|
49 StubCodeGearは自動生成されてしまうため、 値の取り出し以外のことを行う場合は自分で実装する必要がある。
|
|
50 しかしモデル検査に関する処理は様々なCodeGearの後に行う必要があるため、 すべてのCodeGearのStubを静的に実装するのは煩雑である。
|
|
51
|
22
|
52 ノーマルレベルのCodeGearの処理の後に、StubCodeGear以外のMeta Code Gearを実行したい。
|
|
53 Stub Code Gearに直ちに遷移してしまう\texttt{\_\_code meta}以外のMeta CodeGearに、 特定のCodeGearの計算が終わったら遷移したい。
|
|
54 このためには、特定のCodeGearの遷移先のMetaCodeGearをユーザーが定義できるAPIが必要となる。
|
20
|
55 このAPIを実装すると、ユーザーが柔軟にメタ計算を選択することが可能となる。
|
16
|
56
|
20
|
57 GearsOSのビルドシステムのAPIとして\texttt{meta.pm}を作製した。
|
|
58 これはPerlのモジュールファイルとして実装した。
|
23
|
59 meta.pmはPerlで実装されたGearsOSのトランスコンパイラであるgenerate\_stub.plから呼び出される。
|
21
|
60 meta.pmの中のサブルーチンである\texttt{replaceMeta}に変更対象のCodeGearと変更先のMetaCodeGearへのgotoを記述する。
|
22
|
61 ユーザーはmeta.pmのPerlファイルをAPIとしてGearsOSのトランスコンパイラにアクセスすることが可能となる。
|
20
|
62
|
21
|
63 具体的な使用例をコード\ref{src:metapm}に示す。
|
|
64 meta.pmはサブルーチン\texttt{replaceMeta}が返すリストの中に、特定のパターンで配列を設定する。
|
|
65 各配列の0番目には、goto metaを置換したいCodeGearの名前を示すPerl正規表現リテラルを入れる。
|
|
66 コード\ref{src:metapm}の例では、\texttt{PhilsImpl}が名前に含まれるCodeGearを指定している。
|
|
67 すべてのCodeGearのgotoの先を切り替える場合は\texttt{qr/.*\//}などの正規表現を指定する。
|
20
|
68
|
|
69 \lstinputlisting[label=src:metapm, caption=meta.pm]{src/meta.pm}
|
25
|
70
|
|
71 generate\_stub.plはGears CbCファイルの変換時に、 CbCファイルがあるディレクトリにmeta.pmがあるかを確認する。
|
|
72 meta.pmがある場合はモジュールロードを行う。
|
|
73 meta.pmがない場合はmeta Code Gearにgotoするものをデフォルト設定として使う。
|
|
74 各Gode Gearが\texttt{goto文}を呼び出したタイミングでreplaceMetaを呼び出し、 ルールにしたがってgoto文を書き換える。
|
37
|
75 変換するCodeGearがルールになかった場合は、 デフォルト設定が呼び出される。
|
|
76
|
|
77 \section{別Interfaceからの書き出しを取得する必要があるCodeGear}
|
|
78
|
|
79 従来のMetaCodeGearの生成では、 別のInterfaceからの入力を受け取るCodeGearのStubの生成に問題があった。
|
|
80 具体的なこの問題が発生する例題をソースコード\ref{src:insertTest1}に示す。
|
43
|
81 \lstinputlisting[label=src:insertTest1, caption=別Interfaceからの書き出しを取得するCodeGearの例]{src/pop2test.cbc}
|
37
|
82 この例では\texttt{pop2Test}Code Gearから \texttt{stack->pop2}を呼び出し、 継続として\texttt{pop2Test1}を渡している。
|
|
83 \texttt{pop2Test}自体はStackTest Interfaceであり、 \texttt{stack->pop2}の\texttt{stack}はStack Interfaceである。
|
43
|
84 例題ではStack Interfaceの実装はSingleLinkedStackである。
|
|
85 SingleLinkedStackの\texttt{pop2}の実装をソースコード\ref{src:pop2}に示す。
|
|
86 \lstinputlisting[label=src:pop2, caption=SingleLinkedStackのpop2]{src/pop2.cbc}
|
37
|
87
|
43
|
88 このコードをgenerate\_stub.pl経由でメタ計算を含むコードに変換する。
|
|
89 変換した先のコードを\ref{src:pop2meta}に示す。
|
|
90 \lstinputlisting[label=src:pop2meta, caption=SingleLinkedStackのpop2のメタ計算]{src/pop2meta.cbc}
|
|
91
|
37
|
92
|
38
|
93 当初Perlスクリプトが生成した\texttt{pop2Test1}のstub CodeGearはソースコード\ref{src:pop2stub-origin}のものである。
|
41
|
94 CodeGear間で処理されるデータの流れの概要図を図\ref{fig:stackTest1}に示す。
|
38
|
95 \lstinputlisting[label=src:pop2stub-origin, caption=生成されたStub]{src/pop2stub-origin.cbc}
|
|
96 \texttt{\_\_code pop2Test}で遷移する先のCodeGearはStackInterfaceであり、 呼び出しているAPIは\texttt{pop2}である。
|
|
97 pop2はスタックから値を2つ取得するAPIである。
|
|
98 取得したAPIはGearsOSのInterfaceの処理ルールにより、 Context中のStack Interfaceのデータ格納場所に書き込まれる。
|
|
99 しかしソースコード\ref{src:pop2stub-origin}の例では\texttt{Gearef(context, StackTest)}でContext中の\texttt{StackTest} Interfaceのdataの置き場所から値を取得している。
|
|
100 これではpop2でせっかく取り出した値を取得できない。
|
41
|
101
|
39
|
102 ここで必要となってくるのは、 呼び出し元のStack Interfaceからの値の取得である。
|
|
103 どのInterfaceから呼び出されているかは、 コンパイルタイムには確定できるのでPerlのトランスコンパイラでStub Codeを生成したい。
|
|
104
|
41
|
105 \begin{figure}[htp]
|
|
106 \begin{center}
|
|
107 \includegraphics[width=130mm]{drawio/stackTest1.pdf}
|
|
108 \end{center}
|
|
109 \caption{stackTest1のstubの概要}
|
|
110 \label{fig:stackTest1}
|
|
111 \end{figure}
|
39
|
112
|
|
113 別Interfaceから値を取得するには別の出力があるCodeGearの継続で渡されたCodeGearをまず確定させる。
|
|
114 今回の例では\texttt{pop2Test1}が該当する。
|
|
115 このCodeGearの入力の値と、 出力があるCodeGearの出力を見比べ、 出力をマッピングすれば良い。
|
|
116 Stack Interfaceのpop2はdataとdata1に値を書き込む。
|
|
117 pop2Test1の引数はdata, data1, stackであるので、前2つにpop2の出力を代入したい。
|
|
118
|
|
119 Contextから値を取り出すのはメタ計算であるStub CodeGearで行われる。
|
|
120 別Interfaceから値を取り出そうとする場合、 すでにPerlトランスコンパイラが生成しているStubを書き換えてしまう方法も取れる。
|
|
121 しかしStubCodeGearそのものを、 別Interfaceから値を取り出すように書き換えてはいけない。
|
|
122 これは別Interfaceの継続として渡されるケースと、 次のgoto先として遷移するケースがあるためである。
|
|
123 前者のみの場合は書き換えで問題ないが、 後者のケースで書き換えを行ってしまうとStubで値を取り出す先が異なってしまう。
|
|
124 どのような呼び出し方をしても対応できるようにするには工夫が必要となる。
|
|
125
|
|
126
|
|
127 GearsOSでは継続として渡す場合や、 次のgoto文で遷移する先のCodeGearはノーマルレベルではenumの番号として表現されていた。
|
|
128 今回のような次に実行するStub CodeGear、つまりメタCodeGearを切り替えたい場合は、ノーマルレベルからメタレベルへの変換時にenumの番号を切り替えることで実現可能である。
|