StoryEdit 開発日誌

ウェブアプリ StoryEditを作ってましたが延期。普通のブログ。

最近はもっぱらGo言語である。CGOでやっぱりはまる。

Go言語、なかなかに良い。まずはおなじみのバイディングを行ってみた。Go言語にはCGO(シーゴー)というバインディングフレームワークがある。これがなかなか強力。
簡単なライブラリをバインドしてみるわけだが、

package main

/*
#cgo LDFLAGS: -lmath
#include <math.h>
static double go_sin(double x) {
  return sin(x);
}
*/
import "C"

import "fmt"

func main {
   fmt.Printf(float64(C.go_sin(C.double(3.14))));
}

なにしてるかというと、go_sinというラッパー関数をgo本体から呼び出している。
はまったポイントは、import "C"の置き場所である。
以下のように、空行を挟むと、コンパイルに失敗する。

package main

/*
#cgo LDFLAGS: -lmath
#include <math.h>
static double go_sin(double x) {
  return sin(x);
}
*/

import "C"

import "fmt"

func main {
   fmt.Printf(float64(C.go_sin(C.double(3.14))));
}


ちなみに、直接Cの関数を使えるので、以下のようにもかける。というか、こうかく。

package main

/*
#cgo LDFLAGS: -lmath
#include <math.h>
*/

import "C"

import "fmt"

func main {
   fmt.Printf(float64(C.sin(C.double(3.14))));
}

ちなみにCGOの機能をみて、「なんだ、すごく簡単じゃないか。なんでもバインドし放題だぜ」と思ったんだが、これは大きな間違いであった。(その2に続くか?)

JNAではまる。その3。 jna3からjna4、Direct mappingへ移行する。

はまりポイント、第三回。実はこれまで使っていたのは、com.sun.jnaパッケージだったのだが、maven centralで惹いてくると、jna-3.0.9しかなかったため、これを利用していた。しかし、ネットのドキュメントのバージョンは、3.2.7とか。実際は、https://github.com/twall/jna ここで開発されているのがjna-4.0.0なのだが、これはmavenでサポートされていない個別コンパイルのものだと思ってた。しかし、違った。net.dev.java.jnaと指定すれば、とってこれた。

jnaのドキュメントをみてると、実はライブラリのバインドの方法は2つあるようだ。

public class HelloWorld {

    // This is the standard, stable way of mapping, which supports extensive
    // customization and mapping of Java to native types.

    public interface CLibrary extends Library {
        CLibrary INSTANCE = (CLibrary)
            Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
                               CLibrary.class);

        void printf(String format, Object... args);
    }
}

こちらはinner classで読み込む方法。Clibrary.INSTANCE...といちいち書かなければいけないのがおっくうだった。

public class HelloWorld {

    public static native double cos(double x);
    public static native double sin(double x);

    static {
        Native.register(Platform.C_LIBRARY_NAME);
    }

    public static void main(String[] args) {
        System.out.println("cos(0)=" + cos(0));
        System.out.println("sin(0)=" + sin(0));
    }
}

そしてこちらがDirect mappingという方法。extra performanceが必要な場合は、と書いてあるので、こちらのほうがよさそうだ。というわけで、Direct mappingにバインドを移行する。関数がいままでpublic static だったのが、public static native になる。おそらく、Java側のJNIアクセスにあうようなバイナリをメモリに生成するのだろう。jna3での、一つ一つの関数ラッパをメモリ上に生成する方法に比べて早い気がする。

しかし、そこで問題発生。Libraryの初期化で失敗してしまう。
問題になっていたのは、以下の関数。

public static native int epoll_wait (int epfd, EpollEvent[] ev, int maxEvents, int timeout);

どうやら、Structureは使えないようだ。StackOverflowに聞いて、解決。Structureの場合は、Structure[0].getPointer()を渡すようにしろ、とのこと。なるほど。しかし型チェックははずされるので、publicにはしないほうが良いだろう。

というわけで、無事epollのバインドは終了。微妙にsetAlignTypeも変更する必要があった。

    // from sys/epoll.h
    public static class EpollData extends Union {
        public int fd;
        public long u64; // we don't need, but make size 8.
        public EpollData() {
            super();
        }
        public EpollData(int fd_or_u32) {
            super();
            this.fd = fd_or_u32;
            setType(Integer.TYPE);
        }
        public EpollData(long u64) {
            super();
            this.u64 = u64;
            setType(Long.TYPE);
        }
        public static class ByReference extends EpollData implements Structure.ByReference {}
        public static class ByValue extends EpollData implements Structure.ByValue {}
    }

    public static class EpollEvent extends Structure {
        public int events;
        public EpollData data;

        public EpollEvent() {
            super(Structure.ALIGN_NONE);
        }

        protected List getFieldOrder() {
            return Arrays.asList("events", "data");
        }

        public static class ByReference extends EpollEvent implements  Structure.ByReference { }
        public static class ByValue extends EpollEvent implements Structure.ByValue { }
    }

    private static native int epoll_create(int size);
    private static native int epoll_create1(int flags);
    private static native int epoll_ctl(int epfd, int op, int fd, EpollEvent.ByReference ev);
    private static native int epoll_wait (int epfd, Pointer /*EpollEvent[] */ ev, int maxEvents, int timeout);

JNAではまる、その2。__attribute__((__packed__))のマッピングをする。

さらに2時間ほどはまった。。。第一回は以下から。
JNAではまる。Javaからepollを使いたい。 - StoryEdit 開発日誌

さて、やりたいことはepollをJNAから使うってことだが、いかんせん、JNA初心者には学ぶことがおおい。

そもそもバインドしたい構造体をみてみる。

typedef union epoll_data
{
  void *ptr;
  int fd;
  uint32_t u32;
  uint64_t u64;
} epoll_data_t;

struct epoll_event
{
  uint32_t events;      /* Epoll events */
  epoll_data_t data;    /* User data variable */
} __attribute__ ((__packed__));

最初は"union"を見逃していたため、後に苦労したが、この構造体のバインドは以下の順番で処理した。

  1. 構造体のマッピング
  2. 構造体のネストのマッピング
  3. void*のマッピング
  4. unionのマッピング
  5. __attribute__(((__packed__)))のマッピング (はてな文法のため括弧ひとつ多い)

5つ目のpackedされたデータのマッピングは情報がなかなか見つからなかったのでここについて書くことにする。

__attribute__(((__packed__)))って?

CPUは現在、モバイル機器を除いて、64bitCPUが主流だ。プログラマが考えなければいけないのは、主にレジスタ、パイプライン、キャッシュなどであるが、これらがすべて64bit単位で動く。Cコンパイラはある構造体をコンパイルするときに、CPUからアクセスしやすいように、構造体のアラインメントを行う。これは、CPUからアクセスする単位である8バイトの先頭にデータがくるように、すべてのデータをそろえた方が、アクセス効率が良く、全体的にパフォーマンスがあがるからだ。

このため、8バイトの乗数未満のデータはすべて8バイト単位に整列させられる。しかし、構造体のネストや配列化がおこる場合、このアラインメントはメモリ効率を落としてしまう。特にCPUキャッシュはサイズが小さいため、なるべく多くの情報をのせた方がいい。したがって、コンパイラはすべてのソフトウェアを対象に性能をあげるためにアラインメントをおこなうが、パフォーマンスクリティカルなソフトウェア(カーネルなど)は、キャッシュサイズを意識したプログラミングが行われる。そして__attribute__(((__packed__)))は、この最適化を行うための指示で、コンパイラによるアラインメントを抑止する。上のepoll_data_tは実際は12バイトのデータを持つが16バイトにアラインメントされるところを、最小の12バイトで作る。

__attribute__(((__packed__)))のmapping

JNAで構造体をマッピングするとき、デフォルトではアラインメントは勝手にプラットフォームにあわせて行われる。しかし、このアラインメントの種類をかえるメソッドが、Structure.setAlignTypeである。
従って、以下のようにバインドする。

        // epoll bindings
        public static class EpollData extends Union {
            public int fd;
            public long u64; // we don't need this, but make size 8.
            public static class ByReference extends EpollData implements Structure.ByReference {}
            public static class ByValue extends EpollData implements Structure.ByValue {}
        }

        public static class EpollEvent extends Structure {
            public EpollEvent() {
                super();
                setAlignType(Structure.ALIGN_NONE); // to make it packed
            }
            public int events;
            public EpollData data;
            public static class ByReference extends EpollEvent implements  Structure.ByReference { }
            public static class ByValue extends EpollEvent implements Structure.ByValue { }
        }

Structure.ALIGN_NONEはアライメントなし。今回はStructure.GNU_Cでも良い。

JNAではまる。Javaからepollを使いたい。

2時間ほどはまった。時間返せ。

JNAとは、Java Native Accessのことである。ネイティブなライブラリ関数をJavaから呼び出すための仕組みで、libffiを使って実装されている。

@ITページのサンプルに、以下のような構造体のバインドが載っている。
http://www.atmarkit.co.jp/fjava/special/jna/jna_3.html

public static class _Employee extends Structure {
     String id;        /*社員番号*/
     String name;    /*氏名*/
     int age;          /*年齢*/
     String sectionId; /*部署番号*/
}

非常に簡単である。私の以下のようにepollの構造体をバインドした。

        public static class EpollData extends Structure {
            Pointer ptr;
            int fd;
            int u32;
            long u64;
        }
        public static class EpollEvent extends Structure {
            int events;
            EpollData data;
        }

そして、テストで、この構造体をnewする。

  EpollEvent ev = new EpollEvent();

すると、こうだ。

  testCreateHandler: Structure class org.hogehoge$Libc$EpollEvent has unknown size (ensure all fields are public)

実際には、それぞれのフィールドにpublicが必要になる。エラーメッセージにもろ正解が書いてあるんだが、JNAはおろか、Javaさえよく理解していない私には、このメッセージに気づくまでに時間がかかった。

そして、以下のサイトをみて、メッセージの意味に気づく。
http://www.eshayne.com/jnaex/example04.html

なるほど。publicついてるね。

最終的にはこういう形に落ち着きそう。

        // epoll bindings
        public static class EpollData extends Structure {
            public Pointer ptr;
            public int fd;
            public int u32;
            public long u64;
        }
        public static class EpollEvent extends Structure {
            public static class ByValue extends EpollEvent implements Structure.ByValue  {}
            public int events;
            public EpollData data;
        }

追記:上のバインディングはまだ未完成だった。詳細は以下。
JNAではまる、その2。__attribute__((__packed__))のマッピングをする。 - StoryEdit 開発日誌

Effective Java 2nd Editionを読んでいる。

Java はじめてもうすぐ一年。仕事で使い始めて半年。徐々に慣れてきた。そろそろまじめに、Effective Java 2nd Editionを読む気になって読み進めているのが、想像以上に面白い。2、3、6章を読み終えた。

Javaの深いところ、とはいわないが、言語的にこう書くのがモダンである(しかしEJ2がでたのは遠い昔の2008年だが)、というような項目がつらつらと書いてあり、Cあがりの私には新鮮である。Design pattern全盛のころ(2006年頃)にGoF本を読んでた私には、たとえば、開始早々でてくる「Singletonはenumを使え」の時点で既に新鮮である。JavaでSingletonを書くためには、getInstanceだろ、と刷り込みがおこっているためだ。

Builderの実装にも惚れた。古き佳きJavaを触っていた私が知っていた、1年前のJavaは、JavaBeans patternと呼ばれる書き方であったようだ。再び触りはじめたころにOSSを読んで、「そうか、thisをコンストラクタで呼び合うようにするのが変更箇所が少なくてよりモダンなのか」と思い、書いていたコードはTelescoping patternと呼ばれており、既に古きものになっていたらしい。たしかに、Argumentが多い場合はBuilderが良い。returnでBuilderを返すあたりも、Javascriptっぽくかけるので好きだ、いや、モダンだ(StringBuilderのappendの羅列を見たあたりで、なにかしらreturnする書き方に改めたが)。

@SuppressWarningをつけろとやたらいってくるIDEを無視し続けていたが、なぜ警告してくるかも明記してある。Reflectionが便利だという話は知っていたが、実際に使ってみて、その威力は明らかになったし、JUnitアノテーションを自分で実装できる例題がありつつ、最後には「しかしAnotation拡張は使うべからず」との、なんとも残念な結びにも、共感できる。

「エキスパート C」や「バイナリハック」のような実装の良書を待ち望んでいる私には、正直、Effective Javaなんぞ、、、と思っていたが、もっと早く読んでいれば良かった。2書を超えるものではないにしても、Java界隈ではじめて出会えた良書である。これも半年のJavaのコーディングの中で、なかなか思うようにコードを書けないことに悩んだ末に読んだからの感動なのかもしれない。

おすすめである。

Effective Java (2nd Edition): A Programming Language Guide (Java Series)

Effective Java (2nd Edition): A Programming Language Guide (Java Series)


追記:ちなみに、他の良書も調べてみると、以下のリンクが良さげ。Refactoring、Clean codeと既読本も入っており、他の本も読んでみようと思う。

http://java.dzone.com/articles/top-10-books-advanced-level

Go言語、ビルド編。

Scala, Erlang, Go, などなど、様々な新言語がひしめいてきた2013年。4年前の登場から、話題性が高かったGO言語だが、久々に触ってみた。

ビルドがgo buildになってる。

'6g'とか、アーキテクチャにあわせたコンパイラがいくつもついてきた、リリース当初。それでもgofmtとか、新時代を予兆させるツールはついていたものの、ライブラリ周りがあまりに揃っておらず、実用にはやはり遠いと感じたあのころ。Makefileをかくのか、、うーん、たしかに、モダンとは云いがたいなぁ、なんて思ってました。

久々に触ったGoには、Go toolsという充実のツール郡がついてる。buildも、そのひとつ。

Go pathって?

Go pathとは、Go toolsが前提とするビルド環境をさす。Go言語では、GOPATHという環境変数があり、GOPATHにて、Go pathへのパスを指定する。Go pathの構成を以下に示す。

-- src
 |-- mysoft
    |-- foo
       |-- foo.go
    |-- bar
       |-- bar.go
-- bin
-- pkg

Go pathは、基本的に三つのディレクトリで構成される。src, pkg, binだ。Go言語のtarballを解凍したら、まさしくこの基本構成となっていることに気づくだろう。'go build'は、ソフトウェアを、このGo pathで開発、ビルドすることを前提としている。

src

ここには、ソールファイルがおかれる。Javaなどと同じく、名前空間でディレクトリが別れている。mysoftというプログラムを書いているとし、mysoftのなかには、foo、barという2種類のパッケージを実装しているとしよう。すると、src/mysoft, src/mysoft/foo, src/mysoft/barという3つのディレクトリ以下に、ソースファイルをおいておく。

pkg

ここには、依存する外部パッケージを置く。.aなどの静的ライブラリがおかれるのもここ。

bin

できたてのバイナリがここにのる。

具体的に、どうやってビルドする?

環境設定

go buildは、Go pathでビルドされることを前提とする。ここで注意点は、標準ライブラリなどもpkgにはいってないと使えないことである。そのため、Go言語のtarballを解凍したディレクトリ(標準ライブラリとかある)+自分のソフトウェアのディレクトリがGOPATHにはいってる必要がある。

このため、

$ export GOPATH=/path/to/go
$ export GOPATH=$GOPATH:$MYSOFTDIR

と、GOPATHに、標準のGoディレクトリと、自身のソフトウェアディレクトリをいれてやる必要がある。

ビルドー
$ go build mysoft

で、おけ。


ちなみに、最近のGoだと、GOROOTと、GOPATHは同じディレクトリにできないので、注意。

Gitでサブディレクトリのみをマージする。git filter-branchで。

タイトル通り。あるサブディレクトリだけブランチ間でマージしたい。というとき。

いろいろ方法はあるが、以下の方法をとった。

1. git filter-branchでそのディレクトリだけのリポジトリを作る(切り出し)
2. マージする。
3. パッチを作成する。
4. 元のリポジトリをcloneしてきて、サブディレクトリに移動し、パッチをあてる。

1. git filter-branchでそのディレクトリだけのリポジトリを作る(切り出す)

> cd $proj
> ls
dir_a        dir_b
> git filter-branch --subdirectory-filter dir_b --prune-empty --all

これで、サブディレクトリだけ切り出すことができる。

2. マージする。

> git branch
*  branch1
   branch2

> git merge  branch2

普通にマージ。

3. パッチを作る

> git diff --cached > patch

mergeされているものとのdiffは--cached で。

4. 元のリポジトリをcloneしてきて、サブディレクトリに移動し、パッチをあてる。

> git clone git@github.com/myrepo.git
> cd myrepo
> cd dir_2
> patch -p1 < patch

3で作ったパッチは、ディレクトリが一階層ずれてるのだけ注意。git applyはなぜかうまく行かなかったので、普通に当てました。git apply使える人はそれで。

ちなみに、git filter-branchなんて使ったことなかったんだけど、なかなか強力。すべてのコミットにわたって、適用したい動作を投げて、再コミットしてくれる、まさに完全書き換えコマンド。

合わせて、以下の公式ドキュメントもおすすめ。
Git - 歴史の書き換え


予想以上にらくちんにできるgitに感動。やっぱいいね〜。