LeapMotionをHaskellで動かすための準備 その2

前回の続き。

今回はHaskellでLeapMotionを動かすところまでやる。
具体的には、HaskellFFIを通してc++用のLeapMotoinSDKを使う。

まずは普通にc++でLeapMotionSDKを使ってみる。

step4.cc

#include <iostream>
#include "Leap.h"
using namespace Leap;

Controller *new_controller(void){
    return new Controller();
}
void delete_controller(Controller *controller){
    delete controller;
}
int num_hands(const Controller *controller){
    return controller->frame().hands().count();
}

int main() {
    Controller *controller = new_controller();
    while(true){
        std::cout << "num hands: " << num_hands(controller) << std::endl;
        sleep(1);
    }
    delete_controller(controller);
    return 0;
}

Haskellから呼べるように、手の数を得るための関数を書いている。

LeapMotionのヘッダファイルを"include/Leap.h"に用意し、コンパイルする。

g++ step4.cc -I./include -o step4 libLeap.dylib

実行してみる。

num hands: 0
num hands: 0
num hands: 1
num hands: 1
num hands: 2
num hands: 0

手を検出して手の数が表示された。


では、これをHaskellで実行するように改造する。

先ほどのstep4.ccのメイン関数以外を抜き出して、extern "C"で囲って、wrapper.ccとして保存する。

wrapper.cc

#include "Leap.h"
using namespace Leap;

extern "C" {
    Controller *new_controller(void){
        return new Controller();
    }
    void delete_controller(Controller *controller){
        delete controller;
    }
    int num_hands(const Controller *controller){
        return controller->frame().hands().count();
    }
}

これをHaskellFFIで呼ぶコードを書く。

LeapMod.hs

{-# LANGUAGE ForeignFunctionInterface #-}
module LeapMod where

import Foreign.Ptr
import Foreign.C

data LeapController = LeapController
data LeapFrame = LeapFrame

foreign import ccall "wrapper.h new_controller" cNewController :: IO (Ptr LeapController)
foreign import ccall "wrapper.h delete_controller" cDeleteController :: Ptr LeapController -> IO ()
foreign import ccall "wrapper.h num_hands" cNumHands :: Ptr LeapController -> IO CInt

numHands :: Ptr LeapController -> IO Int
numHands controller = do
    num <- cNumHands controller
    return $ fromIntegral num

そしてそれをmain関数からループ内で呼ぶコードを書く。

Main.hs

import Control.Monad
import Control.Concurrent

import LeapMod

main = do
    ctrl <- cNewController
    forever $ do
        threadDelay $ 1 * 1000 * 1000
        num <- numHands ctrl
        putStrLn $ "num hands: " ++ (show num)

controllerをdeleteする場所が無いのが気になるものの、とりあえずこれで準備はできた。
コンパイルしてみる。

g++ -c -I./include wrapper.cc -o wrapper.o
ghc --make -main-is Main Main.hs -o ex_step4 libLeap.dylib -lstdc++ wrapper.o

実行する。

num hands: 0
num hands: 0
num hands: 1
num hands: 1
num hands: 2
num hands: 0

同じ結果を得ることができました。

これでHaskellからLeapMotionを触れることがわかりました。
あとは、適当にラッパーを充実させてやれば、色々できそうです。