僕は友達が少ない3巻 感想

小鳩がかわいくて生きるのがつらい。


※ネタバレとか

隣人部のみんなは相変わらず来ることのない本番へ向けての予行演習という意味で夏を満喫しています。プールしかり花火しかり夏祭りしかり。
自分に友達ができリア充になったときのためという前提のようですが、同じ部活の人間だって友達だしすでに彼らはリア充なのではないでしょうか。


高校時代、部活で一緒にすごした人間とは一生付き合いがあると高校のころ先生に言われました。まさにそのとおりだなと感じているところですが、隣人部の面々もかけがえのない友達だと思います。


そろそろお互いを友達だと認めて高校生活を素直に満喫すればいいと思います。


さて、1巻の最後の伏線。2巻ではスルーされましたが、ようやく最後の最後で・・・!
さて4巻が楽しみです。

2つのテキストファイルの指定行以降を比較する

@tail -n +5 text1.txt > tmp
@tail -n +5 text2.txt | cmp - tmp
@if [ $$? != 0 ]; then \
    : なんかやる ;\
fi
@rm -fv tmp


tail -n +5 file
これでファイルの指定行以降を表示する。この場合は5行目以降


text1.txtの指定行以降をテンポラリに書き出し、text2.txtはパイプで渡す。
cmpの戻り値をみて何かしらの処理を行う。
最後にテンポラリファイルを削除する。

JavaCCとJJTreeを使ってjavaコードをC++へ変換 その2

第1回目


前回のエントリにあった、System::new_array/System::delete_arrayの実装について説明します。


javaでは以下のように要素の数が異なる配列の作成が可能です。

int v[][] = 
{
  { 0, 1, 2},
  { 0, 1}
};

これはC++では以下のように表現できます。

int **v = new int*[2];
v[0] = new int[3];
v[1] = new int[2];
v[0][0] = 0;v[0][1] = 1;v[0][2] = 2;
v[1][0] = 0;v[1][1] = 1;


C++に変換する上でGCは実装しないことにしていたので、これらもきちんと破棄する必要があります。

delete v[0];
delete v[1];
delete v;


ここで、vに対して操作して全てが破棄できれば便利ですよね?


最終的には以下のようなコードに変換します。

//生成
int **v = static_cast<int**>(System::new_array(2, 2 , sizeof(int*)) );
v[0] = static_cast<int*>(System::new_array(1, 3 , sizeof(int)) );
v[1] = static_cast<int*>(System::new_array(1, 2 , sizeof(int)) );

//破棄
System::delete_array<int*>(v);


vがSystem::delete_arrayに渡される時、int*の要素数がわからなければdelete v[0];とdelete v[1];を呼ぶことが出来ません。


そこで、System::new_array時にそれぞれの要素の数と要素の大きさ、ポインタ参照の深さを記録するようにします。


new_arrayの第1引数はポインタ参照の深さ、第2引数は要素数、第3引数は要素の大きさです。


実際にそれぞれの実装をお見せします。
MALLOCを読み替えればBREW以外でも利用可能です。

static void* new_array(int depth, int elementnum, size_t element_size){
  void *ptr = MALLOC(elementnum * element_size + sizeof(int) * 3);
  *reinterpret_cast<int*>(ptr) = depth;
  *(reinterpret_cast<int*>(ptr) + 1) = elementnum;
  *(reinterpret_cast<int*>(ptr) + 2) = element_size;
  return reinterpret_cast<int*>(ptr) + 3;
}

template<typename T>
static void _delete_array(void *ptr, int depth){
  if( ptr == NULL ) return;
  int elementnum = *(reinterpret_cast<int*>(ptr) - 2);
  int element_size = *(reinterpret_cast<int*>(ptr) - 1);
  if( depth == 1 ){
    LIB_ASSERT( elementnum > 0 );
    if( elementnum == 1 ){ //newして確保した領域
      delete reinterpret_cast<T>(ptr); //(int*)ptr - 3をFREEします
    }else{ //プリミティブな型の配列とか
      FREE(reinterpret_cast<int*>(ptr) - 3);
    }
  }else if( depth > 1 ){
    for(int i=0; i<elementnum; i++){
      _delete_array<T>(
        *reinterpret_cast<void**>(reinterpret_cast<char*>(ptr)
         + i * element_size), depth - 1);
    }
    FREE(reinterpret_cast<int*>(ptr) - 3);
  }
}
template<typename T>
static void delete_array(void *ptr){
  if( ptr == NULL ) return;
  int depth = *(reinterpret_cast<int*>(ptr) - 3);
  _delete_array<T>(ptr, depth);
}
static inline
int size_array(void *ptr){
  return *(reinterpret_cast<int*>(ptr) - 2);
}

//newとdeleteはカスタマイズしたものだけ記載しています
void* operator new(size_t size) throw(){
  void* ptr = MALLOC(size + sizeof(int) * 3);
  *reinterpret_cast<int*>(ptr) = 1;
  *(reinterpret_cast<int*>(ptr) + 1) = 1;
  *(reinterpret_cast<int*>(ptr) + 2) = size;
  return reinterpret_cast<int*>(ptr) + 3;
}

void operator delete(void* address) throw(){
  address = reinterpret_cast<int*>(address) - 3;
  FREE(address);
  return;
}


int型などプリミティブな型では、末端の要素は値で保持しますが、それ以外のクラスのインスタンスはポインタで保持します。

//生成
foo *v = System::new_array(2, 2, sizeof(foo*));
v[0] = new foo();
v[1] = new foo();
//破棄
System::delete_array<foo*>(v);

同じ1次元の配列でも非プリミティブ型ではポインタ参照の深さは2、プリミティブであれば1となります。


この場合インスタンスの生成にはそのままnewを用います。
末端の要素であるv[0]やv[1]にはdelete reinterpret_cast(v[0])に相当するものが呼ばれます。

PNG画像データの透過判別

PNGの画像データにtRNSチャンクがあるかどうか判別するコードです。
tRNSチャンクがあるという事は、透過PNGという事です。
ファイルから読み出しメモリ上にあるデータを探索します。

//void* fileBuffer; メモリに展開された画像データの先頭を示すポインタ
//int dwSize; ファイルサイズ

typedef unsigned char u8;
typedef unsigned int u32;

bool transparency = false;
u8 *dat; = static_cast<u8*>(fileBuffer);
dat += 8;
while( dat < static_cast<u8*>(fileBuffer) + dwSize ){
  u32 chunksize = *reinterpret_cast<u32*>(dat);
  //エンディアン変換 リトルエンディアン環境の場合
  chunksize = 
    (((chunksize      ) & 0xff) << 24) |
    (((chunksize >> 8 ) & 0xff) << 16) |
    (((chunksize >> 16) & 0xff) << 8 ) |
    (((chunksize >> 24) & 0xff)      );

  dat += 4;
  if( dat[0] == 't' &&
    dat[1] == 'R' &&
    dat[2] == 'N' &&
    dat[3] == 'S' ){
      transparency = true;
      break;
  }
  dat += 4;
  dat += chunksize;
  dat += 4;
}

SyntaxHighlighterのサンプルを動かす。

SyntaxHighlighter - Download
現在の最新版は3.0.83です。
シンタックスハイライターははてなダイアリーで言うところのスーパーpre記法のような機能を提供するものです。
残念ながらはてなでは使えませんが、Webや他のブログなどで使うといいと思います。


さて、今回最新版を落としてきたところサンプルを実行するには一手間必要な感じになっていたのでいろいろメモりながら見ていこうと思います。

rvmのインストール

まずcygwinのsetup.exeを起動してgit-coreとcurlとncurses(tput)とgcc(c)をインストールします。まぁ適当に。
続いてrvm: 複数のRubyを共存させる最新のやり方 - 昼メシ物語を参考にrvmをインストール。


次に.bash_profileに以下のコマンドを書き加えます。
これも上記サイトを参考のこと

if [[ -s ${HOME}/.rvm/scripts/rvm ]] ; then source ${HOME}/.rvm/scripts/rvm ; fi


以上です。

rubyのインストール

rvmを使ってrubyをインストールします。
SyntaxHighlighter3.0.83の場合rubyの1.8.7-p249@copydecaが必要なようですので、このバージョンをインストールします。

rvm install 1.8.7-p249@copydeca


私の環境では上記コマンド実行中コンパイルに失敗していました。
詳しく追いきれていないのですが、eval.c:211と gc.c:43のint _setjmp(), _longjmp();をコメントアウトすると通りました。
別に原因があるはずですがこれ以上調べていません。


次に以下のコマンドを実行します

rvm gemset create 'copydeca'


次に以下のコマンドを実行するか、syntaxhighlighter_3.0.83/testsにカレントを変更してください。

rvm 1.8.7-p249@copydeca

rubyスクリプトを起動

シェルスクリプトが用意されているのでそれを叩きましょう

./webrick.sh

停止するにはCtrl+cを押下して下さい。

サンプルを見る

webブラウザで次のURLを入力します。
http://127.0.0.1:2010/
表示されたディレクトリにある以下のファイルがサンプルです。
brushes_tests.html
syntaxhighlighter_tests.html
theme_tests.html

ディレクトリ内のクラスをリフレクションを用いて列挙する

トランスレータの実装方法を模索していた時にリフレクションを用いてクラスの内部構造を書き出すという事を考えていました。
これだとメソッドの実装に手を入れるのが困難なため採用しなかったんですけど、折角なので出来たところまで書いておこうと思います。


特定のディレクトリにあるクラス(classファイル)とそのフィールドと初期値を列挙します。
./classesはクラスファイルが出力されているディレクトリ。
今回はDoja5.0のSDKで作成したアプリだったのでURLClassLoaderで必要なライブラリを追加でロードするようにしています。

File classdir = new File("./classes");
URLClassLoader loader = new URLClassLoader(
    new URL[]{ classdir.toURI().toURL(),
        new File("C:/iDKDoJa5.0/lib/profile/DoJa-5.0/classes.zip"
).toURI().toURL(),
        new File("C:/iDKDoJa5.0/lib/profile/DoJa-5.0/doja_classes.zip"
).toURI().toURL()
    }
);

for( File f : classdir.listFiles() ){
    String name = f.getName();
    int dot;
    if( (dot = name.indexOf(".")) != -1 ){
        name = name.substring(0, dot);
    }
    Class<?> listcls = Class.forName(name, true, loader);
    if( listcls.isMemberClass() ) continue;
    
    Object obj = null;
    
    try{
        obj = listcls.newInstance();
    }catch(Exception e){
        
    }
    
    if( obj == null &&
        listcls.getDeclaredConstructors().length > 0 &&
        !listcls.getDeclaredConstructors()[0].isAccessible() ){
        //まず自分のクラスの型の変数を探す。
        //次にgetInstanceをはじめpublic staticメソッドを片っ端から実行する
        //ここで自分のクラスの型の変数が初期化されればOK
        try{
            Method creater = listcls.getMethod("create");
            obj = creater.invoke(null);
            if( obj == null )
            for( Field search : listcls.getDeclaredFields() ){
                if( search.getType() == listcls ){
                    obj = search.get(null);
                    break;
                }
            }
        }catch(Exception e){
            System.err.println(e.getMessage());
        }
        if( obj == null ){
            try{
                Method creater = listcls.getMethod("getInstance");
                obj = creater.invoke(null);
                if( obj == null )
                for( Field search : listcls.getDeclaredFields() ){
                    if( search.getType() == listcls ){
                        obj = search.get(null);
                        break;
                    }
                }
            }catch(Exception e){
                System.err.println(e.getMessage());
            }                        
        }
        
        //このクラスインスタンス作れません
        if( obj == null ){
            System.out.println("クラス" + listcls.getName() +
                "はインスタンス化出来ません");
            continue;
        }
    }        
    
    System.out.println(listcls.getName());
    for( Field _f : listcls.getDeclaredFields() ){
        System.out.print(Modifier.toString(_f.getModifiers()) + " ");
        Object value;
        if( _f.isAccessible() ){
            value = _f.get(obj);
        }else{
            _f.setAccessible(true);
            value = _f.get(obj);
            _f.setAccessible(false);
        }
        System.out.print(_f.getName() + " = ");
        if( value == null )
            System.out.println("null");
        else if( value.getClass() == String.class )
            System.out.println((String)value);
        else if( value.getClass() == Integer.class )
            System.out.println((Integer)value);
        else if( value.getClass() == Float.class )
            System.out.println((Float)value);
        else if( value.getClass() == Boolean.class )
            System.out.println((Boolean)value);
        else
            System.out.println(value);
    }
}


シングルトンパターンで作成されたクラスも簡易的ですが対応しています。
フィールドの初期値を得るにはインスタンスを作成する必要があるのですが、private宣言されたコンストラクタを持つクラスでは対応できません。
シングルトンパターンの場合は多くの場合getInstance()などといった名前のメソッドでインスタンスを取得できるので、それを利用しインスタンスを作成します。


参考
JavaSE Jarファイルに含まれるクラスを列挙する - @//メモ
chamaeleonmannのはてなダイアリー
Javaリフレクションメモ(Hishidama's Java Reflection Memo)

電波女と青春男5巻感想

ネタバレがあるかもしれませんのでご注意を。


5巻はマコちゃんがお父さんになり、りゅうしさんとエリオが
キャッキャウフフそんなお話でした。
あと前川さんはかわいい。


りゅうしさんとエリオのギクシャクした感じが薄れ、普通の友達と呼べる程度には仲良くなりました(なったのかな?)
これは読んでいて楽しい展開でした。


前川さんが微妙に誠に好意を持ってるのも見ていて楽しいです。
誠は何気ないことを言ったつもりでも前川さんは結構ドキッっとしている様とか。


エリオは電波ではなくなってるんですが、如何せん幼すぎる印象が感じられます。
2人で自転車に乗り海へ飛び込んだ後のエリオなんかは普通の女の子のような振る舞いだったのにどうしてこうなったんでしょう。


5巻はそんな感じでそれぞれの気持ちの変化みたいなものがあったように感じました。
6巻も楽しみです。