OS X用GigE Visionカメラドライバ - その44 [OS X用GigE Vision]
進捗がはかばかしくないGEN<i>CAM規格の実装。何回かにわけてやってきたスキーマファイルに書かれているノードの定義のダイアグラムへの書き下しは今日で最後。今日はノードの持っているエレメントの中で最も複雑なpIndex周りの定義。ダイアグラムをプリントアウトして全部並べたらなんかわかるような気がしてきた....かな?
IntegerノードとFloatノードはpIndexを持つことができる。pIndexと兄弟エレメントにValueIndexedとpValueIndexedが複数個あるはずである。また、さらに兄弟エレメントとしてValueDefaultとpValueDefaultを持つことができる。図-32のようになっている。 pIndexは整数ノードの名前を保持している。ValueIndexedとpValueIndexedエレメントはすべてIndexというアトリビュートを持っていて、整数の定数値を持っている。さらにValueIndexedは定数値(Integerノードの中なら整数値、Floatノードなら浮動小数点数値)を、pValueIndexedは整数あるいは浮動小数点数を返すノードの名前を保持している。
これがどういうふうに動作するかというと...
まずpIndexの値を調べる。これは整数ノードを指しているはずである。この整数ノードに問い合わせて整数値を得る。その整数値と同じIndexアトリビュートの値を持つValueIndexed、あるいはpValueIndexedの値がInteger、Floatノードの値となる、というのである。もし対応するIndexアトリビュートを持つエレメントがないときはValueDefaultかpValueDefaultの値がノードの値となる。
こういうのは普通ならpIndexの内部構造となっているべきもので、GenApiの場合はそれがノードの子エレメントにフラットに並んでいる。pIndex単独では整数の値がわかるだけなのでpIndex自身では何もできない。従って上の作業を素直に実装するなら全部ノードの仕事ということになって、ノードのクラスは巨大なものになる。
オブジェクト指向というか、それ以前の構造化プログラミングという基本の「キ」を知らない、あるは知ってるけど無視する人が作ったんだろう。その人はおそらくこんな複雑なオブジェクトもひょいひょいとコードできる頭のいい人なんだろう。「オブジェクト指向?そんなもん使わなくてもこのくらいアセンブラで書けるわ」という人なんだろうな。勘弁して欲しいものである。
例えば、無理やりスキーマファイルの通りにクラス設計したとしても、まったく同じコードが複数のクラスに現れることになるので、その部分をインクルードファイルにするなどして、手間を省くべきである(コピペはやめたほうがいい)。さらにそんなことするぐらいなら、クラス構造を考え直して、同じものが分散しないようにした方がずっといい。
またエレメントもノードの子要素としてフラットに並んでいるので、セマンティクスに合わせて階層化した方がわかりやすいし、実装はずっと簡単になる。
例えばGroupノードはスキーマファイルではNodeElementTemplateを持っていないけど、ノードとみなすことにする。ただしGroupノードはpFooBarに参照されないこともあり得るので、暗黙のCategoryノードを追加して参照から漏れているGroupノードを参照させることにする。こうしないとGroupノードのコメントの意味がなくなるし、Rootカテゴリのツリーとして配列し直したときにARCによって解放されてしまうからである(規格のドキュメントにはGroupノードは動作させる上では意味がないというようなことが書いてある。最終的に無視されるならそんなものは規格に含めるべきではない。僕にはその姿勢が理解できない)。
一つ例外があって、RegisterElementTemplateにあってアドレスの計算をするためのIntSwissKnifeはpFooBarから参照されることはないが、これをノードとみなして、これを保持しているRegiterノードには参照するpFooBarを追加してIntSwissKnifeを参照させることにする。その方がすっきりする。
またこのアドレス計算用IntSwissKnifeは実用的にはNameアトリビュートは必要ないので、もしカメラ記述ファイルによってはNameアトリビュートが与えられていない場合もあり得る。その場合は重複しない名前を勝手に割り当てないといけないかもしれない。これはRootカテゴリから辿ることでノードオブジェクトの保持をする場合、必要になる。
そうするとおそらく多くのノードで使われないプロパティが大量に発生して、メモリの無駄になるけど、それは諦める。定義されたエレメントだけを保持する辞書にすれば無駄はなくなるけど、それは非常に面倒である。
ノードの定義の中に現れず、デフォルト値も与えられていないエレメントのプロパティはnilとする。ノードは自分のプロパティにnilのエレメントがあったら、そのエレメントの機能を持っていない、と解釈する。例えばToolTipやDescriptionエレメントがない場合はそう言った文字列が表示できないということになるし、MinやpMinエレメントがない場合は最小値が存在しない、ということになる。
nilの解釈のされ方はエレメントのセマンティクスに依存するので、エレメントのクラスオブジェクトの仕事にする方がわかりやすいはずだけど、そうすると他のエレメントの状態も知る必要があったりして、かえって複雑になってしまうような気がする。したがって残念ながらnilエレメントの解釈はノードの仕事ということになる。
またノード内部で同じ使われ方(同じセマンティクス)のエレメント、例えばLengthとpLengthなどは一つの複合エレメントとしてオブジェクト化する。ノードオブジェクトは自分のバイト幅を知るために、LengthとpLengthのどちらが存在しているかを調べて存在している方にバイト幅を問い合わせるのではなく、Length複合エレメントオブジェクトに問い合わせると、Length複合エレメントオブジェクトはどちらを持っているかをあらかじめ知っていて(エレメントを定義するXMLを読み込んだ時点で確定している)、存在する方の値を返す、ということにする。例えばLengthとpLengthは実際にはどちらか一方が必ず存在しなければいけないので、Length複合エレメントは存在する方だけを保持する構造を持っていればいい、ということになる。
Categoryノードは特殊だけど、これもpFeatureが参照するノードの配列(NSArray)を値として返す、ということにする。
10.4.27 pIndexの構造
pIndexの使い方はかなり複雑である。分かりにくいのでここでまとめることにする。IntegerノードとFloatノードはpIndexを持つことができる。pIndexと兄弟エレメントにValueIndexedとpValueIndexedが複数個あるはずである。また、さらに兄弟エレメントとしてValueDefaultとpValueDefaultを持つことができる。図-32のようになっている。 pIndexは整数ノードの名前を保持している。ValueIndexedとpValueIndexedエレメントはすべてIndexというアトリビュートを持っていて、整数の定数値を持っている。さらにValueIndexedは定数値(Integerノードの中なら整数値、Floatノードなら浮動小数点数値)を、pValueIndexedは整数あるいは浮動小数点数を返すノードの名前を保持している。
これがどういうふうに動作するかというと...
まずpIndexの値を調べる。これは整数ノードを指しているはずである。この整数ノードに問い合わせて整数値を得る。その整数値と同じIndexアトリビュートの値を持つValueIndexed、あるいはpValueIndexedの値がInteger、Floatノードの値となる、というのである。もし対応するIndexアトリビュートを持つエレメントがないときはValueDefaultかpValueDefaultの値がノードの値となる。
こういうのは普通ならpIndexの内部構造となっているべきもので、GenApiの場合はそれがノードの子エレメントにフラットに並んでいる。pIndex単独では整数の値がわかるだけなのでpIndex自身では何もできない。従って上の作業を素直に実装するなら全部ノードの仕事ということになって、ノードのクラスは巨大なものになる。
オブジェクト指向というか、それ以前の構造化プログラミングという基本の「キ」を知らない、あるは知ってるけど無視する人が作ったんだろう。その人はおそらくこんな複雑なオブジェクトもひょいひょいとコードできる頭のいい人なんだろう。「オブジェクト指向?そんなもん使わなくてもこのくらいアセンブラで書けるわ」という人なんだろうな。勘弁して欲しいものである。
11 ノードとエレメントのクラス構造
以上で、スキーマファイルを読んで整理した。この構造をObjective-Cのオブジェクトとして書くのはイマイチだということははっきりした。なぜなら似たような、あるいはまったく同じ定義が複数のクラスに分散してしまうからである。これは分かりにくいし、メンテナンス性が悪い。例えば、無理やりスキーマファイルの通りにクラス設計したとしても、まったく同じコードが複数のクラスに現れることになるので、その部分をインクルードファイルにするなどして、手間を省くべきである(コピペはやめたほうがいい)。さらにそんなことするぐらいなら、クラス構造を考え直して、同じものが分散しないようにした方がずっといい。
またエレメントもノードの子要素としてフラットに並んでいるので、セマンティクスに合わせて階層化した方がわかりやすいし、実装はずっと簡単になる。
11.1 クラス構造設計の方針
まず、設計方針を決めておこう。11.1.1 ノードオブジェクト
まずすべてのノードは抽象ベースクラスNodeのサブクラスにする。ノードとは- NameあるいはCommentアトリビュートを持つ
- pFooBarの形(pAddress、pInvalidatorなど)の参照エレメントにその名前が出現する
例えばGroupノードはスキーマファイルではNodeElementTemplateを持っていないけど、ノードとみなすことにする。ただしGroupノードはpFooBarに参照されないこともあり得るので、暗黙のCategoryノードを追加して参照から漏れているGroupノードを参照させることにする。こうしないとGroupノードのコメントの意味がなくなるし、Rootカテゴリのツリーとして配列し直したときにARCによって解放されてしまうからである(規格のドキュメントにはGroupノードは動作させる上では意味がないというようなことが書いてある。最終的に無視されるならそんなものは規格に含めるべきではない。僕にはその姿勢が理解できない)。
一つ例外があって、RegisterElementTemplateにあってアドレスの計算をするためのIntSwissKnifeはpFooBarから参照されることはないが、これをノードとみなして、これを保持しているRegiterノードには参照するpFooBarを追加してIntSwissKnifeを参照させることにする。その方がすっきりする。
またこのアドレス計算用IntSwissKnifeは実用的にはNameアトリビュートは必要ないので、もしカメラ記述ファイルによってはNameアトリビュートが与えられていない場合もあり得る。その場合は重複しない名前を勝手に割り当てないといけないかもしれない。これはRootカテゴリから辿ることでノードオブジェクトの保持をする場合、必要になる。
11.1.2 エレメントの位置付け
ノードの持っている子要素をノードのエレメントと呼ぶことにする。エレメントも抽象エレメントオブジェクトのサブクラスとして定義して、ノードオブジェクトのプロパティとして保持する。そうするとおそらく多くのノードで使われないプロパティが大量に発生して、メモリの無駄になるけど、それは諦める。定義されたエレメントだけを保持する辞書にすれば無駄はなくなるけど、それは非常に面倒である。
ノードの定義の中に現れず、デフォルト値も与えられていないエレメントのプロパティはnilとする。ノードは自分のプロパティにnilのエレメントがあったら、そのエレメントの機能を持っていない、と解釈する。例えばToolTipやDescriptionエレメントがない場合はそう言った文字列が表示できないということになるし、MinやpMinエレメントがない場合は最小値が存在しない、ということになる。
nilの解釈のされ方はエレメントのセマンティクスに依存するので、エレメントのクラスオブジェクトの仕事にする方がわかりやすいはずだけど、そうすると他のエレメントの状態も知る必要があったりして、かえって複雑になってしまうような気がする。したがって残念ながらnilエレメントの解釈はノードの仕事ということになる。
またノード内部で同じ使われ方(同じセマンティクス)のエレメント、例えばLengthとpLengthなどは一つの複合エレメントとしてオブジェクト化する。ノードオブジェクトは自分のバイト幅を知るために、LengthとpLengthのどちらが存在しているかを調べて存在している方にバイト幅を問い合わせるのではなく、Length複合エレメントオブジェクトに問い合わせると、Length複合エレメントオブジェクトはどちらを持っているかをあらかじめ知っていて(エレメントを定義するXMLを読み込んだ時点で確定している)、存在する方の値を返す、ということにする。例えばLengthとpLengthは実際にはどちらか一方が必ず存在しなければいけないので、Length複合エレメントは存在する方だけを保持する構造を持っていればいい、ということになる。
11.1.3 ノード、エレメントの値
スキーマファイルでは細かく型が分類されているが、idに統一する。従って整数や浮動小数点数はNSNumberで、文字列はNSStringを使う。 ノードとエレメントの値の問い合わせは統一できて@protocol GADEvaluationProtocol <NSObject> - (id)readValue; - (void)writeValue:(id)newValue; @endで読み書きする。つまりノードとエレメントは抽象クラスのレベルでこのプロトコルを採用し、サブクラスで実際の値となるオブジェクトをやり取りするということにする。
Categoryノードは特殊だけど、これもpFeatureが参照するノードの配列(NSArray)を値として返す、ということにする。
2016-05-14 21:07
nice!(0)
コメント(0)
トラックバック(0)
コメント 0