moveしたりforwardしたりするC++0x
今日は野良仕事をしに行っていたわけだが、まぁこのことは後に書くとしよう。
正味の仕事は3時間ほどだったので大したことはなかったが、これでもかなりの効果はあったようで。
手伝いに行った意味もあったものだ。
C++0xやるかと思って、改めてVisual Studio 2010のBeta版を入れてみた。今のところ一番いけてるコンパイラだと思う。(参考記事 : C++0xの化け物じみた右辺値参照)
これを見ると結構いろいろな機能に対応しているね。なかなかよい。
その上でいろいろ試していたのだが、おもしろいこともあるもんだ。
std::unique_ptrという右辺値参照を活用したスマートポインタがあるのだが、これってコンテナに入れられたかなと思って実験してみた。
std::vector<std::unique_ptr<std::string>> v(1);
std::unique_ptr<std::string> p1(new std::string("hage"));
v.at(0)=std::move(p1);
v.push_back(std::unique_ptr<std::string>(new std::string("hoge")));
なんか平然と入れられるし。
std::move付け忘れるとどうなるかって話ですが、へんなエラーが出てコンパイルに失敗する。
まぁしかしこれがいけるならshared_ptrよりもシンプルでいいよね。unique_ptr万歳!
これを回すときですが、せっかくなのでfor_eachしてみた。
std::for_each(v.begin(),v.end(),[](std::unique_ptr<std::string>& p){
std::cout << *p << std::endl;
});
shared_ptrと違ってコピーできないので参照渡ししないとならん。constは必要に応じてどうぞ。
果たしてこう言うのが便利なのかと言われると難しいが、他のところにコピーして渡す気さえなければいいんじゃないの。
さて、もう左辺値を破壊してしまってもかまわないときにはstd::moveを使うのですが、std::forwardというのもある。
これはテンプレートに関係がある。
template <typename T>
Test TestCreator(T&& other){
return Test(other);
}
こんなテンプレート関数があったとき、Tの型名は自動で推論される。
もしTest型の右辺値を渡せばT=Testとなるのは容易に予想できる。
ところがTest型の左辺値を渡すとT=Test&となってこのテンプレートが使われる。
なんでやねんという話だがT& &やT& &&やT&& &はT&になるというルールがあるらしく、Test& &&はTest&と同じ意味になってしまうから。
まぁなんにせよ、こう書いておけば右辺値も左辺値も歓迎と言うことになる。
ここで元が右辺値ならstd::moveしたいが、元が右辺値であるかチェックしたいと。
そこでstd::forwardで、これを使うことで判別して適当にしてくれると。
return Test(std::forward<T>(other));
こんな風に書けば、自動で判定して適当な処理をしてくれる。
右辺値か左辺値かまで完全に転送するからPerfect Forwardingなんだと。
これを見て初めて理解できた。
それにしてもこの機能はすばらしいね。C++0xすばらしい。
しかしそんな風に言ってますが、C++0xは2000年代にリリースできなかったんですよね。
本来ならC++09みたいになるはずだったのに失敗してしまったと。
果たしてどういう方向にまとまるのか。できるだけ早くまとめてほしいところ。
以下に素晴らしくてもコンパイラが対応してないと使えないもん。
Author : hidemaro
Date : 2010/04/01(Thu) 23:58
C・C++ | comments (0) | trackback (0)
丸いQtウイジェット
なんと丸3日でそれなりのものができたというのは驚くところ。
ただ、まだ詰めが甘いかなと思うところもあるし、他にもやることはあるし。
2日後の展示のためのプログラムなのですが、果たしていけるのだろうか。
今日のお題はQtで丸いウイジェットを作る方法。
普通はウイジェットっていうのは四角いものです。
ですが、丸の部分以外を表示しなければ丸く見えるということで丸いウイジェットを作れるわけです。
この辺は.NET Frameworkでもありますよね。
System.Windows.Forms.ControlのRegionプロパティにしかるべき領域を設定すると。
それに相当するQtのはQWidgetのsetMaskですね。
Circleクラスとして作ってみた。
まずヘッダファイル。
#ifndef CIRCLE_H単に継承しただけです。なんてことはありません。
#define CIRCLE_H
#include <QtGui/QWidget>
class Circle : public QWidget{
Q_OBJECT
public:
Circle(QWidget* parent=0);
};
#endif // CIRCLE_H
そしてソースファイル。
#include "circle.h"setMaskにQRegionのオブジェクトを与えて設定する。
Circle::Circle(QWidget *parent) : QWidget(parent){
this->setMaximumSize(60,60);
this->setMinimumSize(60,60);
this->setMask(QRegion(QRect(0,0,60,60),QRegion::Ellipse));
QPalette mypalette;
mypalette.setColor(QPalette::Window,QColor(Qt::red));
this->setPalette(mypalette);
this->setAutoFillBackground(true);
}
あと、色を変えるためにパレットをいじったりしているが、基本的にはsetMaskするだけ。
さて、この自作ウイジェットをフォームに貼り付けよう。
と考えるのはもっともな話でどうやって貼ろうか僕も考えたものです。
デザイナーから自作ウイジェットを貼る方法は少々複雑です。
その自作ウイジェットの基底クラスのウイジェットを配置して、それを格上げするという方法でやります。
その配置したウイジェットで右クリックして、「格上げ先を指定」で設定するわけです。
新しい格上げされたクラスでCircleクラスを追加して、その追加されたのを選らんで格上げとすればいいわけです。
見た目は何も変わりませんが、これでCircleクラスのウイジェットに化けたわけです。
これで実行してみると赤い丸が見えるはず。
こんな丸いもん作ってどうすると思う人もあるかも知れない。
けど画像として丸を表示するよりも良い点もあるわけです。
例えば丸の部分だけがクリックに反応したり。
なかなか面白いのでいろいろ試してみてください。
Author : hidemaro
Date : 2009/10/28(Wed) 23:41
C・C++ | comments (0) | trackback (0)
Qtでシグナル・スロットつないでUDP
目的はUDPを使ったプログラムを作るため。
UDPってブロードキャストできるから楽しそうですね。ということで遊んでみるために作っている。
GUIでやりたくて、せっかくC++やってきたんだからQtかなと思ってQtでやっている。
QtにはQUdpSocketというのがあるので、これを使えばさほど苦労せずに作れる。
とはいえどうやってやればいいかはなかなかわからんのでサンプル見た。
それを参考にUDPAgentクラスを作ってみた。
まずヘッダファイル。
#include <QObject>ところでQtのシグナルとスロットってのはご存じですかね?
#include <QString>
#include <QtNetwork>
class UDPAgent : public QObject{
Q_OBJECT
private:
QUdpSocket udp;
private slots:
void _recive(void);
public slots:
void SendBloadcast(const QString& data);
signals:
void Recive(const QString& data);
public:
UDPAgent(void);
};
シグナル言うのはなにかあったら発生するもの。
スロット言うのはメソッドで、シグナルに接続することのできるもの。
シグナルはクラスの定義の時に signals: 以下に定義します。
定義だけして実装はしないわけですね。この辺はQtは勝手に実装してくれると。
スロットは public slots: か private slots: 以下に定義して、内容も他のメソッド同様に実装すると。
そういうことを踏まえてみて欲しい。
なんか受信したらReciveというシグナルが発生して、なにか送りたければSendBloadcastというメソッドを実行すればよいと。
そういう設計になっています。
ソースコードですが、こんな感じ。
#include "udpagent.h"コンストラクタで、udpが受信できるようになったときに発生するreadyReadシグナルと、privateなスロット_reciveを接続しています。
UDPAgent::UDPAgent(void):udp(this){
connect(&this->udp,SIGNAL(readyRead()),this,SLOT(_recive()));
udp->bind(12345);
}
void UDPAgent::_recive(void){
QTextCodec* euc=QTextCodec::codecForName("EUC-JP");
QTextDecoder eucdec(euc);
while(udp.hasPendingDatagrams()){
QByteArray datagram;
datagram.resize(udp.pendingDatagramSize());
udp.readDatagram(datagram.data(),datagram.size());
this->Recive(eucdec.toUnicode(datagram));
}
}
void UDPAgent::SendBloadcast(const QString& data){
QUdpSocket udp;
QTextCodec* euc=QTextCodec::codecForName("EUC-JP");
QTextEncoder eucenc(euc);
this->Debug(QString("Send : %1").arg(data));
QByteArray bytedata=eucenc.fromUnicode(data);
udp.writeDatagram(bytedata,QHostAddress::Broadcast,12345);
}
こういう風にして接続することを書けるんですね。
Qtではこういう風にシグナルとスロットの接続で書くことが多いですね。
_reciveでは受信の処理がいろいろ書いてあります。
データはEUC-JPでエンコードされているはずなのでデコードしてReciveシグナルを発生させると。
シグナルを発生させるのはシグナルのメソッドを実行するだけです。
SendBloadcastメソッドは受信処理とは全く別になっていて、
文字列をEUC-JPでエンコードしてブロードキャストで送信しているだけです。
これでシグナルをなんしかのスロットに接続したりすれば使えるわけですね。
こういう風にQtは使うわけですよ。
ところでさっきEUC-JPでエンコードしたりデコードしたり書きましたが、実はこうなったことには事情があります。
いや、元々はUTF-8だったんですよ。けどちょっと問題がありましてEUC-JPになったわけです。
その問題というのはBOMです。
BOMというのはUTF-16・UTF-32において、ビックエンディアンかリトルエンディアンかを判別するために最初に置く特殊な文字。
ただUTF-8ではその判別はいらないのでBOMはなくてもいいはず。
だけどUTF-16・UTF-32同様にBOMを置くという流儀もあったりする。UTF-8である印になるからとかなんとか。
けど置かないという流儀もある。むしろそれが基本のはず。
BOMさえ置かなければUTF-8というのはASCIIの範囲内ならばASCIIとの互換性があるんですね。
だから置くとこの互換性がなくなって都合が悪いと。そういう考えもあります。
日本では特に付けない方を UTF-8N と表記してあることがあります。TeraPadとか。正式な表記じゃないのだけどね。
ただ、そういうわけでもなく単にUTF-8とあればBOM付きかBOMなしか分からないんですね。
それでQtにエンコードさせたらBOMが付いてエンコードされたわけです。
ただQtにデコードされたらBOMをBOMと認識せずにU+FEFFの文字が一番最初に残ってしまったんですよ。
それでバグが発生していて困ってね。
バグを消すためにUTF-8から日本語に限ればまぁ無難なEUC-JPに変えてみました。
ちょっとこの辺は残念なところなのだが、別にEUC-JPでも問題はないからこれで行こうと思う。
Author : hidemaro
Date : 2009/10/27(Tue) 23:47
C・C++ | comments (0) | trackback (0)
Windowsでカーソル動かすCUIで
まぁなにかと疲れていたのだろう。
以前VT-100のエスケープシーケンスでいろいろやっていたことがあったのだが、
ふと使いたくなってWindowsで試していた。ところがうまくいかない。
以前はLinuxでやっていたのだが、Windows 2000,Windows XPでも使えるはずなんだけどな。
実はどうもANSI.SYSを標準で読み込んでいないかららしい。
まぁそういう事情は知らなかったので、一体どうすれば実現できるのかなとうんうんうなっていたが、
どうもWin32APIにそういう関数があったので、これを利用してやってみた。
そういうお話。
調べるとカーソル移動はSetConsoleCursorPosition関数というのを使えばいいらしい。
#include <iostream>基本的にはC++のプログラムなのですが、Win32APIはCなので所々Cっぽいですね。
#include <cmath>
#include <Windows.h>
int main(void){
HANDLE out=GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(out,&info);
std::cout << info.srWindow.Right << std::endl;
for(double t=0;t<=3.141592*2;t+=0.1){
COORD pos;
pos.X=info.srWindow.Right-(1.0+std::cos(t))*20;
pos.Y=info.srWindow.Top+(1.0+std::sin(t))*10;
SetConsoleCursorPosition(out,pos);
std::cout << "*";
}
SetConsoleCursorPosition(out,info.dwCursorPosition);
return 0;
}
やたら構造体が多いので複雑なんですわ。
COORD構造体にxy座標をセットしてSetConsoleCursorPositionでセットするだけですね。
ただ、そのためには画面の大きさの情報がいるので、これはGetConsoleScreenBufferInfoで得ている。
最後はここにそれを呼び出したときのカーソル位置が入っているので戻しているわけです。
これでなにができるかというとCUIのプログラムのデバッグ時にコンソールの右上にデバッグ情報を表示できると。
これでXをinfo.srWindow.Right-10、Yをpos.Y=info.srWindow.Topなんかにセットして表示すれば楽しそうですね。
こういうことをしたかったわけです。
他にもいろいろできそうですね。
まぁ問題はWin32の流儀がようわからんのでMSDNもよう読まんことだが。
Author : hidemaro
Date : 2009/10/19(Mon) 23:58
C・C++ | comments (0) | trackback (0)
破壊的コピーは速かった、unique_ptrも速かった
それはC++0xで導入された右辺値参照の性能について。(参考記事 : C++0xの化け物じみた右辺値参照)
これは結構重要なことです。
C++のメモリの確保の仕方にはスタックとヒープがあって、というのはCと同じことですね。
int x[100];どちらも大きさ100のint型配列を作っているが作っている場所が違うというのがポイント。
int* y=new int[100]; //後でdelete[] y;をお忘れ無く
前者は静的に作っているので、これは作られる場所はスタックです。
スタックは言うたらスコープの中のメモ用紙みたいなもんですわ。
なのでスコープを出ると破棄されちゃいます。が、確保は超高速で気にする必要なし。
一方、ヒープはスコープを出ても破棄されない、これは破棄は自分で行うから。
その一方でメモリの確保・破棄はちょっとコストがかかる。これが問題。
そのため僕がCを授業で習ったときはヒープからの確保はやらなかったわけだ。
まぁこれは正解といえば正解。Cならそれでもいいんちゃいますかね。
Cならば、スコープ内で用済みならばスタック、そうじゃなかったり動的に配列を確保したいときはヒープと。
こういう役割分担でOKなわけですが、C++ではちょっと事情が変わってくる。
例えばCで返り値が配列なときはこうすることになるかな。
int* fib(int len){
int i; int* r;
r=calloc(sizeof(int),len);
if(len>0) r[0]=0;
if(len>1) r[1]=1;
for(i=2;i<len;i++) r[i]=r[i-1]+r[i-2];
return r;
}返り値を受けた方は適当なところでfreeしなければならないので好きじゃない。ところが、C++ではstd::vectorという便利なものがあるのでこういう手もある。
struct fibgen{
int x,y;
fibgen(void):x(0),y(1){}
int operator()(void){ int r=x; x=y; y=x+r; return r; }
};
std::vector<int> fib(int len){
std::vector<int> r(len);
std::generate(r.begin(),r.end(),fibgen());
return r;
}なんかどうでもいいところに技巧を凝らしてしまったが、std::vectorのオブジェクト自体を返り値にしている。このオブジェクトはスタック上に構成されている。
まぁ配列の実体はヒープ上に取ってあるんだけど、それは気にしなくていい。
返り値を受けたらどうするのかというとコピーコンストラクタでこのオブジェクトを丸ごとコピーする。
巨大な配列を丸ごとコピーするのって大変だよねってのは僕がC++始めたときから思ってたこと。
なので僕はヒープ上に作ってポインタをboost::shared_ptrに入れてから返すようにとかしていた。
これならばコピーしなくて済むからコピーが大変なオブジェクトでも安心だと。
ただしヒープからメモリを取ってくると言うのは結構な負荷じゃないのとも思っていたわけだ。
一体どっちの方が速いかは疑問だった。
そしてC++0xでは右辺値参照が入る見込み。
これを使えば破壊的コピーで高速にコピーできるようになるはず。
std::vectorの場合だったら配列の実体はヒープ上にあるからこれをひったくってくるんだろう。
もしこの仕組みがあるのならコピーするのが大変だったオブジェクトも随分軽くなるのではと。
まぁしかし実際どれぐらい性能に差があるのかというのはようわからんから実験してみた。
'H'が100個連続した文字列を表すstd::stringを50万回作ったりしてみる実験をした。
実験したのは
スタック上に構成した場合
スタック上に構成してスタック上に破壊的コピーした場合
スタック上に構成してスタック上に普通にコピーした場合、
ヒープ上に構成した場合
ヒープ上に構成してヒープ上に破壊的コピーした場合
ヒープ上に構成してヒープ上に普通にコピーした場合
まぁ最後の2つは実用的じゃないが試しにやってみた。
その結果は、上から順番に375ms,469ms,953ms,828ms,1250ms,2922ms
いままではスタックに構成してスタックに普通にコピーするか、ヒープに構成してポインタで返すかだったわけだ。
それで比較してみると953msと828msだからわずかにヒープに構成してポインタで返した方が早い。
まぁしかしこれほどの長さの文字列でもこの程度なら正直いらんかもしれんな。
文字列が何個も詰まったstd::vectorとかでもない限り意味は無いかも。
一方、破壊的コピーした場合はなんと469ms、これはすごく速い。
C++0xの時代が来たらわざわざポインタで返す必要はないかもね。
もう1つ確かめておきたいことがあった。
それはスマートポインタの速度。
std::shared_ptrとstd::unique_ptrと生ポインタ、これを1つ構成したときとコピー・移動して20個構成するのを50万回やった。
なお、いずれも初めにさっきと同じオブジェクトを作っている。
ポインタの個数1つの場合、前から順番に、1266ms,828ms,828msとなっている。
まずshared_ptrは結構遅い。これだけ参照ポインタだから複雑だもんね。
一方、unique_ptrと生ポインタは大した差はない。ちなみに生ポインタは手動でdeleteする時間も入れてるよ。
まぁだから結局同じことを書いてやっているか、書かずにやっているかの違いですね。
ポインタの個数20個の場合、前から順番に、1594ms,828ms,828msとなっている。
まぁ生ポインタはただの数字のコピーを繰り返しているだけだから差がないのはあたりまえだが、
unique_ptrが変わらないのは意外だった。
多分だがデストラクタを呼ぶのは大したコストじゃないんだろう。
そしてそのデストラクタではすでに移動されていれば何にもしないだけなので簡単だと。
そういうことじゃないかな。
一方shared_ptrは時間が延びている。これは最後の1個以外でもデストラクタは複雑だから。
まぁあたりまえですね。
このことからC++0xではポインタはunique_ptrで包んで返り値にするのがなにかとよさそうだということだな。
受け取った方がshared_ptrに詰めたいならばreleaseすればいいだけだもんね。
それにしても右辺値参照強いなぁ。
こういうのを見ると右辺値参照に対応しているコンパイラでコンパイルしたくなるよね。
けど、全体的には微々たる差じゃないかなとも思った。
というのはやっぱりC++ってそもそも速いから。
多少無駄なコピーがあってもJavaやC#で作るよりは速いでしょう。GCないですしね。
というかあいつらは基本的にはヒープからしかメモリを確保できないから。
そういう意味では高速なスタックからのメモリ確保を選択できるC++はおいしいのかも。
C++0xの時代には使い勝手を中心に選んでいきたいところですね。
Author : hidemaro
Date : 2009/09/05(Sat) 22:50
C・C++ | comments (0) | trackback (0)
キーが押されるまで待ってみよう
そんな中で以下のことを実現したかった。
ある部分を実行中にqを押せば終了し、押されなければ最後まで実行して終了する。
しかしこれが非常に難しかった。
C++の標準ライブラリのstd::cinから読み込めると思ったがそうはいかない。
実際にはPOSIX関数由来 kbhit()とgetch()を使うことに。
これとboost::threadを組み合わせて作ってみた。
#include <iostream>100msまで終了待機して、それで終了しなければkbhit()でキー入力を確認し、されていたらキーをgetch()で読み取ると。
#include <boost/thread.hpp>
#include <boost/ref.hpp>
#include <conio.h>
class Stoppable{
private:
boost::thread thread;
public:
Stoppable(void):thread(boost::ref(*this)){}
void operator()(void){
for(int i=0;i<10;i++){
boost::thread::sleep(boost::get_system_time()+boost::posix_time::milliseconds(1000));
std::cout << "==" << i << "==" << std::endl;
}
}
bool Join(void){
while(1){
if(this->thread.timed_join(boost::get_system_time()+boost::posix_time::milliseconds(100))) return true;
if(kbhit()&&getch()=='q'){
this->thread.interrupt();
return false;
}
}
}
};
こういう要領になっています。
これでJoinメソッドを動かせば、終了かqを待機するわけだが、これには問題があることが分かった。
オブジェクトを破棄するとスレッドも停止されると思い込んでたんだが、そうもいかないらしい。
なのでオブジェクトが破棄されてもスレッドは動き続けたままだった。
どうすればいいのかはよくわからなかったんだが、interruptすればスレッドが止まるらしい。
というわけでthis->thread.interrupt();っていうのを書き足しておいた。
これで終了時には止まっている。
多分これで大丈夫。
これで途中中断機能がつけられた。やったー。
まぁやってること自体は簡単なことなんですけどねぇ。
元々boost::condition_variableがいるかと思ったんだがいらんかったわ。
ちなみにこれの使い方はこんな感じ。
boost::condition_variable stop;なんで待機する方にmutexが必要なのかはよくわからないが、まぁこうすればOK。
//別のスレッド
stop.notify_all();
//待機するメソッド
boost::mutex mutex;
boost::mutex::scoped_lock lock(mutex);
stop.wait(lock);
あとtimed_waitとかもあるのでなかなか楽しそう。
だが結果的には使わなくても済んでしまった。
やっぱりマルチスレッドってのは大切だと思いましたね。
しょうもないことだけどマルチレッドなしではどうにもならないってことはやっぱりあるから。
そういうときBoostがあって本当によかったなと思わせてくれますね。
Author : hidemaro
Date : 2009/09/03(Thu) 23:37
C・C++ | comments (0) | trackback (0)
ようこそQt世界へ、Qtに染まりましょう
そんな中、今日は部活に出かけていた。まぁ部室に買ったものを置きに来ただけですが。
今日はそれだけだったのですが、どうも聞けば夏休み前なので宿題で忙しくて活動する人がいないらしい。
まぁ確かに宿題が残ってたらつらいわ。
今年は僕は余裕があったので早く終わったけど、夏休み後半は宿題で大変ですわ。
C++でGUIといえばQtらしい。
というのをKDE好きの友人から聞いた気がする。
もっとも彼が言いたかったのは、XといえばKDE、KDEといえばQt、QtといえばC++みたいなことだったが。
まぁしかしC++のライブラリとしてはQtは非常に有名なので。
というわけでQt SDKをダウンロード。
Qt - Download
ここでLGPL/FreeのQt SDKをダウンロードしてインストール。
ここで気付いたんだが、QtってNokiaが作ってたのか。知らんかった。
できたらQt Creatorを起動する。そしてQt4 GUIライブラリを選択。
ここでmainwindow.uiファイルを選ぶと、グラフィカルな編集画面が出てくる。
これでフォームのデザインをする。
Horizonal SliderとLabelとLCD Numberを配置する。
それぞれ名前はslider,label1,lcdとしておきましょうか。これはプロパティのobjectNameで決められる。
あとLCDはsegmentStyleをflatとかにしておいた方が見やすいね。
とりあえずこれでビルドして実行してみると画面が表示される。
まずQtの第一歩としてはこれでOKです。もちろんこれでは何にも出来ませんが。
さて、ここからがQtの世界なのだが、どうもQtのオブジェクトは全部QObjectを継承しているらしい。
そのQObjectにはシグナルとスロットという機能がある。
VBやらC#の言葉を使えば、シグナルはイベントだ、そしてスロットはイベントに登録するメソッドといったところ。
Qtではシグナルとスロットをつなげて使う。これが特徴。
VBやC#ではメソッドを用意してイベントに登録する。まぁ大して変わらんわな。
しかしQt CreatorというIDEはおもしろい。
この編集画面の上の方にアイコンがたくさんならんでいます。
左からウイジェットの編集・シグナル/スロットの編集……という風なアイコンがならんでいる。
この左から2つ目を選んでみる。そしたら配置してあるウイジェットの上にカーソルを乗せると赤くなる。
これは何をする画面なのかというと、シグナルとスロットをつなぐ画面。
というわけで実践だ。
sliderからドラッグし始めてlcdで放す。するとシグナル/スロット接続を設定という画面が出てくる。
ここでシグナルはvalueChanged(int)を選ぶ。
そしたらこれと同じ引数、または引数を減らしたスロットにつなぐことになる。
ちょうどいいことにdisplay(int)というスロットがあるのでここに接続する。
これでOKする。これで接続完了。実行してみる。
するとスライダーの値を変えていったらいちいち反映される。こりゃすばらしい。
もっともこんな風に毎度毎度ちょうどいいスロットがあるわけでもないですけどね。
今回はlabel1の値を"Value is 10."のように変えたいとする。さすがにlabelにそんな機能はない。
さて、どうするか。こういうときはドラッグ先を何もないところにする。
すると電気用図記号のグランドマークが出てくる。これは地面、すなわちMainWindowにつなぐということ。
シグナルはvalueChanged(int)を選ぶが、つなぐさきがない。そこで編集をクリック。
そしてSlots側で+を選んでスロットを追加する。ここではgetSlider(int)というスロットを追加した。
そしたらこれを選んでOK。これで接続できた。
あとは接続先を具体的に書けばOK。ここで初めてコードを書くことになる。
まずmainwindow.hを選んでclass MainWindowの定義に書き足す。
class MainWindow : public QMainWindow{
//以下を書き足す
private slots:
void getSlider(int value);
};private slots:以下を書き足すことになります。あれ? private:は知ってるが、private slots:ってなんだ?
実はこれはQtの拡張です。実はQtはこれをmocってやつでなんかコンパイルできるコードに変換しているらしい。
まぁこういうのを見ると本当にQtってQtの世界を作ってるよなぁと思わされる。
とりあえずこれで宣言は出来たので、内容をmainwindow.cppに書く。
void MainWindow::getSlider(int value){
this->ui->label1->setText(QString("Value is %1").arg(value));
}uiの下に今回登録したウイジェットのオブジェクトのポインタが入っている。ここからlabel1を探して、これのsetTextメソッドを呼び出せばいい。
ここでテキストを設定するわけだが、実はこのテキストがstd::stringとかじゃないというのが厄介。
Qtはなんか自分でいろんなライブラリを用意しているらしい。
Qt 4.5 リファレンス
ここで主要なクラスを見ると、ずいぶんたくさんある。なんとstd::vector相当のものとしてQListとかいうのもある。
この中でQStringというのを探す。いろいろな機能がある。
std::stringよりも多機能なのは間違えなさそうでargメソッドを使えばさっきやりたかったことも簡単にできる。
これでOK。これでビルドして実行すると、ラベルも変わっていきますね。
まぁなんというかQt自体がいろいろなものを持っているのでなかなかおもしろい。
例えばネットワーク系のライブラリもQtにありますね。これは便利そう。
もちろんSTLやBoostと組み合わせて使うのは全く問題ない。
が、なんとなくいらない気がする。QSharedPointerがboost::shared_ptrの代わりになりそうだし。
なんかQtで作るプログラムはQtに染まってしまいそうだ。
まぁしかしいろんな考え方があるでしょう。Qtなしで作った部分とQt部分を組み合わせてつかうだとか。
その辺はC++なので柔軟にできるんじゃないかなと思います。まだ調べてないけど。
Author : hidemaro
Date : 2009/08/31(Mon) 23:23
C・C++ | comments (0) | trackback (0)
BoostとOpenSSLで作るHTTPSクライアント
そしたらとりあえずHTTPSでつなげるということなので調べて実際にやってみた。
とはいえ資料がちぐはぐで困ったものですが。
それにあわせて以前作っていたライブラリを大幅に書き換えてみた。
せっかくなのでできたソースコードとDoxygenで作ったライブラリを置いておく。
HTTPClient.tar.bz2 (36MB)
しかしboostつかってるからってboost::shared_ptr使いすぎだろという印象はうけるかもしれない。
理由は2つありまして、1つは継承を使っていろいろやっているから。(参考記事 : 継承したらポインタ使ってnewしないと代入できないぞ)
もう1つはコピーするのがいやだから。文字列いちいちコピーしてたら大変でしょ。
C++0xなら前者はunique_ptr、後者は右辺値参照に期待して生で渡すとかもありだったかもしれん。
使い方はdoxygen以下のドキュメントに書いてあるがこんな風に使う。
boost::shared_ptr<Hidemaro::HTTPClient> http=Hidemaro::HTTPClient::Create("http://www.example.com/foobar");
http->Request();
std::istream &resstream=http->GetResponceStream();
while(!resstream.eof()){
std::string line;
std::getline(resstream,line);
std::cout<< line << std::endl;
}楽チンですね。結果はストリームで得られるのでファイル入力と同様に扱えるだろう。しかもhttps://~ とすれば一応はアクセス出来る。
証明書の検証とかしてないから本当はまずいんだが、シンプルにアクセス出来るようにした。
証明書の検証の方法はまた調べるわ。今のところは証明書無視でやっている。
さらに便利なのはテストでローカルのファイルを読みたいときもあるかも知れない。
そんなときfile:///c:/work/test.txtのようにすれば読める。
これは以前.NETのSystem.Net.WebRequestでfileスキームが使えて便利だったので自分でも実現してみた。
(参考記事 : 日陰のas演算子は支えてくれた)
HTTPSを使うにはboost/asio/ssl.hppをincludeすればよいのだが、OpenSSLが必要。
というわけでインストール。Shining Light Productions - Win32 OpenSSL
ここからLiteじゃないやつをダウンロードしてインストール。
その後インストールしたディレクトリのincludeをインクルードディレクトリに設定。
lib\VCとlib\VC\staticをライブラリディレクトリに設定。
これだけで足りるかと思ったんだがライブラリファイルは手動で組み込む必要があるらしい。
Microsoftのコンパイラの場合はこう書いておけばいい。
#pragma comment(lib, "libeay32MDd.lib")便利ですね。他のコンパイラでもコンパイラオプションで設定すれば大丈夫なはず。
#pragma comment(lib, "ssleay32MDd.lib")
めんどくさいのでこれはさっきのファイルの中には仕込んである。
ところで、このソースコードはヘッダファイルこそまぁきれいなのだが、ソースは恐ろしいものになっていると思う。
まずHTTPClientを継承したクラスの宣言がつらつらと書かれています。
が、外からこのクラスたちを知ることは出来ないんですね。privateなクラスがならんでいるようなもんですよ。
その次にIClientCreatorというクラスをそれを継承したClientCreator<T>というクラスがある。
これはTクラスのインスタンスを作るクラスです。なんじゃそりゃという感じですよね。
std::shared_ptr<IClientCreator> creator(new ClientCreator<HTTPClientImpl>(80));ここでHTTPClientImplをHTTPSClientに変えても動く。
std::shared_ptr<HTTPClient> client=creator->Create("libserver.ddo.jp",0,"/foo",false);
なにがしたかったのかというと、HTTPClientImplのインスタンスを作るオブジェクトとかをstd::mapに投げ込みたかったんですよ。
そのための工夫です。C++のテンプレート様々ですね。
mapの内容を書き換えればなんか別のスキームも追加できるかも知れない。
というなかなかおもしろい設計になっています。
あとはBase64変換とURLエンコード用の関数がありますね。
自分でHTTPのクライアントを組み込もうとか言うのなら参考になるんじゃないかなとは思う。
boost::asio::ip::tcpの資料ってそんなには多くないのよね。
それがつらいよね。あとHTTPの具体的な通信方法を知らないと実装できないし。
とはいえBoostなかったらWinSockとかSocketを直接操作しないといけないので移植とかむりだしね。
Boost使うのが無難だとは思いますよ。そんなに変わらないかも知れないけど。
Author : hidemaro
Date : 2009/08/28(Fri) 19:30
C・C++ | comments (0) | trackback (0)
auto_ptrはunique_ptrになっていた
まぁ今はC++ばっかり使っているのでC++0xの実験だけでいいじゃないとは思った。
いやね、C# 4.0ってそんなに魅力的な拡張ってないのよね。dynamicとか僕はあんまり使わないだろうし。
ライブラリの拡張があるかもしれんが、まぁその辺は情報持ってないからね。
++C++にはあんまりそういうネタはないのですが、まぁちょっとずつ調べていくか。
前にいろいろ試していたとき、boost::scoped_ptrは編入されなかったのかと思っていたのですが、
実はstd::auto_ptrと合併してstd::unique_ptrとして新しく追加されていることに気付きました。
これに伴い、評判のよろしくなかったstd::auto_ptrは非推奨となるようです。
結構いろいろなことができそうです。
まず、boost::scoped_ptr的な側面を辿ってみた。
#include <iostream>破棄などの状況が見やすいようにMyObjとかいうクラスを用意してみた。
#include <memory>
#include <string>
class MyObj{
public:
int ID;
MyObj(int id):ID(id){
std::cout << "Create MyObj : " << this->ID << std::endl;
}
MyObj(const MyObj& other):ID(other.ID){
std::cout << "Copy MyObj : " << this->ID << std::endl;
}
MyObj(MyObj&& other):ID(other.ID){
std::cout << "Move MyObj : " << this->ID << std::endl;
}
~MyObj(void){
std::cout << "Destroy MyObj : " << this->ID << std::endl;
}
};
int main(void){
{
std::unique_ptr<MyObj> x(new MyObj(10));
std::unique_ptr<MyObj> y;
y.reset(new MyObj(20));
x.swap(y);
y.reset(new MyObj(30));
std::cout << "x->ID : " << x->ID << std::endl;
std::cout << "y->ID : " << y->ID << std::endl;
}
{char dummy; std::cin >> dummy;}
return 0;
}
そういえば、Visual Studio以外ではmainの返り値ってvoidは許されないんだね。
GCCでそうだったわけですが、intとするのが無難ですよね。
scoped_ptrはswapとresetによりさすポインタを操作できるんですね。
実はこの機能があるのでスコープ内で完結するならばstd::auto_ptrと同等の能力はあったわけですよ。
今回はyを後でresetで設定して、xとyを入れ替えて、yを新しいオブジェクトでresetしてるので、
xはnew MyObj(20)で作ったオブジェクト、yはnew MyObj(30)で作ったオブジェクト、new MyObj(10)のオブジェクトはreset時消えた。
ということが実行して理解できるはず。
けどboost::scoped_ptrは一切のコピーコンストラクタを持たないから返り値にはできない。
あと、所有権を放棄するreleaseとかはない。別にscoped_ptrならあってもよかったと思うけど。
だからこういうことすらできない。
MyObj* Create(void){
boost::scoped_ptr<MyObj> p(new MyObj(20));
return p.release();
}この辺がstd::auto_ptrとの違いで棲み分けだったわけです。それができるstd::auto_ptrは破壊的コピーするとかいう問題もあったと。
実は破壊的コピーの問題について解決する方法が見つかったというのがこのunique_ptrのきっかけじゃないかなと。
というのは右辺値参照のことですね。(参考記事 : C++0xの化け物じみた右辺値参照)
これを使えば、関数の返り値など右辺値は破壊的コピーができて、
そして左辺値であっても std::move() にかぶせれば破壊的コピーができるわけです。
これ使えるぞと思ったんでしょう。その結果std::unique_ptrが誕生したということになります。
std::unique_ptr<MyObj> Create(void){
std::unique_ptr<MyObj> p(new MyObj(20));
return p;
}
//本体
std::unique_ptr<MyObj> x=Create();
std::unique_ptr<MyObj> y=std::move(x);
if(x==0) std::cout << "x is null" << std::endl;
std::cout << "y->ID : " << y->ID << std::endl;移動がはっきりするようになりましたし、swapとかresetとかもあるし便利ですね。なお、破壊的以外のコピーができないのは相変わらずです。
基本的にはコピー不可だけど、右辺値またはstd::move()をかぶせた場合は可ということです。
なのでもちろんstd::vectorには入れられません。
しかしこういうときはstd::shared_ptrを使うので問題ありませんね。
Author : hidemaro
Date : 2009/08/22(Sat) 12:57
C・C++ | comments (0) | trackback (0)
可変長引数テンプレートを使えば安全な可変長引数が!
それがなんとC++0xになって初めて搭載されると言うことだ。
ただちょっと腑に落ちないところもある。
まぁとりあえず試してみた。
どうも今C++0xを試すならばGCC 4だということだ。
というわけでCygwin導入。ちゃんとgcc-4ってのがあるのでそれを選らんでインストール。
それでこんなソースコードを書いてみた。
#include <iostream>さて、これをコンパイルするわけだがとにかくgccには慣れていない僕はgccコマンドでコンパイルしたがエラー。
int sum(){}
template <typename... Types>
int sum(int x,Types... args){
return x+sum(args...);
}
int main(void){
std::cout << sum(1,2,3) << std::endl;
return 0;
}
どうもg++コマンドをつかうべきらしい。
そういえばgccってGnu Compiler Collectionの略なんだよね。
Cのコンパイラはgccコマンド、じゃあC++はg++と。
というわけでこうすればOK。
$ g++-4 -std=c++0x -o test test.cppところがエラー発生。どうもCygwin同梱版の問題らしいが……こうすれば解決。
$ g++-4 -std=c++0x -Wl,--enable-auto-import -o test test.cppとりあえずコンパイルしたら ./test で実行。
すると9と表示された。やったね!
これはC++0xの新機能、可変長テンプレート引数によるもの。
可変長テンプレート引数は再帰でのみ解決できるのでこのような方法を取っています。
Typesってのが型の集合。これを引数に入れるときにはTypes...と書く。
それでもって引数の集合はargs、これを引数とするときはargs... と書く。
ここで注意しなければならないのは集合をばらそうとは考えないこと。
再帰で解決することになっているらしい。
まぁしかしながら、型セーフだし、個数もわかるのは大きいよね。
個数はsizeof...(Types)のようにすればわかる。それに再帰の回数をカウントしてもいいよね。
ところで今回はこういうことはできないんかね。
int sum(int x,int... args){
return x+sum(args...);
}なんでintしか引数に取らないのにわざわざ型引数を取る必要があるんだろう。型引数の長さで引数の数を指定することになるのかな。
ちなみにint以外のものを引数に入れても、引数を1つずつ減らしていったときに該当する関数が無くなるのでコンパイル時に死ぬ。
なのでC++お得意の意味不明なエラーで困ることになる。
まぁそれは仕方ない。
可変長引数というのはCからある機能ではあるのですが、怖い機能でもありました。
なのでC++ではどちらかというと必要な長さまでいちいち関数を書くということをやっている人が多かった。
かのBoostもそうだ。けどここで初めてできるようになったと。
まぁ扱いは残念ながらいいとは言えませんけどね。だって配列で受けるとかできないわけだし。
けど工夫次第ではなんとでも。
#include <iostream>std::vectorに再帰的に詰めて、そして最後にstd::vectorだけになったら実際やりたいことをやると。
#include <vector>
int _sum(const std::vector<int>& list){
int sum;
std::vector<int>::const_iterator end=list.end();
for(std::vector<int>::const_iterator p=list.begin();p!=end;++p){
sum+=*p;
}
return sum;
}
template <typename... Types>
int _sum(std::vector<int>& list,int x,Types... args){
list.push_back(x);
return _sum(list,args...);
}
template <typename... Types>
int sum(Types... args){
std::vector<int> list;
list.reserve(sizeof...(Types));
return _sum(list,args...);
}
これも工夫。C++は工夫しないとなかなかうまくいかないけど工夫すればなんとでも。
Author : hidemaro
Date : 2009/08/21(Fri) 23:54
C・C++ | comments (0) | trackback (0)