昔の最近の出来事(2004.09)

2004/09/30

出張。

2004/09/29

出張。

2004/09/28

出張。

2004/09/27

具合が悪くて一回休み。一日中寝てたり。

正規表現についてもう少し調べたり。すると先日欲しいと思ったperlでいう所の 括弧で括る話について、「前方参照」という名で実装されている模様。 所が、取りだしの手順がすぐに判らず悩んでみたり。マニュアルを行ったり来たり した所、java.util.regex.* にPatternクラスとMatcherクラスというのがあり、 これらを使用して、perlのそれと同じ感じにできるという事みたい。 例えば、

#    posx posy	image         "label"
item 100  100	"./test1.jpg" "テスト1"
item 100  120	"./test2.jpg" "テスト2"

という感じの空白で区切られた一行フォーマットを読み込む場合、

Pattern p = Pattern.compile("[\\s\\t]*item[\\s\\t]+(\\d+)[\\s\\t]+(\\d+)[\\s\\t]+\\\"(\\S*)\\\"[\\s\\t]+\\\"([^\\\"]*)\\\"") ;
Matcher m=p.matcher(str) ;
if( m.matches()==true ){
   System.out.println("pos=" + m.group(1) + "," + m.group(2) + " : " + m.group(3) + " : " + m.group(4) );
}

てな感じで取り出せば良いらしい。Patternクラスをnewで当てるのではない とか(実体はcompileメソッドでnew Pattern("Regex",0)を実行しているの ですが)、文字リテラルとして'\'はエスケープしなくてはならないので、 \d+のような正規表現も"\\d+"と書かなくてはならないとか、 Matcher.group()メソッドを実行する為に、一度Matcherクラスの インスタンスを生成しなくてはならないというオブジェクト指向っぽく 無い操作が必要であるとか、色々ありますが、一応実現可能ではあるみたい。
因みに、Pattern.compile("Regex").matcher("str").matches()とやるのと Pattern.matches("Regex","str")とString.maches("Regex") とは同じ事 みたいなのですが、どちらにしても、Matcher.groupメソッドは呼び 出せないという感じみたい。こういう複雑なマッチの場合、マッチさせただけで終りという事は 殆どの場合無いと思いますので、 Stringクラスにパターンマッチシーケンスを入れろとまでは言わないまでも (Stringあってのパターンマッチングですが、その逆は真では無いから)、 PatternクラスからMatcherの結果が取り出せればなぁ (PatternクラスあってのMatcherクラスで、その逆も真、つまり一心同体だから) という気はしなくはありません。

あと、gcjのregexクラスについても調べてみたのですが、3.4.xのソース では、Patternクラスはほぼ全部「"Not implemented"」でエラーする 仕掛けになっているようなので、gcjで色々やるのは当分無理って感じ です。

キーボードのタブキーが反応しなくなってしまいました(T_T)。 1000円キーボードでは一年足らずですぐにヘタってしまうので、 メカニカルキーボードにした のですが、結局これも 一年でヘタってしまったという感じ。 稼動率がそれほど高いという訳では無い(出張ばかりで平日は殆ど 使っていませんでしたから)と思うのですが、むーん。

2004/09/26

昼頃起床。

ソースをコンパイルしてみました。gcjではコンパイルできないので、 javacで全ソースをコンパイルしてみた所、いくつかのソースが エラーしたりするのですが、とりあえず一通りのクラスファイルが できてみたり。問題はこれをjarファイルにする所なのですが、 ファイル数が多過ぎて、コマンドラインの指定に入りきらないと いう罠にハマってみたり。仕方なしにリストを採取して、 アップデートモードでファイルを追加してどうにかjarファイルを 作成できたのですが、それを使ってgcjでコンパイルすると、 メモリを思いっきり消費して、最後は赤○白×のsegfaultで死亡。 むぅ。
因みに、/usr/include/以下のヘッダファイルは参照していないようで、 ここにclassファイルよりgcjhで生成したCのライブラリヘッダを入れても コンパイラには見えていないようでした。という事は、基本的に コンパイラの解釈できる形式で且つ人が見ても判るテキスト形式の ファイルというのは存在しない為、コンパイラがサーチするライブラリ やヘッダパスに乗ったファイルを、ただテキストビュアで見るだけでは 情報を知る事はできないという事になりそうです。ですから、 「1.別途ドキュメントは用意する」もしくは「2.ソースを提供」 以外の方法で、クラスの中身を知るには「3. javapコマンドで逆アセンブルする」 くらいしか無い予感。
また、C言語のようなプロトタイプ宣言が存在しない 為、gcjではクラスファイルの生成順序を間違えると、「そんなクラス 知らん」とすぐに言われてしまいます。これは、ビルドに対して、 必要以上に明確な依存関係を要求されるという気がします。 javacの場合はクラスファイルが存在しなければ一対一に対応付く ソースファイルがあれば勝手にコンパイルして使ってしまう ようです。でも、そこまでやられてしまうと、ある場所で動かしていた 時は動いたのに、ファイルを移動すると何故か動かなくなったり しそうで、またそうなってしまった時の理由がよく判らなくなるような気 がしなくもありません。コンパイルし忘れが問題になると いうのに過剰に反応した結果の様にも思える仕様ですが、 なにもコンパイラでどうにかしなくても.....とは思いました。

SDKの日本語ドキュメントを眺めていると、VMの実行時オプションに ガベージコレクタのプロファイル情報を出力するオプションがあり、 それを眺めてみました。 ガベージコレクタが動作に要した時間が出るというものですが、 リスト構造を切ったり張ったりすると、一回のCGに要した時間よりも、 発生する頻度が増える様子が見て取れます。ガベージコレクトを行なう メモリの捻出量(?)は毎回ほぼ同じにしても、それにかかる時間は微妙に 異なる様です。 リストもサイズを限定して、非アクティブなリストを繋ぐチェーン に繋ぎとめておくような構造にすれば、ガベージコレクタの動作を 最低限に抑える事ができそうですが、そのようなチューニングが 必要か否かはプログラムによるといった所でしょうか。

文字列操作の練習をしたり。C言語でやる場合、トークンをスペースか タブに限定して、できるだけパースが面倒でないフォーマットにするか、 限度を越える場合はyacc/lexというパターンにする所なのですが、 JavaのそれだとStringクラスのメソッドにawkレベルの文字列操作が 含まれている様なので、それを使うという感じ。でも、perlの正規表現 マッチの中に、括弧でくくった部分をパラメータとして$[1-n]の変数に 取得できる構文に慣れると、awkレベルのそれでも面倒という感じです(^^;
それよりも、gcc-3.3.3に含まれるlibjavaでは、String.matchesメソッドが 無いようで、gcjではダメらしいという事の方が問題かも。 gcc-3.4.2には入っていたので、3.4.2になるまではgcjには面倒な環境の様です。 そういや、Javaって標準で使用できるクラスについての仕様というのは 決まっているのかしら?と思ったり。gcc-3.3.3のStringクラスのソースは 2002/06で、gcc-3.4.2のそれは2003/07という感じなので、割と最近 の差という感じがします。でも、Stringクラスなんて最初の頃から存在 してたのにも関わらず、拡張を続けているという事は、意外と枯れて いないのかしら?とも思ったりします。

攻殻2ndGIG鑑賞。パズとサイトーという普段は脇役的キャラクタを ターゲットとして、それぞれに一話という今回。サイトーの話に グッときました。

2004/09/25

昼頃起床。

J2SDKに含まれるJavaコンパイラjavacと、gcjの違いを調べたり。 と言っても、挙動の違いを実験で比べただけですが。
AWTやSwingを使用したものでは、gcjでネイティブバイナリを生成 する事はできない事が判ったり、ImageIOはまだgcjのライブラリに 入っていない事が判ったりそんな感じ。 gcjの--help指定で表示されるヘルプは、gccのヘルプそのまま になってて役に立たないので、オプションの事を調べるには man gcjでなくてはならないとか、割と変な発見ばかり(^^;

Antを入れていたのですが、テストするのに丁度良いbuild.xmlが無かった ので、XMLパーサーである xerces をソースからビルドしてみる 事にしてみたり。ビルド用のツールを別途ダウンロードしなくては ならない事に気づかずに、なにやらエラーするのに唸ってみたり。 xercesのREADMEに、 「xerces本体とtoolsを同じディレクトリで展開せよ」というような 事が書いてあったので、ツールをダウンロードして、本当に 同じディレクトリに展開したらうまくいかなかったり (正しくはxercesを展開してそのディレクトリにもぐった所でtoolsを展開する) で、ハマりながらビルドしてみた所、一応 ライブラリの生成はできてみたり。でも、ドキュメントディレクトリの XMLは表示できず、使い方がよく判らないので、結局バイナリ アーカイブもダウンロードしてしまうへっぽこぶりを発揮(^^;。 READMEの通りに実行すると、jarしか生成しない模様で、./build.sh だけを実行してもそれと同じ。結局、./build.sh allとか指定すれば 良いみたいというのを、build.xmlを読んだりして判ってみたり。 んー、なんて言うか、こう、微妙に不親切というかそんな感じ? 一応、AntによるビルドはOKと言えそう。それにしても、xercesの ビルドスクリプトも、Antのコマンドも、何気にシェルスクリプト でラッピングしてあって、確かにVM上で動作するバイナリは共通 にできているのですが、それを本当に使う様にするまでのやり方 に、微妙な感じがしました。
結局の所、最後はCPUの命令セットに変換されなければ実行できない 訳なのですが、VM自体はOS上で動く一アプリにしか過ぎないので、 OS上で動作するプロセスの一つとして起動する所までに、 シェルスクリプトや統合環境によるラッピングで一枚 皮をかぶせざるを得ないのが、仕方ないのですがダサいと思います。 どうせやるならば、JavaVM上で動作するUNIX風のシェル環境なり、 コンソール環境なりを作って、その上で普通にJavaのクラスを 実行ファイルの様に扱える様にすれば、もうちっとマシな感じが しなくはありません。そうなると、全てのツール(コンパイラも含む)を Java化する必要があるかも知れませんが。

そういや、gcjでは各クラスのヘッダファイルに相当するファイルが /usr/include/java|javax 下に置かれており、ファイルを見れば どのようなクラスやメソッドがあるのかはなんとなく判るのですが、 J2SDKには細かな事が判るファイルが一切無くて、普通どうやって クラスやメソッドの検索をするのだろうと思ってみたり。 何気にJ2SDKのディレクトリを探っていたら、src.zipという ファイルがあり、その中にソースが一式入ってました。 ファイル名とクラス名が一対一の言語文化なので、ここから 探って中身を見れば、どういうメソッドが存在するのか判ると いう感じ。
という事はImageIOがgcjで使えなかったのを、がんばれば使える様に なったりできるのかしら?

2004/09/24

日付け越え前に帰着。ふぅ。

先日の話で、ふとこういうケースはどうか?と試してみたり。
先日の実験ではリストを保持するインスタンス自体へ参照を失わせる 事を行なったのですが、リストを保持するインスタンスはそのままに、 その中で保持しているリスト自体への参照を失わせる事を試して みました。でも、それでも問題無く、ガベージコレクタは動作しました。
リスト構造のように、実際に参照をトレースしなければ、その存在 可否が判らないようなパターンだと思われますが、うまく機能して いるようです。ふーむ。それにしても、何かしらの細工が必要か、 パフォーマンスに影響が出ると考えられそうですが、そこまでは 調べられず。

眠くて死亡。

2004/09/23

昼頃起床。

リストの続き。なんとなくそれっぽくなった予感。 リストを増やしたり減らしたりしても消費メモリが変わらない所をみると、 ガベージコレクタが動いているようなそうでも無いような。それにしても、 newでオブジェクトを割り当てる事は意識するのに、オブジェクトが使われなく なるのは意識しなくて良いというのが、なんか気持ち悪い感じがしなくも ありません。

続いてC言語でいう所の関数ポインタのリストみたいなのをどうやって 作れば良いか考えてみたり。しばらく唸った結果、 メソッドのオーバーライドと、オブジェクトのキャストを使用すれば 良さそうな感じでそうしてみました。

MoveFunc.java:
class MoveFunc {
    int move( CharObject obj ){
     :

というオブジェクトの動きを決めるクラスを基本として、

MoveFunc1.java:
class MoveFunc1 extends MoveFunc {
    int move( CharObject obj ){
     :

てな感じでMoveFunc1に継承し、メソッドmove()をオーバーライドして、 それらを使って

    MoveFunc mflist[]= new MoveFunc[10] ;
    mflist[0]=new MoveFunc() ;
    mflist[1]=new MoveFunc1() ;
       :

てな感じでリストにキャストするって感じ。これで任意の移動メソッドを持つ キャラクタをリストに繋ぐ事で、リストを回しながら移動メソッドを実行するという 一意の手順でキャラクタを動かせるという感じ。

そういや、リストを書いてて、例えばメソッド内などでnewで新たなインスタンスを割り当てる ようなクラスfooを、foo_objとしてインスタンス化したとき、foo_objへの 参照が失われると、foo_obj内で割り当てられた子インスタンス(というのか 判りませんが)も、ガベージコレクトの対象になるのかなぁ?と思ったり。 でも、双方向リスト(前方向にも後方向にも辿れるリスト)の場合は どうなるのか?という疑問が湧いてきました。双方向リストを構成 する場合、リストの先頭を定義して、後ろに繋ぐという形を取ると 思います。


 TOP─┐
      │  ┌────┐    ┌────┐
      └→│List1   ├─→│List2   ├→null
    null←┤        │←─┤        │
          └────┘    └────┘

このような参照構造を組上げたとして、TOPがList1への参照を失ったとしても、 List2からList1への参照が残っているので、List1は必要と言えます。 List1が残っていると、List2への参照が残っているので、List2も必要と なります。つまりTOPがリストへの参照を失った瞬間に、このリストを参照する術 は無くなるのですが、リストの実体は互いに参照しあっている為、 ガベージコレクトの対象になるかどうかは簡単に判らなくなると思います。 これでは、メモリリーク状態に陥っているのでは?と思いました。
という訳でちょこっと実験してみたのですが、メモリリークする事無く、 うまく動作するようです。 推測しますに、インスタンスを生成するクラスでは、上位インスタンスからの リンクが張ってあり、上位インスタンスが参照を失った際には、それが 下位のインスタンスまで伝播する様になってるのではないかと思われます。
でも、盲腸を付けて更に上位のインスタンス(下図のCUR)にリストのシッポを 掴ませて、直上のインスタンスへ参照(TOP)を失わせても、リストは消える 事無くCURから参照できました。

       CUR──────────┐
+-- -- -- -- -- -- -- -- -- --│- -- -- -- --  +
| TOP─┐                     ↓               |
|      │  ┌────┐    ┌────┐        |
|      └→│List1   ├─→│List2   ├→null  |
|    null←┤        │←─┤        │        |
|          └────┘    └────┘        |
+-- -- -- -- -- -- -- -- -- -- -- -- -- -- --  +

単純に上位インスタンスからの参照の有無だけで、実体存在の可否が 決定される訳では無さそうです。良くできてるなぁ。

食事を買いに行くついでにちょっこり本屋に寄ってみたり。 書棚を眺めていると、JavaCC(Javaコンパイラコンパイラ)なるものの 本が置いてあったので、眺めてみるとJava向けの 字句解析器の自動生成プログラム(Cで言う所の yacc/lexのようなもの) の説明書でした。ほう、こういう物も一応存在しているのかと思ったり。

2004/09/22

出張。日付け越え前に帰着。ふぅ。

Javaでリストを書く練習をしてみたり。でも途中でこんがらがって死亡(汗;

2004/09/21

出張。

2004/09/20

昼過ぎ起床。でも、ぐうたら過ごして終了。

ガベージコレクタとポインタ。Javaの文法で、参照型という用語が 出てくるのですが、これって動作的にはC言語の ポインタに似たイメージである事が判ってみたり。 これを踏まえてガベージコレクタの機構がなんとなく想像できる 予感がしたのでメモ。

ガベージコレクタはインタープリタ言語などで実装されている、 未使用となったメモリ領域の回収→再利用機構の事を指すのですが、 恥ずかしながらどういう時にその動作が発生し、それは具体的にどの様に実現 しているものなのか良く判ってませんでした(^^;
C言語ではポインタというメモリ領域の位置(アドレス)を指す 仕様(概念)があります。ポインタは意図した任意のメモリ領域を 指す事ができ、しかも領域を指すのに何の手続きも必要と しません(借金するのに借用書を書かずに借りられるような感じ?)。 従って、malloc()関数などで取得したメモリ領域をとあるポインタ変数に 覚えた後、何も考えずにそのポインタ変数に全く別のメモリ領域を 指し直すような事も、問題無く行えます。 ここで問題になるのは、ポインタを新しいメモリ領域に指し直した時、 元々指されていたメモリ領域からは、その領域がいかなるポインタ変数 からも指されていない、いわゆる未使用状態になっている事を、 知る術が無い点でしょう。唯一未使用状態になった事を知る方法は free()関数で未使用メモリ領域になる事を、明示的に知らせる しかありません(真の意味ではプログラムを作成した本人にしかfree() して良いかどうかは判らない と言えるかも知れませんが)。 何せ、借用書無しにお金(メモリ)を貸している のですから、債務者の自己申告以外に貸したお金を回収する方法が 無いという感じでしょうか。このような状況が意図せずに生まれてしまい、 メモリを確保するばかりで不要になったメモリを一向に返さないという状態が 続く事をメモリリークと呼び、稼動時間の長いプログラムでは思わぬ 動作不具合を起こしてしまう事があります。 他にも、OSがメモリ保護機能を有している場合は、ポインタでいくら自由に メモリ領域を指す事ができても、その領域が保護されていると、 読み書きした途端にSegfaultで死んでしまいます。実際に自分の持ち物で無いものを、 そこにあるからと言って、勝手に使うと捕まるのと同じイメージ でしょうか。
一方、Javaやインタープリタ系の言語にはポインタという概念を 明示的に持ったものはありません(私の知る限りは、ですが(^^;)。 しかしながら、コンピュータという機構の 下で動く以上、メモリをデータの記憶領域として使用する必要が ある為、どの変数をどこに記憶しているかを指す手段はどうしても 必要になります。ここで、メモリから必要な領域を割り当てる時と、 その領域を指さなくなった時を監視する機構があれば、メモリ領域 が指されなくなった時点で、その領域は未使用領域である事が 判定できると思います。あるメモリ領域を指さなくなる契機は 何でしょう?と考えると、メモリ領域を指す変数の代入が挙げられる と思います。ポインタの概念が無い言語仕様で、ポインタに代わる 物は 動的にメモリ領域を確保する事が宣言されたインスタンスや文字変数が 挙げられると思います。これらの変数に代入が行なわれる際、 元々指していたメモリ領域を未使用領域に指定してから、新しい領域を 指し直せば良い事になると思います。
例えば文字列変数を使用して、次の様な操作を仮定してみます。 「$str1="new",$str2=" message",$str3="old message"」とした時、 $str3を「$str3=$str1 + $str2」として、"new message"という文字列 に置きかえてみる事を考えてみます。

  1. $str1が指している領域と$str2が指している領域のサイズを足した新たな領域を確保する。
  2. (1)で確保した新たな領域に連結式に従って"new message"という文字列データを作成する。
  3. $str3が元々指していたメモリ領域を無効化する。
  4. $str3を(2)の領域を指す様に変更する。

こんな感じ。(3)で生じた無効化されたメモリ領域はガベージコレクトの対象と なり、またいつか再利用されるという感じ。
これらを踏まえると、C言語では、そもそも実際には実装されていないメモリ領域を ポインタで指す事が可能だったり、メモリ領域がポインタ で指されているかどうか(つまり使用されているかどうか)を自動的に知る術が無い点で、 ガベージコレクタを原理的に実装できる言語仕様にはなっていないものと思います。 勿論、多くのインタープリタ言語がC言語で実装されていたりする事を 考えると、プログラミング時のコーディング規約として色々な制限を 設けて関数を実装すれば、C言語でもガベージコレクタを実装する事が 可能ではあると思われます。
ガベージコレクト可能な言語仕様を考えてみますに、次のような条件が 必要かと思われます。

  1. 任意のメモリ領域を指す手段を設けない。(C言語でいう所の str='&'foo とか、char *str=0xnnnnnnnnとか)
  2. 異なる参照型(クラス)からのキャストを許さない。

こんな所?(1)はJavaでいう所のnewでしかメモリ領域を確保させないと いう感じでしょうか。Perlなんかの場合はプリミティブ型はどの変数 でも保持していて、文字列型の代入が自動的にこれに当たるものと 推測します。(2)はJavaなんかではそれ自体が文法として チェックされていると思います。
逆に、こういう制限を設けてしまうと、都合が悪い場合もあると 思います。主に、ハード構成に依存したプログラムを書く場合などが それに当たると思います。しかしながら、OSやデバイスドライバは ハードに依存するのは当然のソフトウェアな訳ですから、 そういった特殊な条件下でも、意図した動作を記述できない場合は、その言語では、 そのソフトウェアを作成できないという事が言えると思います。

ガベージコレクタを持った言語の利点としてよく挙げられる点に、「メモリリークが 発生しない」という点があります。 また、メモリリークが発生する理由として、単純に「free()のし忘れ」と いうのが挙げられると思います。まぁ、確かにそうなのですが、 何故free()(もしくは開放関数)の実行し忘れが生じるかという話をした時、 自動的にメモリのやりくりを行なってくれる言語を使用した経験があってから、 C言語を使用したというのが考えられないかなぁと思いました。 例えば、C言語は文字列処理が苦手とされていますが、言語仕様として 文字列変数は定義されていませんので、厳密に言うと、言語自体は文字列処理が 苦手なのではなく「出来ない」が正しく、その人自身が実は正確な意味での 文字列処理プログラミングが苦手だった事が発覚したと言えるのじゃないかと思います。 先に述べた文字列の置き換えを例に挙げると、それまでの言語だと 「文字列変数のコンカチと代入でいいじゃん」だったのが、C言語で表現しようと 思うと、「何故か面倒」と感じるのではないかと思います。「置きかえる」と いうメモリ領域操作を正確に意識する必要が無かったのですから、 使われなくなる古い領域を明示的に「いりません」と言う必要があるなんて 概念も無い訳です。 後は、人間の性格による所も あるかも知れません。「自分が思っている以上に、不要になった物への興味は失われる」(笑。 ガベージコレクタを「ゴミ収集」の意味でそのまま使用してみると、 「新しい物を買ったので、古いものはゴミに出す。 ゴミ捨て場に置いとけば持っていってくれる。でも、そのゴミが どの様に処理されるのかは よく知らない。」、 もっとヒドいと、「ゴミ箱に入れとけば定期的に無くなる」なんて感じ。 これを踏まえて考えてみますに、新しい物に置き換えたという事は、置きかえられた古いものは 大抵の場合、暗黙のうちに不要と解釈して問題無いと いう事が、「メモリリークが発生しない」という解釈に置きかえられている のかなぁと思ったりします。稀に捨ててはいけないものを捨ててしまう事が あるかも知れませんが、 それは大抵気付くので問題無いという感じ。そう考えると、ガベージコレクタは 人間のいいかげんな部分をうまく補っているのかなぁなどと思ったりします(^^;

そう言えば、その昔、N88-BASICの音楽演奏のプログラムの紹介の中で、 「ガベージコレクタが動く為、その時一瞬演奏が停止する」というような コメントを見た覚えがあります。その時、データは演奏に全て必要 なハズだから、ガベージコレクトの対象になる部分なんて無いと思う のになんで?という疑問を抱いた事がありました。 古いデータから順にガベージコレクトの対象になるのだという判るような 判らないような説明を聞いた事もあるのですが、「古いか新しいか」と 「使っているか使っていないか」とは関係無いような?という疑問には 誰も答えてくれなかったような気がしますが(^^; さておき、 今、これまでに書いた事を踏まえてその時の動作を想像すると、次の ような事だったのじゃなかろうかと思います。 N88-BASICで書かれた音楽演奏プログラムは、誰が決めたという訳ではなく、 なんとなく決まったスタイルとして、演奏チャンネル毎に $A1〜$An,$B1〜$Bn,... といった文字列変数に音楽データとなる文字列を入れておいて、 最後にPLAY文で、順にplay [$A1,$B1,..]; play [$A2,$B2,...]; .... と演奏するという 感じになっていました。 いちいち変数に代入するのは見やすさの点もあると思うのですが、 当時のBASICでは論理的な一行に書ける文字数が決まっている為、 複数チャンネルの演奏データをPLAY文に直接べったり書くのは無理が ある(あるチャンネルは4拍子全音符、他は32分音符でとかいう場合など) という理由もあったのじゃないかと思います。 この時、PLAY文には演奏データを収めた文字列変数を指定するというのは 文法的に決まっていることなので、単純に文字列変数を指定するだけでは ガベージコレクトの対象になるような未使用メモリ領域が発生する事は ありません。しかし、例えば同じフレーズを繰り返すのに、文字列変数の 連結により表現した際、直接PLAY文に文字列連結式を書いてしまうと、 ガベージコレクトの対象となる未使用領域が発生するものと推測します。

  1. $A="c4d4e4f4" で4拍子のドレミファを演奏する演奏データを格納する。
  2. play $A+$A+$A+$A で4拍子のドレミファを×4回続けるとした時、
  3. 一時領域(仮に$TMPとする)として $TMP=$A+$A+$A+$A という領域を生成し、
  4. 実際の演奏には play $TMP を実行する。
  5. play文を通過すると、$TMPは参照しなくなるので、未使用領域にする。

こんな感じ。毎回$TMPに相当する領域を切り売りしていると、無限ループで 演奏させた場合、そのうちメモリプールが無くなってしまうので、 ガベージコレクタが動作する必要がある。という事だったんじゃぁなかろうかと 推測しました。それでも、$ATMP=$A+$A+$A+$A として、変数$ATMPに "c4d4e4f4c4d4e4f4c4d4e4f4c4d4e4f4"をつなぎとめて、 play $ATMP とすると、未使用領域になる事は ありませんので、(アルゴリズム次第ですが)ガベージコレクトも発生しない (発生してもすぐ終る)ものと思われます。
これ考えていて、例えば print "$a $b" みたいな事をする時も、 "$a $b" を展開した一時領域は printが終了した時に無効化する必要 があるのかもと思ったり。

それぞれのプログラム言語には歴史があると思います。メモリが極めて少なく、 OSの代わりにBASICが基本ソフトウェアだった大昔に、例えば C言語のようなアセンブラと代わらない手段を用意するよりは、 BASICの方が安全にプログラムを動かす事ができ、少ないメモリを 自動でやりくりするという機構の方が、ハードリソースを最大限生かす という意味でもニーズに合っていたのではないかと 思います。 コンパイラが動作する程度にメモリが安く多く実装できるように なり、OSによるメモリ保護がサポートされているのならば、 C言語で速度優先みたいなのもアリになったのではないかと思います。 そして、何もせずとも速度が得られる今の時代には、再びメモリのやりくりに あまり手のかからないような言語仕様がニーズにあっている のかも知れません。なんて事を思ったり思わなかったり(<どっちや)。

2004/09/19

昼過ぎ起床。

ppcsim 0.96bをリリースしてみました。御参考まで。

彩コアモジュール三版で、描画ツール類のパラメータをいじり過ぎて 元の値が判らなくなってしまったので、レジストリエディタを使って レジストリキーを削除し、設定を初期値に戻してみたり。 アレ?水性ペンと筆ツールって同じパラメータでしたっけ?

姉チャン。ひたすらゾンビ切り。フラグを立てる条件が、あるブロックの ゾンビ殲滅というのが多いので結構面倒なのですが、バイオハザードみたく 恐くない のが、TANE的には丁度良いです(あのタイトルで恐いのを期待する奴ぁいない でしょうが)。難点はと言えば、ストーリーモードの セーブポイントは面クリア毎なのですが、一面当たりのクリア時間が 結構かかったりするものですから、セーブ機能は付いているのに 本体電源入れっぱなしという何だか訳わからない状況になってます。

切って切って最終面入り口で(自分の肩が)力尽きて死亡。

2004/09/18

ケロロ軍曹をやってる時間に起床。健康的(そうか?)

久しぶりに神保町をウロウロ。でも、特にめぼしいものは見つからず。

binutils-2.15対応という事で、ELFファイルのロードルーチンを見直して みたり。セクション名を見ずに、.text領域は固定にしてあったのですが、 単純にセクション名の引き方がよく判っていなかっただけだったりします。 そんな感じで真面目に見直した所、なんとなくセクション名を引く方法が 判ったので、.textという名のセクションで始まる領域のメモリ配置アドレス の先頭を実行開始アドレスとする様に変更してみました。 そんな感じで、配置が変わっても追従するようにできました(^^)v。

ppcsimの使用方法のページも少し古くなってて、微妙に使い方が変わっていたり する点もあったので、「ppcsimの使い方」と「ppcsimインターナル」を 更新してみました。更新時に実際にppcsimで動かしながら、表示が変わった所 などを切り貼りしていたのですが、その途中でppcsim-0.95で表示がバグって いるところを発見したり色々(^^; まだ直しきれていない部分がありますが、 取りあえずの参考まで。

binutils-2.15 + gcc-3.4.2の組合わせで再度povrayのppcクロスコンパイル を行ない、ppcsimでの実行確認を行なってみたのですが、前回3.4.1に対して 0.3%程度実行命令数が増えてしまうという結果だったのですが、 今回はそんなにムチャクチャになりませんでした。システムコールの 実行回数が何故か26463回も実行されているという謎の結果だったのですが、 今回は3582回と増えてはいるものの、変というほど増えていませんでした。 むー、何故だ?
そんな訳で、以前の実行結果と今回の実行結果を見比べてみる事に。 すると、システムコールllseek()の戻り値が以前実行したものと異なっている のを発見。今回のはllseek()のエミュレート(Cygwinネイティブでlseek()に 置き換え)の戻り値がエラーしている模様。何故?試しに、

   ret = lseek( get_phyfd(mres,(int)mres->GPR[3].rl),
		(off_t)mres->GPR[5].rl,
		(int)mres->GPR[7].rl ) ;
から、
   ret = lseek( get_phyfd(mres,(int)mres->GPR[3].rl),
		(off_t)((int)mres->GPR[5].rl),
		(int)mres->GPR[7].rl ) ;

に書き換えてみた所、以前と同じ戻り値が得られる様になりました。 実際にoffsetは負の値が入っていたのですが、 これで動くという事は、off_tが以前はintだったのが、longに なってて、unsigned int 型であるmres->GPR[5].rlをlongにキャストした時に 符号拡張されなかった為、負の値のつもりが巨大な正の値に見えてしまい、 結果lseek()の実行に失敗してしまうという感じ。それにしても、 lseek()が失敗した状態でも、シミュ実行はそれなりに終るというのが 不思議なのですが(^^; ついでにllseek()の戻り値の返し方がデタラメなのを発見したりでへっこり。 で、そこを直したらシステムコールの実行回数が同じになり、 gcc-3.4.1とgcc-3.4.2の性能差も0.00006%悪化という感じで、まぁ無視できる レベルに収まりました。最初から良く確認するべきでした。 そんな訳で新版をリリースした途端にバグを発見しまくりでダメダメ(T_T)。

何気に買ってみたSIMPLE2000シリーズの「THE お姉チャンバラ」に 色んな意味でハマってみたり。

2004/09/17

出張。帰着。ふぅ。

Cygwinパッケージをダウンロードしたり、Webをちょろっと巡回したら眠くて死亡。

2004/09/16

出張。

2004/09/15

出張。

2004/09/14

出張。

2004/09/13

出張。

2004/09/12

明け方までSAIを使ってて、寝て起きたら午後もいい時間(汗;

寝る前にgcc-3.4.2の再ビルドを仕掛けていたのですが、エラーで終了してたり。 一応libstdc++のconfigureは通っているみたいですが、前のゴミが残ってて うまくいかなかったのかと思い、libstdc++ディレクトリ下を手でファイル削除 して再度ビルドしたり。今度はOK。ふう。それにしても、前に3.4.1がどうやって ビルドできたのかが判らないままなのですが、binutilsのバージョンに依存する 場合もあるという事みたい。それにしてもやっぱり謎。

そんな訳で、povray-3.5bをPCCクロスビルドしppcsimで実行確認。何故か UnknownInstructionでシミュ実行失敗。スタートアドレスを見ると、 Linuxバイナリは0x10000100を固定でスタートアドレスとしていたのですが、 それがずれている模様。スタートアドレスを変更して実行しようとした所、 LinuxSysCallのエミュレーションモードではppcsimオプションによる 変更ができないというバグを発見したり(^^; ちょろっといじって実行したら、 やはりその先でUnknownInstruction。どうやらDLLによるライブラリ読み込み がONになっているらしく、先日の--as-neededオプションが関係しているのかと ldのmanページを見てみると、やはりDLLに関連するオプションらしい事が 判りました。で、gccのconfigure実行を確認した所、どうやら--disable-shared 指定が抜けいたようで、この為、sharedライブラリの使用可能という事で それ用のバイナリが生成されるようなビルド(なので、ldの--as-needed が必要だった)になっていたというのが原因の様です。という訳で、 謎は完全解明。てゆーか、ヲレのせい?てへっ [はーと]
逆に、sharedライブラリを有効にする場合はbinutilsの2.15 を使用しなくてはならないと言う事になりそうです。
で、また、gccを--disable-shared指定ありでビルドしなおすこと数時間、 それでpovrayをビルドしたのですが、やはりスタートアドレスが変わっている 模様。これはbinutils-2.15によるものだろうという事でbinutils-2.14に 戻して、再度povrayをビルドし直して、どうやらスタートアドレスが 今まで通りになりました。しかしながら、binutils-2.15を使用すると、 ppcsim実行がうまくいかないというのは問題です。スタートアドレスの 取得方法を見直す必要がありそうです。
そんな訳で、povray実行で確認したのですが、何故かシステムコール実行が 3.4.1に比べて異常に多くなってたり(3.4.1まで3334回だったのが 3.4.2では26463回実行されている)、命令数で0.3%ばかり増えていたり (3.4.0→3.4.1の時は0.00004%程度の悪化でした)で、 マイナーバージョンアップの割には思いっきりレベルダウンしている予感が します。なんとなくlibstdc++の影響が大きい予感がしますが、本当の 所はよく判らず。

因みに、cygwinの方は、gccビルドを何度も行なってましたが、ハングる事も 不穏な動きも無く安定動作してたので、当面はこれで使ってみるという事で。

そんな訳でSAIコアモジュールテスト3版レポート。 全ツールについて、筆圧感度が調節できる様になってます。 150%程度で使用したのですが、スタイラスを握る手が疲れる事が無く、 非常に快適でした(^^)。他、筆ツールが追加となってます。 パラメータが多く、色の出具合を確認するのに少し手間がかかりましたが、 TANE的には結構好きな味が出ていると思います。色々変えてなんとなく は判った気にはなってますが、パラメータがそれぞれ、どこにどのように 効くのかが判れば、もう少し出る色のイメージを掴みやすいかもと思いました。
一点気になったのが引きずりの挙動について。引きずり自体のイメージは 掴めるのですが、実際に使用してみると、コントロールが難しい様に感じました。 その理由を考えてみたのですが、引きずりの元になる色に透明度の値が 入っていない為、例えば、赤(RGB:FF0000)を筆圧を弱めて塗り、 薄い赤になった下地を作り、それを引きずると、薄い赤(RGB:FF8080とか)と現在色が 混ざるように直感的には思うのですが、実際には透明度成分の無い元の赤(RGB:FF0000) と混ざった結果になるため、それが、見た色から引きずりを想像した結果と 直感的に結び付かないという点が、コントロールの難しさに感じる のではないかと推測します。

[筆ツール引きずり]

そういや、筆ツールの「筆圧→描画濃度」のチェックを外しても、描画濃度が 筆圧に反応している様に思いました。怪しげに思ったのはその辺だけでした。

そんな訳でラクガキ。何も考えずに描いたら、途中でどうしたいのか よくわかんなくなったというヒドい感じに。今回は筆をメインで使って みたのですが、結果的に引きずりとかをうまく使った感じにはなってないです(^^;

[何が何やら]

午後の良い時間に、セロというマジシャンの出ている京都めぐりの番組を やってました。 以前たまたまTVで見た事があった のですが、かなり凄くて驚いたのを覚えていたので、思わず見てしまったり。 見ていて何がスゴイのかと考えたのですが、ストリートで次から次へと 物を出すマジックを行なったり、相当な至近距離で起こっているけど全然判らないと いうのが、その凄さなのかなぁと思いました。いやー、ビックリ。

何気にハマりこんでいる週間少年ジャンプで連載中の「DEATH NOTE」。 単行本でしか読んでないのですが、なんていうか、こう、変なドキドキ感 に病みつきです。それが3巻目に来て「むきーっ!こう来たかーーっ!」 という感じになり、更に、条件が加わりそうな続きがすごく気になってます。 因みに、L視点で見るか、キラ視点で見るかで、その人の性格が判るような 気がしなくもありませんが、今の所TANEはキラ視点です。もう、いつ バレるかヒヤヒヤしながら読んでる感じです。L視点方はまだ名前がバレて いない分、ヒヤヒヤ感はありませんが、4巻目以降で出てくるミサという 死神の目を持った女が出てくると、L視点もヒヤヒヤ感が上がりそうな 予感がしています。

2004/09/11

朝普通に起きたと思ったら、二度寝して昼過ぎ起床(汗;

先日ビルドしたスナップショットをインストールし、更に 自分ビルドをしてみたり。取りあえず fork()でIDLE状態に 陥り、ハング状態になるという感じにはなりませんでした。 何も変わって無いので、今回はたまたまOKだったという 話があるのかも知れませんが、取りあえずこれで様子をみてみよう と思う所。

gcc-3.4.2が出ていたので、ダウンロードしてみたり。 差分の一覧を見る限りでは、PPC32に対する変更は加えられていない ので、3.4.1のバイナリと同じ結果が得られそうですが、 cygwinのテストも兼ねて(libstdc++ライブラリは何故かfork()の テストに効くのです)、ビルドしてみる事に。でもlibstdc++のconfigure実行に 失敗。ただ、今回はcygwinの不具合起因ではなく、純粋にconfigure実行での 不具合起因による模様。

checking for stdint.h... (cached) yes
checking for main in -lm... configure: error: Link tests are not allowed after GCC_NO_EXECUTABLES.
make: *** [configure-target-libstdc++-v3] Error 1


原因を追跡してみた所、configureの前述周辺でのテストに失敗した 訳ではなく、テストを行なう前の使用できるgccに問題があるようで、 そのgccの実行判定部分を見てみると、以下のような実行結果になって ました。

configure:2353:  /cygdrive/c/tane/develop/tooltest/gcc-3.4.2/build/gcc/xgcc -B\
/cygdrive/c/tane/develop/tooltest/gcc-3.4.2/build/gcc/ -B/usr/local/ppc/powerp\
c-linux/bin/ -B/usr/local/ppc/powerpc-linux/lib/ -isystem /usr/local/ppc/powerpc\
-linux/include -isystem /usr/local/ppc/powerpc-linux/sys-include -o conftest -O2\
 -g -O2 -O2 -g -O2  conftest.c  >&5
/usr/local/ppc/powerpc-linux/bin/ld: unrecognized option '--as-needed'
/usr/local/ppc/powerpc-linux/bin/ld: use the --help option for usage information
collect2: ld returned 1 exit status


という感じでリンカのオプション解釈にエラーがあったため、 実行可能なgccでは無いと判断され、結果その先でバイナリ生成不可能gccと チェックされて、configure実行に失敗するという感じ。 以前ビルドに成功した3.4.1でも、念のため再度ビルドし直してみると、同じく configureでエラーとなった為、3.4.1を入れた時に反応が変わったのかとも思った のですが、でも、configure内で実行しているのはビルド内でコンパイルした xgccだし、前のバージョンが関係するとは考えにくい予感。 binutilsも2.14から変えていないので関係無さげだとは思ったの ですが、binutilsを2.14から2.15に入れ替えてみて、 --as-neededなるオプションを解釈できるのか試してみる事に。 どうやら、2.15のbinutilsを入れると、--as-neededを付けてもエラー しない模様。そして、再度ビルドを行なってみると、エラーせずに通過しました。 うーむ、以前ビルドした時には、確かに3.4.1のビルドはできたのに (その時の残骸を成功例として今回の失敗と見比べたから)、なんでだ? で、しばらく動いて一応エラーなく終了。でも、ビルド完了したのは 確認用の3.4.1......がっくり。

掲示板にてしんさんより Antというツールの存在を教えていただきました。欠点の無いmakeと いう事だったので、期待して調べたのですが、TANE的にはちょっと微妙な 感じのするツールというのが、今の所の印象です。 まず、makeの欠点として挙げられている点が、文法的な部分だけだったり する為、makeで原理的に不可能だった点をカバーしているという訳では 無いというのが、微妙な点の一点。利点として挙げられている「Ant自身が Javaで書かれているからプラットホームの依存性が低い」という点も、 冷静に考えると個人的には微妙な点に入ります。確かに、ファイルコピーや コンパイラの起動が、Antのビルトインコマンドとして実行可能な為、Antの実行下 (即ちJava環境の実行下)では、プラットホームに依存しなくなるという点 は確かにメリットかも知れません。しかしながら、実際の所、 ソースをオープンにしないような条件下であると、プラットホームの依存性 というのは、あまり問題にならないような気がします。ただ、UNIX系OSでない Windows環境では、標準で使用できるシェルコマンドがあまりにも貧弱なので、 makeが移植された所で、それで十分とは一概には言えません。 そういう意味で、Windows環境下という条件の下で見た時に、 「UNIX系OSのmakeに匹敵するツールがWindowsでも使用できる様になった」 というのが、感覚としては正確なのではないかと思います。
build依存ファイルの記述にXMLを使用するのは悪くない選択だと 思うのですが、文法上XMLって閉じタグをきちんと書かなくてはならないので、 手で書く場合、その手のエディタを使わないと、変に長い名前のタグだと 閉じタグを書くのが面倒臭い感じになる様に思いました。
そうは言っても、Antを実際に使ってみた訳では無いので、使ってみると また感想が変わるかも知れませんが。

所で、Makefile中でコマンド列の始まりはファイルの行頭から「タブ文字」で 区切る必要がありますが、この点、確かにハマるケースが多いみたいです。 あまり深く考えた事が無かったのですが、何故コマンド列の先頭をタブ文字で 記述する必要があったのかを考えてみました。
例えばコマンドラインがせいぜい40文字程度で記述できる場合は、大抵の テキストエディット環境では、一行に収まると思われます。ですから、 「行頭から1文字以上の空白文字および1文字以上のタブ文字の場合はコマンド行を示す」 と解釈しても問題無いと思われます。ここを指して「何故空白で 区切るのがダメなのだ?」というのが、ハマった場合に疑問に思う点なのかも 知れません。ここで、一行が非常に長い上、コマンドライン中に空白を含む 文字列を手で記述する必要がある場合を考えてみます(例えばソースの前加工 にperlやawkのベタスクリプトを書くような特殊ケースです)。この時、

print "                                      \n" ;


みたいなのを複数行にまたいで書くという、如何にもハマリそうな事を行なった場合、 以下のような記述になると思います。表示の都合上、タブ文字を'--------' で示します。

--------perl -e "print \"        \
--------        .\n\";"


この時、先ほどのルール 「行頭から一文字以上の空白文字およびタブ文字だったらコマンド列」を 適用すると、2行目の空白はきっと削られてしまうでしょう。 つまり、「行頭に一文字のタブがあればコマンドライン」 (perl的に書くと /^\t(\s*|\S)$/ で、$1がコマンドライン文字列といった所でしょうか?) とする事で、書いた通りのコマンドラインがそのままシェルに渡せる という事になるのじゃないかと想像します。 結果的に、単語では無いのでタイプミスする事が無く、 見た目があまりうざく無い上、インデントされているからコマンド部分が見た目に 判りやすく、タブキー一発で入力も簡単、プログラムでのパース(手書きパーサー とかだとやはり重要かも)も簡単になる、という点で、会心の文法アイデア だったのかも知れませんが、普通のエディタや紙に印刷した時、空白と区別が 付かないという罠があった......という感じなのでしょうか? 本当かどうかは判りませんが(^^;

所で、先ほどのperlのベタスクリプトをMakefileに書いて、実際にどう動くか 確認してみました。便宜上タブを--------で表現しています。

tmp> make -v
GNU Make 3.80
Copyright (C) 2002  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
tmp> cat Makefile 
SHELL=/bin/sh

aaa : a.v
--------perl -e "print \"       A\
--------      a    .\n\";"


空白を'='として表現すると、期待値としては「=======A======a====.」と表示 されるハズですが、実際に実行してみると、

tmp> touch a.v
tmp> make aaa
perl -e "print \"       A\
   a    .\n\";"
       A a    .


で、「=======A=a====.」と、継続した行の6文字の空白が1文字の空白に圧縮 されています(^^; てか、そうなるんだったら、 行頭はタブ文字じゃ無くてもイイじゃん! と思わずツッコミ入れてしまいました(^^;
でも、冷静にもう一度考えてみますに、Makefile内では変数を使用する事が できます。変数への代入はどこで行なっても良いので、やはり、コマンド行部分と Makefileの文として解釈しなくてはならない部分の区別は必要な為、先頭から 空白文字で区切るという方法は無いという事になりそうです。 また、継続行について考えてみると、 最初の行でコマンドライン部分の開始が判っており、その状態から継続行がある 事を示すバックスラッシュ(もしくは¥マーク表示)を入れる訳ですから、 継続行から見れば、コマンド行部分である事は先頭にタブ文字が無くても 自明という事になるでしょう。それを踏まえると、継続行であると判断できる場合は、 行頭からの タブ文字削除と空白文字の1文字圧縮は必要無くても 良いと思うのですが、この点は拡大解釈という気がしなくもありません。

2004/09/10

出張。帰着。ふぅ。

Cygwinアップデート。cygwin1.dllのバージョンが上がっていたり、色々 ありますが、win32apiが3.1になっていたので入れてみました。 これの一つ前のバージョンでは、OpenGL関連の#define定義が、更に 一つ前のバージョンよりも削られてしまってて、Nurbs関連の基本的 なプログラムがコンパイルできないという罠がありました (参考)。 今回入れた3.1では取りあえず、HairMakerのコンパイルはOKそう。 もっと突っ込んだ使い方をするとどうなのかは判りませんが、 TANEが現状使っている範囲内ではOKそうな予感がします。

SAIの新版が出てたり。 筆ツールの実装など、かなり多くの機能が追加実装されていますよ。 パラメータの調節項目が多くなっているので、パラメータの感触を 掴んでいるところです。

で、cygwin DLLを1.5.11-1にしてみたのですが、fork()性能問題 が残ったままなので、一旦古いのに戻したり。 先日スナップショットをコンパイルした 時は、自分コンパイルができなかったのですが、取りあえず 先日のものより新しいのが出ていた(fork.ccに変更は無いのですが) ので、fork()性能問題回避パッチだけをあてて、ビルドを仕掛けてみたり。

とかやっているうちに眠くて死亡。

2004/09/09

出張。

2004/09/08

出張。

2004/09/07

出張。

2004/09/06

出張。

2004/09/05

昼過ぎ起床。

そんな感じで表示だけはこんな感じになってみました。 読み込みにちょっと時間がかかる(ISDNで10秒くらい)かも知れません(^^;

テスト

何気に、先日消えていると言ったα値もちゃんと残っているようで、 結局、何が問題だったのかよく判らなくなってしまってますが、 取りあえず結果オーライという事にしてしまおう。

そういや、Javaアップレットのソースを複数のファイルに分けて、makeで ビルドする事を考えた時、javaソースファイルの中に複数のクラスを 記述すると、コンパイル後はクラス名毎に.classファイルが生成されます。 JAVAではクラス名とファイル名との関係が非常に強い(というか無視できない) ので、makeを使う上では一見 makeのルールに厳格に沿っていそうですが、 例えば一つのfoo.javaというソースから、foo.classとfoosub.classが生成 される場合、foosub.classを消してしまっても、foo.javaを再コンパイル する必要があるという指示ができません。そんな感じでなんか微妙。 まぁ、ファイル名とクラス名が一対一になっていれば、クラス検索などが ファイル内をシークする必要が無い分、高速にできそうなのは容易に 想像できるので、それはそれでメリットはある所なのでしょうが。
また、アップレットに限って言えば、生成されるのは.classファイルなので、 例えば複数の.classファイルが必要な場合でも、アップレットとして 直接実行するクラスファイルを生成物の頂点にしてしまうと、そのクラス を含むソースをコンパイルすればOKな様に見えてしまいます。 なもんですから、必要な全てのクラスをコンパイルする事で得られる 実行ファイルに代わるものをフラグファイルとして用意し、 そのフラグファイルよりも新しいソースはコンパイルするというような 細工を行なう必要がありました。

後、#defineのようなプリプロセッサ処理を使ったり enumを使うといった、 定数に名前を付ける手段(というか習慣)が無さげなのですが、これぁ クラスの中に変数を定数型であると自分で決めて使うから必要が無いと いう感じなのでしょうか。どうにも定数をそのままソース中に書く サンプルが多くて(言語仕様に無いのではそうもなるでしょうが)、 ちょっと変えたいだけなのに、そこら中を良く見て変えなきゃいかんというのに、 これ本当か?と思う所があります。

昨日、今日と、地震で揺れる揺れる。震源地は関西方面のようなのですが。

2004/09/04

昼過ぎ起床。

先日の続き。stubをコピーできるか試してみました。でも、コンパイルは 通るものの、実行するとアクセス違反になるみたい。

     1  import java.applet.Applet;
     2  import java.applet.AppletStub;
     3  import java.awt.*;
     4  import java.awt.image.*;
     5  
     6  class ChipChar extends Applet {
     7      int    x ;
     8      int    y ;
     9      Image  img ;
    10      private transient AppletStub stub;
    11  
    12      int ReadImage(String fname){
    13          this.img = getImage(getCodeBase() , fname);
    14          return(0) ;
    15      }
    16  
    17      ChipChar(int x, int y, AppletStub stub){
    18          this.stub=stub ;
    19          this.x   =x ;
    20          this.y   =y ;
    21      }
    22  }
    23  
    24  public class test extends Applet implements Runnable {
    25      ChipChar   pcg ;
    26      
    27      public void init() {
    28          pcg = new ChipChar(0,0,stub) ;
<以下略>    


実行結果は以下のような感じ。

java_test> /c/j2sdk1.4.2_05/bin/appletviewer.exe test.html
java.lang.IllegalAccessError: tried to access field java.applet.Applet.stub from class test
        at test.init(test.java:28)
        at sun.applet.AppletPanel.run(AppletPanel.java:353)
        at java.lang.Thread.run(Thread.java:534)

そもそもstubをアクセスしちゃいかんという事らしい。んー、先日調べたAppletクラスの ソースを見ても、transientなる謎の修飾子(単語の意味は「一時的な」ということ らしいですが)が付いているし、何がなんだか。

TVなど見ながらぐうたら過ごしたり。バク天の「ぱらぱら日本昔話」。 先週の六法全書にぱらぱらマンガ描いてみましたを受けて、ぱらぱらマンガで 日本昔話を表現しようって事なのですが、早速の投稿がものすごくレベルが 高くて驚いてみたり。
ミラジョボビッジ主演のバイオハザード。犬を飛び蹴りするところ以外は、 案外アクション少なくてアレ?っと思ったり。てゆーか、アクション映画 ではないので、そういう所に期待してはいけない予感もしますが(^^;

今更ながらエスプガルーダのアレンジモードにハマり中。 敵弾量が異常に多いアレンジモードですが、逆に、 覚醒時の金塊化が爽快になっている様に思います。また、異常に多い敵弾量な故に、 覚醒の金塊化による弾消しが重要になっているので、覚醒カウンタの 貯めどころと使いどころの配分をある程度考える必要があり、 何気に面白い感じになっていると思います。とは言っても、所詮は ヘボなので、2面ボスを倒せないというレベルなのですが、 一回辺りのプレイ時間が短くて一人スコアアタックな感じが、 適度な時間潰しになってて良いです。

何気にCygwinのスナップショットをチェックしてみると、 以前調査したプロセス消滅問題 が修正されていそうな予感がするので、ダウンロード& 取りあえずfork()性能問題回避のパッチだけを当ててビルド。 そしてDLLを置き換えた後、スナップショットを再ビルドしてみた所、 取りあえずconfigureでずっこけるような事は無さげ。
と思っていたら、途中でfork()がエラーしてそのままプロセスがIDLE状態 になり、プログラムの強制終了で一つずつプロセスを殺していったの ですが、そのうち画面が固まってしまい死亡。やっぱダメでした。むぅ。

Javaアップレット。描画がちらつくのでダブルバッファについて資料を 探ってみたのですが、どこを探しても同じようなサンプルしかなくて、 ちょっと違うのですが...と悩んでみたり。ふと、応用の範囲内ですか? と思い直して、考え直して、あぁそうかと判った所で眠くて死亡。

2004/09/03

出張。帰着。ふぅ。

先日のJavaコードについて、 しんさんより助言をいただきました。 しかし、まるで呪文を唱えられているような感じで、個々の単語の意味が 全く判らないというダメっぷりの為、用語の習得から始めてみました(^^;
という訳で、実験コードを書きながら動きを確かめてみた所で、 おおよそ次の様に理解してみました。

まず、getImage()やgetCodeBase()といったメソッドはAppletクラスの中で 定義されており、Appletクラスの中で定義されているメソッドのそれと同じ ものを指すつもりであれば、Appletクラスを継承する必要がある (継承しないとローカルスコープ内にgetCodeBase()というメソッドなど 無いという感じのコンパイルエラーになる)。所が、先日のような書き方を すると、何故かgetCodeBase()を実行するとNullポインタ例外となりました。
そこで、gccのソースの中に含まれるAppletクラスのソース (gcc-3.4.1/libjava/java/applet/Applet.java)を眺めてみると、次のような 感じになってました。

public class Applet extends Panel
{
  <中略>
  /** The applet stub for this applet. */
  private transient AppletStub stub;
  
  <中略>
  public URL getCodeBase()
  {
    return stub.getCodeBase();
  }
  
  <以下略>

Appletクラス内のメソッドgetCodeBase()の実体は、 private修飾子で割り当てられた AppletStubクラスであるところの オブジェクトstub のメソッドgetCodeBase()を実行する事 という感じ。
これらを踏まえて、Nullポインタ例外の発生プロセスを考えてみますに、 クラスChipCharでは、Appletクラスを継承しているのですから、ローカル スコープ内でgetCodeBase()を実行する事自体は可能なのですが、 Appletクラスを継承しただけでは、AppletStubクラスのオブジェクトとして 生成されたオブジェクトstubの実体が存在しない(存在しないというよりは、 触っても良い安全な実体では無いという方が正しい?)為、そこんところを触って しまって落っこちる.....という感じなのでしょうか?

ここから先の話は、まだ想像の範囲なのですが、先日の記述の様に、 AppletクラスのメソッドをChipCharクラスのローカルスコープ内で 機能できる様にするには、 「ChipCharクラスの中にAppletStubクラスのオブジェクトを持ち、 そこにコンストラクタか初期化メソッドを使って、stubのコピーを保持する?」 くらいしか思いつきません。でも、コピーで保持してしまうと、 オブジェクトstubに非同期な内容書き換えが発生した時、 同期化する術が無いのが問題になりそうな予感がします。
結局、Appletクラスのメソッドは継承する事を考えない 方が良さそうという気がしてきました。

2004/09/02

出張。

2004/09/01

もう一日だけ休業。


TOP PREV