第65回Kotlin dojoを開催した

今週は第65回を開催しました!

引き続きKotlin Hands-onをすすめています。

 

前回の様子はコチラ↓

kdnakt.hatenablog.com

 

 

[第65回の様子]

2021/5/26に第65回を開催した。

 

今週は(たぶん)先週と同じメンバーが来てくれて、自分をいれて5名だった。

自分は今週もナビゲータ役で参加。

 

勉強会本編の内容としては、Kotlin/Nativeを扱うIntroduction to Kotlin/Nativeハンズオンの第3章から第6章までを進めた。

あっというまに終わってしまった……Coroutineは何ヶ月もかかったのに……。

 

[学んだことや疑問点]

  • Introduction to Kotlin/Native: 3. Interoperating with C Libraries
    • ネイティブアプリを実装するときはKotlinの標準ライブラリでは足りないこともあるので、C言語の標準ライブラリにアクセスしよう:例、HTTPリクエストを実行するとき
    • cinteropツールを使うと、Cの関数をKotlinの関数であるかのように呼び出すことができる
    • そのためには、.defファイルでライブラリ情報を定義する必要がある
    • たとえばlibcurl.defは以下のような内容になる
headers = curl/curl.h
headerFilter = curl/*

compilerOpts.linux = -I/usr/include -I/usr/include/x86_64-linux-gnu
linkerOpts.osx = -L/opt/local/lib -L/usr/local/opt/curl/lib -lcurl
linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu -lcurl
    • このファイルはsrc/nativeInterop/cinteropディレクトリに配置する必要がある:他の場所に配置する場合はbuild.gradleファイルで指定が必要
    • headersに指定したライブラリについて、Kotlin用のスタブメソッドが生成される(5章を参照):複数ライブラリを指定する場合は\で区切り新しい行を追加すればよい
    • headerFilterを利用すると、headersで指定したヘッダーファイルが他のヘッダーファイルをインクルードしている場合に、ソースコード中に展開されることを防ぐことができる。バイナリのサイズ削減とかになるらしい。
    • リンカやコンパイラのオプションについては省略。
  • Introduction to Kotlin/Native: 4. Adding interop to build process
    • ビルドを行うには、build.gradleに以下のNLでマークされた行を追加する必要がある
macosX64("macos") {
        compilations.main { // NL
            cinterops {     // NL
                libcurl     // NL
            }               // NL
        }                   // NL
        binaries {
            executable {
                // ...
            }
        }
    }
    • 第2章でlibcurl.defファイルで指定したリンカオプションなどは、以下のように上書きすることもできる
libcurl {
    defFile project.file("libcurl.def")
    packageName 'com.jetbrains.handson.http'
    compilerOpts '-I/path'
    includeDirs.allHeaders("path")
}
    
    • ここまでやると、KotlinのコードからCライブラリにアクセスできるようになる
  • Introduction to Kotlin/Native: 5. Writing the Application Code
    • いよいよKotlin側の実装(今回、実装自体はハンズオンの元リポジトリから前回追加済みなのでプルリクエストは無し)。
    • Main.ktは以下のようになる。
import libcurl.*
import kotlinx.cinterop.*

fun main(args: Array<String>) {
    if (args.size == 1) {
        val curl = curl_easy_init()
        if (curl != null) {
            curl_easy_setopt(curl, CURLOPT_URL, args[0])
            curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L)
            val res = curl_easy_perform(curl)
            if (res != CURLE_OK) {
                println("curl_easy_perform() failed " + curl_easy_strerror(res)?.toKString())
            }
            curl_easy_cleanup(curl)
        }
    } else {
        println("Please provide a URL")
    }
}
    • curl_easy_で始まる各関数がCライブラリとのやりとり部分らしい。なかなかに煩雑……
    • Cライブラリが展開されたKotlinのコードを見ると、CPointerとかそれっぽいクラスがいっぱいあって読みづらい。

f:id:kidani_a:20210527004154p:plain

    • curl_easy_cleanupとか呼び出すのすぐ忘れそうだし、ラッパー作っておかないと実開発では辛そう、などの話をした。
  • Introduction to Kotlin/Native: 6. Building and running the application
    • ビルドは./gradlew buildコマンドを実行するだけではcurl_easy_initなどの関数が参照できず、IntelliJのメニューからBuild Projectを実行する必要があった
    • ビルドするとbuildディレクトリ以下にバイナリが生成される
    • 以下のようにバイナリを実行できた
$ ./build/bin/native/releaseExecutable/kotlin.kexe https://www.example.com
<!doctype html>
<html>
<head>
    <title>Example Domain</title>

(以下略)

 

[まとめ]

モブプログラミング・スタイルで、Introduction to Kotlin/Nativeハンズオンを進めた。

Kotlin/Nativeちょっとだけ理解が深まった気がする!

 

今週のプルリクエストはなし。