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とか読むと考えが変わるかもしれないです.

読みたい.

一ヶ月以上何も書かないと?


一ヶ月以上エントリが無いと何かあるらしい?


とりあえず,CodeCraftおすすめです.


最近C++で勉強したことといえば,dynamic deleterくらい?


あとSTLの解説本を読んでたりします.


今から12/30までほぼ毎日修羅場になりそう.

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(); // 状況に応じたメッセージ

?>