LoginSignup
14
13

More than 5 years have passed since last update.

Modulemap 経由で C のライブラリを利用する

Last updated at Posted at 2016-01-05

はじめに

dynamic framework で C のライブラリを利用したい場合、 Bridging Header 経由では import できないため modulemap を利用します

今回は libevent を Swift から扱ってみたかったので、それをベースに説明します

modulemap 自体に関する解説はこちらが詳しいです
http://qiita.com/ysn551/items/6f783af2b65c2c4d4631

サンプル

導入

modulemap の定義

module libevent [system] {
    link "event"
    umbrella header "libevent-umbrella.h"
    export *
    module * { export * }
}
libevent-umbrella.h
#import <evhttp.h>
#import <event2/event.h>
#import <event2/http.h>
#import <event2/bufferevent.h>
#import <event2/dns.h>

大雑把に説明すると以下になります

  • module <#モジュール名> の部分が import する際の名前になる

    • e.g. import libevent
  • umbrella header <#ヘッダー名> の部分で import されている宣言が Swift から見えるようになる

    • Bridging Header と似たようなもの
  • link <#ライブラリ名> の部分がリンカ処理の際に追加される

    • ビルドログなどで目にする -levent になる部分
    • 場合によってはビルド設定でライブラリの場所を示す LIBRARY_SEARCH_PATHS を指示することも必要
    • link はオプションなので、省略した際は自分でライブラリをリンクする必要がある

modulemap を Xcode で利用する際はビルド設定の SWIFT_INCLUDE_PATHS に modulemap が置かれたディレクトリを追加します

xcodeproj.png

ここまで行うと Swift のソースコードで import したライブラリの関数などの補完が働き、実際に実行することができます

利用シーン

import libevent

func http_request_done(req: UnsafeMutablePointer<evhttp_request>, arg: UnsafeMutablePointer<Void>) {

    var buf = Array<CChar>(count: 256, repeatedValue: 0)
    var nread: Int32 = 0
    repeat {
        let input = evhttp_request_get_input_buffer(req)
        nread = evbuffer_remove(input, &buf, 255)
        if nread == 0 { break }
        print(String.fromCString(buf))
    } while(nread > 0)

    event_base_loopbreak(COpaquePointer(arg))
}

var uri_path: String = ""

let uri = evhttp_uri_parse("http://qiita.com/tattsun58/items/50e606696f1daa607a5f")
let host = evhttp_uri_get_host(uri)
let port = evhttp_uri_get_port(uri) == -1 ? 80 : evhttp_uri_get_port(uri)
let path = evhttp_uri_get_path(uri)
if let path = String.fromCString(path) where path.characters.count != 0 {
    uri_path = path
} else {
    uri_path = "/"
}

let base = event_base_new()
let dns_base = evdns_base_new(base, 1)

let conn = evhttp_connection_base_new(base, dns_base, host, UInt16(port))
let req = evhttp_request_new(http_request_done, UnsafeMutablePointer(base))

evhttp_add_header(req.memory.output_headers, "Host", host)
evhttp_add_header(req.memory.output_headers, "Connection", "close")

evhttp_make_request(conn, req, EVHTTP_REQ_GET, uri_path)
evhttp_connection_set_timeout(req.memory.evcon, 600)
event_base_dispatch(base)

evhttp_connection_free(conn)
event_base_free(base)

おわりに

C ではたくさん出てくる char * を扱う部分(Swift 上では UnsafePointer<Int8>)は Swift.String をそのまま与えることができるので思ったより書きやすかったです

また、今回のサンプルは podspec を同梱しているので pod install すれば import libevent がすぐに利用できるようになっています
※ 事前に brew install libevent が必要です

14
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
13