スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

[Haskell]Qtのウィンドウを出してみる

とりあえずQtのウィンドウを出して、ボタンをクリックしたときにHaskellから渡した関数を呼び出すところまでやってみたのでメモしておきます。

Mac OSX 10.6.5
GHC 7.0.1
Qt 4.7.1

-- Main.hs
{-# LANGUAGE ForeignFunctionInterface #-}

module Main where

import System.Environment
import Foreign.C.Types
import Foreign.C.String
import Foreign.Ptr
import Foreign.Marshal.Array

main = do
(argc, argv) <- getArgs >>= makeCArgs
funptr <- makeFunPtr (putStrLn "Haskell function")
qtExample argc argv funptr

makeCArgs :: [String] -> IO (CInt, Ptr CString)
makeCArgs args =
mapM (newArray . (map castCharToCChar)) args
>>= newArray
>>= \argv -> return ((fromIntegral . length) args, argv)

foreign import ccall "wrapper" makeFunPtr :: IO () -> IO (FunPtr (IO ()))
foreign import ccall "qt_example" qtExample
:: CInt -> Ptr (Ptr CChar) -> FunPtr (IO ()) -> IO ()


/* example.cpp */
#include <QtGui>
#include "SlotProxy.h"

extern "C" {

void qt_example(int argc, char *argv[], void (*slot)())
{
QApplication *app = new QApplication(argc, argv);

QWidget *window = new QWidget();
window->resize(300, 150);

QPushButton* button = new QPushButton("Button", window);
button->setGeometry(100,50,100,50);

QObject::connect(button, SIGNAL(clicked()), new SlotProxy(button, slot), SLOT(run()));

window->show();
app->exec();

delete window;
delete app;
}

}


/* SlotProxy.h */
#ifndef __SlotProxy
#define __SlotProxy

#include <QtGui>

class SlotProxy : public QObject
{
Q_OBJECT

public:
SlotProxy(QObject* parent, void (*slot)());
~SlotProxy();
public slots:
void run();
private:
void (*slot)();
};

#endif


/* SlotProxy.cpp */
#include "SlotProxy.h"
#include <stdio.h>

SlotProxy::SlotProxy(QObject* parent, void (*slot)()) : QObject(parent){
this->slot = slot;
}
SlotProxy::~SlotProxy(){
printf("SlotProxy::~SlotProxy\n");
}
void SlotProxy::run(){
this->slot();
}


ビルド。

moc SlotProxy.h -o moc_SlotProxy.cpp
ghc --make Main.hs example.cpp SlotProxy.cpp moc_SlotProxy.cpp -lstdc++ -framework QtCore -framework QtGui -I/Library/Frameworks/QtGui.framework/Headers

mocは、SIGNALやSLOTを利用できるようQ_OBJECTマクロを持つクラスを拡張してくれるそうです。
スポンサーサイト

[Haskell]FFI使ってC++バインディングに入門

この記事はHaskell Advent Calendarのために書いたものです。

FFIでCの関数を呼び出すところまではすんなりいったので、次C++行ってみよう!として
ドハマりしたという内容です。


C++でクラスを作成して、ラッパー関数を書き、Haskellから呼び出すという手順でやってみます。

とりあえず何も考えずに以下のコードを書きました。

point.h
point.cpp
wrapper.cpp
main.hs


/* point.h */

class Point{
public:
Point(int, int);
int getX();
int getY();
private:
int x, y;
};

/* point.cpp */

#include "point.h"

Point::Point(int x, int y){
this->x = x;
this->y = y;
}

int Point::getX(){
return this->x;
}

int Point::getY(){
return this->y;
}

/* wrapper.cpp */

#include "point.h"

Point* createPoint(int x, int y){
return new Point(x, y);
}

int getPointX(Point* p){
return p->getX();
}

int getPointY(Point* p){
return p->getY();
}

void destroyPoint(Point* p){
delete p;
}

-- main.hs

{-# LANGUAGE ForeignFunctionInterface #-}

module Main where

import Foreign
import Foreign.Ptr
import Foreign.C.Types

data Point = Point

main = do
p <- c_createPoint 1000 2000
x <- c_getPointX p
y <- c_getPointY p
c_destroyPoint p
print x
print y

foreign import ccall "createPoint" c_createPoint :: CInt -> CInt -> IO (Ptr Point)
foreign import ccall "getPointX" c_getPointX :: Ptr Point -> IO CInt
foreign import ccall "getPointY" c_getPointY :: Ptr Point -> IO CInt
foreign import ccall "destroyPoint" c_destroyPoint :: Ptr Point -> IO ()


ビルドしてみます。

$ g++ -c point.cpp wrapper.cpp
$ ghc --make main.hs point.o wrapper.o



すると以下のようなエラーが。

[1 of 1] Compiling Main             ( main.hs, main.o )
Linking main ...
ld: warning: in point.o, file was built for unsupported file format which is not the architecture being linked (i386)
ld: warning: in wrapper.o, file was built for unsupported file format which is not the architecture being linked (i386)
Undefined symbols:
"_destroyPoint", referenced from:
_rKi_info in main.o
_sTt_info in main.o
"_getPointX", referenced from:
_rKg_info in main.o
_sTx_info in main.o
"_createPoint", referenced from:
_rKe_info in main.o
_sWN_info in main.o
ld: symbol(s) not found
collect2: ld returned 1 exit status


「Undefined symbols?」が目にはいったので調べてみると、
CとC++ではコンパイル後の関数名が異なるとのこと。

オーバーロード機能があるC++では同名の関数をユニークな名前に
変換する必要があると書いてあります。
(マングリングっていうらしい)

納得。


でC++で用意した関数をCからも呼べるようにこのマングリングを回避するのが
よく目にする、「extern "C" { }」ってことでした。

早速、Haskellから直接呼ぶ関数を並べているwrapper.cppを
「extern "C" { }」で囲って再度ビルド。


同じエラーが・・・

よくよく見ると

file was built for unsupported file format



というエラーが一緒に出てます。

Mac SnowLeopardで作業していたのですが、そのgccが64bitでコンパイルしていたことによるものでした。
ghcは64bitコードを扱えないそうです。

32bitでコンパイルすればこの問題は解決します。

$ g++ -c point.cpp wrapper.cpp -m32
$ ghc --make main.hs point.o wrapper.o



今度はエラー内容が変わりました。

[1 of 1] Compiling Main             ( main.hs, main.o )
Linking main ...
Undefined symbols:
"operator delete(void*)", referenced from:
_destroyPoint in wrapper.o
_createPoint in wrapper.o
"operator new(unsigned long)", referenced from:
_createPoint in wrapper.o
"___gxx_personality_v0", referenced from:
___gxx_personality_v0$non_lazy_ptr in wrapper.o
(maybe you meant: ___gxx_personality_v0$non_lazy_ptr)
ld: symbol(s) not found
collect2: ld returned 1 exit status



new とか deleteを使えないって言ってるっぽい・・。


でもC++ライブラリのバインディングが存在してるのでできるはず!
とドキュメントを漁っていたら以下の記事に遭遇。

http://www.haskell.org/haskellwiki/CPlusPlus_from_Haskell

この記事の中段あたりに

ghc --make main.hs -o hybrid_program -lstdc++ nifty_code.o



と書かれているのを発見。

「-lstdc++」って、もろいけそうじゃん!

ってことで・・

$ g++ -c point.cpp wrapper.cpp -m32
$ ghc -lstdc++ --make main.hs point.o wrapper.o



ビルドできたー!
C/C++周りの知識に乏しい自分にはきつかった・・org


「-lstdc++」ってgccでC++をビルドする際に使われていたオプションみたいですね。


以上をまとめると・・

・Haskellから呼び出す関数は、extern "C" { }で囲っておく。
・ghcは64bitコードを現在サポートしていない。
・リンクのとき、「-lstdc++」をオプションとして指定する。


C++コードをghciで扱う方法が分かりませんでした。
どなたかご存知でしたらぜひ教えていただきたいです。


最後にMecabで試してみました。
(Mecabのホームページに掲載されていたコードを拝借しました。)

-- main.hs

{-# LANGUAGE ForeignFunctionInterface #-}

module Main where

main = c_mecabTest

foreign import ccall "mecabTest" c_mecabTest :: IO ()

/* wrapper.cpp */

#include <mecab.h>
#include <iostream>

extern "C" {

void mecabTest(){
char input[1024] = "太郎は次郎が持っている本を花子に渡した。";
MeCab::Tagger *tagger = MeCab::createTagger (0, 0);
const MeCab::Node* node = tagger->parseToNode(input);
for (; node; node = node->next) {
std::cout << node->id << ' ';
if (node->stat == MECAB_BOS_NODE)
std::cout << "BOS";
else if (node->stat == MECAB_EOS_NODE)
std::cout << "EOS";
else
std::cout.write (node->surface, node->length);

std::cout << ' ' << node->feature
<< ' ' << (int)(node->surface - input)
<< ' ' << (int)(node->surface - input + node->length)
<< ' ' << node->rcAttr
<< ' ' << node->lcAttr
<< ' ' << node->posid
<< ' ' << (int)node->char_type
<< ' ' << (int)node->stat
<< ' ' << (int)node->isbest
<< ' ' << node->alpha
<< ' ' << node->beta
<< ' ' << node->prob
<< ' ' << node->cost << std::endl;
}

delete tagger;
}

}


ビルドと実行。

$ ghc --make main.hs wrapper.cpp /usr/local/lib/libmecab.dylib -lstdc++
$ ./main
0 BOS BOS/EOS,*,*,*,*,*,*,*,* 0 0 0 0 0 0 2 1 0 0 0 0
5 太郎 名詞,固有名詞,人名,名,*,*,太郎,タロウ,タロー 0 6 1291 1291 44 2 0 1 0 0 0 8614
8 は 助詞,係助詞,*,*,*,*,は,ハ,ワ 6 9 261 261 16 6 0 1 0 0 0 9699
15 次郎 名詞,固有名詞,人名,名,*,*,次郎,ジロウ,ジロー 9 15 1291 1291 44 2 0 1 0 0 0 20566
18 が 助詞,格助詞,一般,*,*,*,が,ガ,ガ 15 18 148 148 13 6 0 1 0 0 0 21070
22 持っ 動詞,自立,*,*,五段・タ行,連用タ接続,持つ,モッ,モッ 18 24 742 742 31 2 0 1 0 0 0 25702
31 て 助詞,接続助詞,*,*,*,*,て,テ,テ 24 27 307 307 18 6 0 1 0 0 0 24174
40 いる 動詞,非自立,*,*,一段,基本形,いる,イル,イル 27 33 919 919 33 6 0 1 0 0 0 24723
46 本 名詞,一般,*,*,*,*,本,ホン,ホン 33 36 1285 1285 38 2 0 1 0 0 0 29905
53 を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ 36 39 156 156 13 6 0 1 0 0 0 29095
54 花 名詞,一般,*,*,*,*,花,ハナ,ハナ 39 42 1285 1285 38 2 0 1 0 0 0 33000
63 子 名詞,接尾,一般,*,*,*,子,コ,コ 42 45 1298 1298 51 2 0 1 0 0 0 39627
65 に 助詞,格助詞,一般,*,*,*,に,ニ,ニ 45 48 151 151 13 6 0 1 0 0 0 39654
77 渡し 動詞,自立,*,*,五段・サ行,連用形,渡す,ワタシ,ワタシ 48 54 735 735 31 2 0 1 0 0 0 42751
86 た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ 54 57 435 435 25 6 0 1 0 0 0 41874
89 。 記号,句点,*,*,*,*,。,。,。 57 60 8 8 7 3 0 1 0 0 0 38440
91 EOS BOS/EOS,*,*,*,*,*,*,*,* 60 60 0 0 0 0 3 1 0 0 0 36904



動いたー!
プロフィール

jou4

Author:jou4
FC2ブログへようこそ!

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。