IntelliJ IDEA / Android StudioでCのコードをKotlinに変換する
IDEAにはnj2kというJavaのソースコードをテキストエディタにペーストしたらKotlinに変換してくれる機能がある(コピペでなくてもメインメニューから変換を指示できるようにもなっている)。当然ながらJavaのコードにしか対応していないけど、KotlinはもともとJavaの土壌から生まれた言語ということもあって変換精度が非常に高い。
(JetBrains/Kotlinのリポジトリではnj2kというディレクトリに変換処理の実装がある。j2kディレクトリにあるものは古い。)
それであれば、いったんCのコードをJavaっぽいコードにして、そこからKotlinに変換してしまえば、退屈な作業のほとんどはnj2kにやらせてしまえば良いのではないか。そう思ってちょっと試しにやってみたら何とかなって、Cのコーディング作業でまる一日くらいかかっていた肉体労働が、1,2時間でだいたい何とかなった。
ただし当然ながらコピペする前のCのコードには変更を加えてあるし、ペーストした後のKotlinコードに修正が必要ないわけではない。あくまで人間にとって面倒な肉体労働(引数の順番の調整とか記号の調整とか)を省力化するためのものだ。自動変換にコストをかけすぎても時間の無駄だ。
いくつかTipsを書いておこう(記憶で書いているので完全なリストではない)。
構造体: これはrecord classにしてしまうのが良いし、nj2kを使わずに自前でコンストラクタに変換したほうが早い。次のようなstructがあるとする。
typedef struct xyz_tag {
int a;
float b;
} xyz;
メンバーの部分だけ選択して、エディタのfind/replaceで (.*) (.*);
をval \2 : \1
に置き換える(型名は後でいじるので今はCのままで良い)。
val a : int
val b : float
頑張れば複数行選択でtypedef ...
から型名まで自動でやってもいいと思うけど、今回はそこまで構造体宣言が無かったので手作業のほうが早かった。
ポインタ: これがあるとnj2kはJavaのコードだと認識しない。一番愚直なのは*
が付いているやつをPtr
とか適当な名前に変えてしまうことだけど、コレクションのとして使っているポインタなら、MutableList<T>
にしてしまえば、foo[x] = y;
スタイルのメンバーアクセスをそのまま残せるので、そうしたほうがよい。これがimmutableなList<T>
だと、foo.get(x) = y
みたいな謎コードが生成されて、結局エラーになってしまう。
void*
をデータバッファとして使っているなら、MutableList<Byte>
にしておくのが安牌だろう。
型 : これは、基本型については、だいたい次の通りで良いと思う。
- char, unsigned char, uint8_t:
UByte
またはByte
- short, unsigned short, ushort, int16_t, uint16_t:
UShort
またはShort
- int32_t, uint32_t:
UInt
またはInt
- int, unsigned int: たぶんだいたい
UInt
またはInt
でよい - int64_t, uint64_t:
ULong
またはLong
- long unsigned long: たぶんだいたい
ULong
またはLong
でよい
これは一定のベストプラクティスが無さそうで、プロジェクトに合わせて自分でアプローチを選択したほうが良いと思う。というのはCでカジュアルに使えるunsignedな整数がKotlinではカジュアルに使えない・experimentalTypesとして使えても型変換ですぐエラーになりやすい、という問題があるためだ。
今回のプロジェクトではさいわいほとんどsignedで扱える数値ばかりだったので(MIDI 1.0の仕組みがそうなっている)、Cライブラリで全部unsignedにしていたものが、Kotlinでは全部signedになった。
この変換はテキストエディタで雑に行えば良いと思う(といっても前後スペースくらいは付けておかないと想定外のテキストを変換する可能性があるのでフツーに気をつける)。
…こんなところだろうか。IDEAで変換対象のソースファイルをKotlinとして作って、エディタ上にペーストした時に「Javaのコードっぽいけど変換する?」って訊かれたらだいたい勝ちだ(前述のMutableListみたいに生成後のコードが無意味なものになっていたりすることもあるので、もう少し手を加えてから変換したほうが良い、みたいになることはある)。
ちなみに単純にCのライブラリとして作った機能をKotlinでも使いたいという場合は、JavaCPPとか使ってJNI経由での呼び出しを自動生成したほうがメンテナンス性も高いだろう。
このライブラリはその意味では少々特殊で、Cライブラリはバインディングを作りやすいように出来ていない(リアルタイム処理を必要とするアプリに組み込みやすいようにsingle headerでstatic inlineだらけのライブラリにしてある)ので、Kotlin向けにだいたい同じ機能をもつAPIを実装している。