std::forward_listのsizeとか
Policyを使って単純に実装するとこうなるのか.
# そもそも直交してるのか?
#include <iostream> #include <list> template< typename NumericT > class size_counter { // サイズをカウントするポリシークラス NumericT size_; protected: void increment(){ ++size_; } void decrement(){ --size_; } public: size_counter(): size_(0){ } NumericT size() const{ return size_; } }; template< typename NumericT > class non_size_counter { // サイズをカウントしないポリシークラス protected: void increment(){} void decrement(){} public: non_size_counter(){} }; // std::forward_listのシンプル版 // std::forward_listにpush_backはないけど template< typename ValueT, class SizeCounter = non_size_counter<std::size_t> > class forward_list : public SizeCounter { typedef ValueT value_type; std::list<value_type> data_; public: void push_back(const value_type& value){ data_.push_back(value); SizeCounter::increment(); } void pop_back(){ data_.pop_back(); SizeCounter::decrement(); } }; int main(){ template< template< typename > class SizeCounter > using int_list = forward_list<int, SizeCounter<std::size_t> >; // sizeがある int_list<size_counter> have_size; // sizeがない int_list<non_siz_counter> not_have_size; std::cout << have_size.size() << std::endl; // OK std::cout << not_have_size.size() << std::endl; // コンパイルエラー }
でもSTLで実装しようとすると当然こんな単純にいくはずはないです.
ゼロオーバーヘッドって保障できないですしね.
これじゃ正直要らないですねぇ.
std::forward_listにサイズがない
※見習いプログラマの意見です.全てのこの意見に対して疑ってください(安易に信じないでください).
std::forward_listとはC++0xに実装される単方向リストです.
C++0x forward_list - Faith and Brave - C++で遊ぼう
これはサイズを返すメンバ関数は持たないそうです.
理由は上記リンク先の記事にありますが,自分は正直2番で実装しても良いんじゃないかと思いますね.
1.確かに標準で実装してしまった場合,『クライアントがsizeが無くても良いから速く・小さいのが欲しい』と思った場合は標準を使えません.
2.標準で実装しなかった場合でクライアントがsizeが欲しい場合は,別途実装すればいいので標準を使えます.
// 2.の例 std::forward_list<int> slist; std::size_t slist_size; // CAUTION!! これはslistのサイズだから必ず更新してね! // こうしなくてもstd::forward_listをラップしてしまえば確実ですけど,上の例よりは面倒です.
でも,もしクライアントが1の様に『たった4バイトでも節約したい.pushやpop時のインクリメント・デクリメントのコストすら消したい』と思うなら,標準のstd::forward_listなんて使わないで自作すると思うんですよね.
(1リストにつき)4バイトのメモリと追加・削除のインクリメント・デクリメントが無視できないほどのオーバーヘッドになる可能性というのは殆ど無くないですか(STLを使う規模のプログラムで(組込みとかは除いて)).
# 結構あるんだったらすみません.
# サイズの確保が4バイトとは限らないですがw
方針が『Cの実装と比べてゼロオーバーヘッドを目指す』なので実装しないという選択もありだと思いますが,ちょっとオーバーじゃないのかなという感想です.
2.の場合でも結構コストがかかります.
上の例のようにする場合は
std::forward_list<int> slist; std::size_t size; // どこかで assert(std::distance(slist.begin(), slist.end()) == size); // チェック! // なんてすればReleaseでO(N)のコストがかからなかったり(でも完璧じゃないですが).
ラップクラスを作成する場合は,全部のメンバ関数に対して転送関数を定義とかしますからね.
でもC++0xでの新機能とか使えば簡単にできるのかな.
というか既存のC++の技術でも簡単にできたりしてw
●まとめ
1の場合が発生する確立×1の場合が発生した場合のコスト<2の場合が発生する確率×2の場合が発生したコスト
上式のように自分は思いました.
『1の場合が発生した場合のコスト』が結構でかいと思いますが,そういう場合はどっちにしろ自作しなきゃならないと思うので.
でもWriteGreatCodeとか読むと考えが変わるかもしれないです.
読みたい.
コーディング規約を
真面目にコーディング規約を作ろうかと思います.
前に書いたやつは書きなぐりというか,その程度のものだったので.
でも一般的にはどのようなものが使われているのですかね.
ちょっと後で探してみようかしら.
Observer
で,何とか実装してみました.
#include <list> #include <algorithm> #include <assert.h> #include <iostream> class subject; // // Observerクラス // class observer { protected: observer(){ } public: virtual ~observer(){} // 変更があった時(notify)subjectから呼ばれる virtual void subject_update(subject* sub) = 0; // observerが登録された時,登録したsubjectから呼ばれる virtual void subject_set(subject* sub) = 0; // observerが解除された時,解除したsubjectから呼ばれる virtual void subject_unset(subject* sub) = 0; }; // // Subjectクラス // class subject { protected: std::list<observer*> observers_; subject(){} public: virtual ~subject(){} virtual void attach(observer* obs){ // 登録した自分をobserverに教え,observerを登録 obs->subject_set(this); observers_.push_back(obs); } virtual void detach(observer* obs){ // 解除した自分をobserverに教え,observerを解除 obs->subject_unset(this); observers_.remove(obs); } virtual void notify(){ // Notify! std::list<observer*>::iterator it = observers_.begin(); for(; it != observers_.end(); ++it) (*it)->subject_update(this); } }; // // ConcreteSubjectクラス // class data : public subject { int n; public: ~data(){ // 登録しているobserversに自分が消えることを教える(解除) std::list<observer*>::iterator it = observers_.begin(); for(; it != observers_.end(); ++it) (*it)->subject_unset(this); } void set(int x){ n = x; notify(); } int get(){ return n; } }; // // ConcreteObserverクラス // class viewer : public observer { std::list<data*> data_; public: viewer(){} ~viewer(){ // 自分を登録しているsubjectに対し,自分をdetachする std::list<data*>::iterator it = data_.begin(); while(it != data_.end()) (*it++)->detach(this); } void subject_update(subject* sub){ std::list<data*>::iterator it = std::find(data_.begin(), data_.end(), sub); assert(it != data_.end()); // dynamic_cast failed! std::cout << "data is " << (*it)->get() << std::endl; } void subject_set(subject* sub){ assert(std::find(data_.begin(), data_.end(), sub) == data_.end()); // まだ登録されてないはず data* new_data = dynamic_cast<data*>(sub); assert(new_data != NULL); // dynamic_cast failed! data_.push_back(new_data); } void subject_unset(subject* sub){ assert(std::find(data_.begin(), data_.end(), sub) != data_.end()); // 登録されているはず data_.remove(sub); } };
コメントが無い….
微妙なコメントをちょっとつけた.
これでダングリングにも対応できるかな?
うーん,使用用途にもよるだろうけど,std::vectorの方がいい気がする.
viewer::~viewer()内で無効なイテレータを操作していた.
気付かないうちになっちゃうもんだね(detach内で普通にeraseしてるが).
Observer
まず最初はObserverからやってみよう.
class observer { public: virtual void update(subject* sub); }; class subject { std::list<observer> list_; public: void add(observer* obs); void remove(observer* obs); void notify(); }; class data : public subject { public: void update_data(){ // update data notify(); } }; class viewer : public observer { public: void update(subject* sub); }; data d; viewer v; d.add(v); d.update_data(); // notify
これでうまく動くのかは分からないけど,こんな雰囲気でいいのかな.
#各メンバ関数の定義は書いてないです.
ただし,これだとObserver派生のvが削除された時にダングリング問題が発生してしまう.
(ダングリング問題って言葉あるのかな?).
そこでGoF本ではdataクラスでviewerクラスへのポインタを保持し,デストラクタでthisをremoveしている.
でもそれのせいで自分は理解がまだ追いついてなかったりw
(続きます).
妹認証
妹認証
http://www.okanesuita.org/auth_sister/
これをリメイクしたんだけど,何か時々失敗する.
妹認証さんのページに「やりすぎておかしくなった場合のクッキー再設定」があるけど,同じ原因なのかなぁ.
よくわからない.
<?php require_once("auth_maid/core.php"); // maidに深い意味はない mauth_init(); // 初期化 $auth_ret = mauth_check(); // 結果確認 mauth_load(); // 次の問題設定 ?> <html> <head> <?php mauth_print_header(); ?> </head> <body> <form action="./this_file.php" method="POST"> <?php mauth_print_forms(); ?> </form> <?php if($auth_ret == MAUTH_NULL) print "未入力だよん"; else if($auth_ret == MAUTH_SUCCEEDED) print "成功!"; else if($auth_ret == MAUTH_FAILED) print "失敗!"; // else // assert(false); print $mauth_message(); // 状況に応じたメッセージ ?>