2011年6月26日日曜日

shared_ptr を自分で書いてみる 5

shared_ptr といいつつ,今回は shared_ptr のお供であるところの weak_ptr を書いてみます.

weak_ptr というのは要するに所有権を持たない shared_ptr なので, 自分が参照している対象の所有者が 0 になったとき,自分も対象のポインタ を参照できなくなるようにします.

参照している所有者の数は shared_count クラスで管理しているわけです から, weak_ptr もこれを共有しつつ所有者数が 0 になっていないかを確 認すれば良いわけです.

しかし,現状の shared_count クラスでは,所有者の数が 0 になった瞬間 に自分自身を破棄しているので,これでは weak_ptr 側から所有者数を チェックする際に無効なインスタンスにアクセスしてしまうので具合がよろ しくありません.

ということで shared_count を次のように変更してみます. weak_ptr からの参照数を管理するようにし,所有数が 0 になったときではなく, weak_ptr からの参照数が 0 になったときに自分を破棄するようにしました.

struct shared_count
{
    long count_;
    long weak_count_;

    shared_count() :count_(1), weak_count_(1) {}
    virtual ~shared_count() {}
    
    void increment() { ++count_; }
    void decrement(void* p)
    {
        if (--count_ == 0) {
            release(p);
            dec_weak_ref();
        }
    }
    void inc_weak_ref() { ++weak_count_; }
    void dec_weak_ref()
    {
        if (--weak_count_ == 0) {
            destroy();
        }
    }
    virtual void destroy() { delete this; }
    virtual void release(void*) const = 0;
};

ここまでくれば,あとは shared_ptr と同じ要領で weak_ptr が実装で きます. boost::weak_ptrintroduction にもあるとおり, weak_ptr 自身からは直接ポインタにアクセスせず shared_ptr に変換し てからアクセスするので,この変換するタイミングで所有者数をチェックす るようにします.

ということで weak_ptr から shared_ptr へ変換するコンストラクタは こんな具合で.

template <typename Y> shared_ptr(const weak_ptr<Y>& r)
    :ptr_(r.ptr_), count_(r.count_)
{
    if (!count_ || count_->count_ == 0) {
        throw bad_weak_ptr();
    }
    else {
        count_->increment();
    }
}

weak_ptr の実装はこんな感じに.

template<class T> class weak_ptr
{
    T* ptr_;
    shared_count* count_;

    template <typename Y> friend class shared_ptr;
    template <typename Y> friend class weak_ptr;
public:
    typedef T element_type;

    weak_ptr() :ptr_(0), count_(0) {}
    ~weak_ptr()
    {
        if (count_) count_->dec_weak_ref();
    }

    weak_ptr(const weak_ptr& r) :ptr_(r.ptr_), count_(r.count_)
    {
        if (count_) count_->inc_weak_ref();
    }
    template<class Y> weak_ptr(const weak_ptr<Y>& r) :ptr_(r.ptr_), count_(r.count_)
    {
        if (count_) count_->inc_weak_ref();
    }
    template<class Y> weak_ptr(const shared_ptr<Y>& r) :ptr_(r.ptr_), count_(r.count_)
    {
        if (count_) count_->inc_weak_ref();
    }

    weak_ptr& operator = (const weak_ptr& r)
    {
        if (count_ != r.count_) {
            weak_ptr(r).swap(*this);
        }
        return *this;
    }
    template<class Y> weak_ptr& operator = (const weak_ptr<Y>& r)
    {
        if (count_ != r.count_) {
            weak_ptr(r).swap(*this);
        }
        return *this;
    }
    template<class Y> weak_ptr& operator = (const shared_ptr<Y>& r)
    {
        if (count_ != r.count_) {
            weak_ptr(r).swap(*this);
        }
        return *this;
    }

    long use_count() const { return count_ ? count_->count_ : 0; }
    bool expired() const { return use_count() == 0; }
    shared_ptr<T> lock() const
    {
        return expired() ? shared_ptr<T>() : shared_ptr<T>(*this);
    }

    void reset()
    {
        weak_ptr().swap(*this);
    }
    void swap(weak_ptr<T>& r)
    {
        std::swap(ptr_, r.ptr_);
        std::swap(count_, r.count_);
    }
};

weak_ptr 自身ではポインタに触れないものの shared_ptr に変換する際 に必要になる兼ね合いでポインタをメンバに含んでいるのがちょっと勿体ないような気もしますね…

なにはともあれ,これでひとまず最低限それっぽい shared_ptr の機能は 揃って一区切りかなー? といった感じですね.

2011年6月19日日曜日

shared_ptr を自分で書いてみる 4

次はカスタムデリータやアロケータを受け取るバージョンの コンストラクタ を実装してみます.(だんだんソース全部載せるのも長ったらしく感じてきたの で,今回は追加変更する部分だけコードを載せることにします)

まずカスタムデリータを渡す場合だと delete してるところを置き換えれ ばいいので,こんな感じに shared_count を派生すれば良さそうです.

template <typename T, typename D>
struct shared_count_with_deleter :public shared_count
{
    D deleter_;
    shared_count_with_deleter(D d) :deleter_(d) {}
    virtual void release(void* p) const
    {
        deleter_(reinterpret_cast<T*>(p));
    }
};

コンストラクタはこんな感じ.

template<typename Y, typename D> shared_ptr(Y * p, D d)
    :ptr_(p), count_(new shared_count_with_deleter<Y, D>(d))
{
}  

次にカスタムアロケータを渡す場合を考えてみます.やることは shared_count の生成破棄をアロケータ経由で行うようにすればいいという ことなので,コンストラクタの実装はカスタムアロケータで確保したメモリ に対して placement new で shared_count を生成します.

template<typename Y, typename D, typename A> shared_ptr(Y * p, D d, A a)
    :ptr_(p), count_(0)
{
    typedef shared_count_with_deleter_allocator<Y, D, A> sc_type;
    typename A::template rebind<sc_type>::other a2(a);
    count_ = a2.allocate(1);
    new (static_cast<void*>(count_)) sc_type(d, a);
}

placement new によって生成さたので shared_count 側で自分自身を破棄 するのに delete 演算子は使えません.というわけで自分自身を破棄する 処理をカスタム化するために destroy メソッドを追加します.

struct shared_count
{
    long count_;

    shared_count() :count_(1) {}
    virtual ~shared_count() {}
    
    void increment() { ++count_; }
    void decrement(void* p)
    {
        if (--count_ == 0) {
            release(p);
            destroy();
        }
    }
    virtual void destroy() { delete this; }
    virtual void release(void*) const = 0;
};

そしてアロケータ利用版の shared_count_with_deleter_allocator では destroy で明示的にデストラクタを呼び,アロケータ経由でメモリを開放 するようにします.

template <typename T, typename D, typename A>
struct shared_count_with_deleter_allocator :public shared_count
{
    D deleter_;
    A allocator_;

    typedef shared_count_with_deleter_allocator<T,D,A> this_type;
    
    shared_count_with_deleter_allocator(D d, A a) :deleter_(d), allocator_(a) {}
    virtual void release(void* p) const
    {
        deleter_(reinterpret_cast<T*>(p));
    }
    virtual void destroy()
    {
        typedef typename A::template rebind<this_type>::other A2;
        A2 a(allocator_);
        this->~this_type();
        a.deallocate(this, 1);
    }
};

いちおうちゃんと動いてるかなー?

shared_ptr を自分で書いてみる 3

参照カウントと削除子の二つ分もメモリ確保してるのはちょっと勿体ないなぁ というか,よく考えてみると,この二つは同じ寿命なので一つにまとめても 良さそうです.

ということで参照カウントと削除子を両方管理するような shared_count クラスを用意してみたバージョンがこんな感じ.

struct shared_count
{
    long count_;

    shared_count() :count_(1) {}
    void increment() { ++count_; }
    void decrement(void* p)
    {
        if (--count_ == 0) {
            release(p);
            delete this;
        }
    }
    virtual void release(void*) const = 0;
};
    
template <typename T> struct shared_count_default :public shared_count
{
    virtual void release(void* p) const
    {
        delete reinterpret_cast<T*>(p);
    }
};

template <typename T> class shared_ptr
{
    T* ptr_;
    shared_count* count_;
        
public:
    typedef T element_type;
        
    shared_ptr() :ptr_(0), count_(0) {}
    ~shared_ptr()
    {
        if (count_) { count_->decrement(ptr_); }
    }
        
    template <typename Y> explicit shared_ptr(Y* p)
        :ptr_(p), count_(new shared_count_default<Y>)
    {
    }

    shared_ptr(const shared_ptr& r) :ptr_(r.ptr_), count_(r.count_)
    {
        if (count_) {
            count_->increment();
        }
    }
    
    shared_ptr& operator = (const shared_ptr& r)
    {
        if (count_ != r.count_) {
            shared_ptr(r).swap(*this);
        }
        return *this;
    }
        
    T& operator * () const { return *ptr_;}
    T* operator -> () const { return ptr_; }
    
    long use_count() const { return count_ ? count_->count_ : 0; }
    T* get() const { return ptr_; }
    
    void swap(shared_ptr& r)
    {
        std::swap(ptr_, r.ptr_);
        std::swap(count_, r.count_);
    }
    void reset()
    {
        shared_ptr().swap(*this);
    }
};

意味的に increment, decrement の処理は shared_count 側だろう思っ て移しましたけど,そうすると decrement にポインタを渡さないといけな くなってしまい,これはこれでちょっと意味が変な気もします.モヤモヤ

2011年6月17日金曜日

shared_ptr を自分で書いてみる 2

shared_ptrコンストラクタの定義 を見るとテンプレート引数で指定し た T 型のポインタだけでなく, T* とコンパチなポインタ (たとえば T のサブクラスのポインタ) であれば渡すことができて,かつ

The destructor will call delete with the same pointer, complete with its original type, even when T does not have a virtual destructor, or is void.

とあるように,ベースクラスの shared_ptr にサブクラスのポインタを格 納した場合でも,デストラクタが仮想じゃなくてもサブクラスのデストラク タがちゃんと呼ばれるそうで.

ということで実装してみる.

こういうときは type erasure な手法を使えばいいらしいということで, 基本的なアイデアはこんな感じでいいのかな?

struct deleter_base
{
    virtual void operator () (void*) const = 0;
};

template <typename T> struct default_deleter :public deleter_base
{
    virtual void operator () (void* p) const
    {
        delete reinterpret_cast<T*>(p);
    }
};

こんな感じにテンプレートで型を保持しておけば正しい型で delete できる.

struct base
{
    // not virtual
    ~base() {std::cout << "base.dtor" << std::endl;}
};

struct derived :public base
{
    ~derived() {std::cout << "derived.dtor" << std::endl;}
};

base* ptr = new derived;
default_deleter<derived>* del = new default_deleter<derived>;

(*del)(ptr);
// result is 
// > derived.dtor
// > base.dtor

そんな感じのを踏まえて修正してみたバージョン

struct deleter_base
{
    virtual void operator () (void*) const = 0;
};
    
template <typename T> struct default_deleter :public deleter_base
{
    virtual void operator () (void* p) const
    {
        delete reinterpret_cast<T*>(p);
    }
};

template <typename T> class shared_ptr
{
    T* ptr_;
    long* count_;
    deleter_base* deleter_;
        
public:
    typedef T element_type;
        
    shared_ptr() :ptr_(0), count_(0), deleter_(0) {}
    ~shared_ptr()
    {
        decrement();
    }
        
    template <typename Y> explicit shared_ptr(Y* p)
        :ptr_(p), count_(new long(1)), deleter_(new default_deleter<Y>)
    {
    }
    shared_ptr(const shared_ptr& r) :ptr_(r.ptr_), count_(r.count_), deleter_(r.deleter_)
    {
        increment();
    }
    
    shared_ptr& operator = (const shared_ptr& r)
    {
        decrement();
        count_ = r.count_;
        ptr_ = r.ptr_;
        deleter_ = r.deleter_;
        increment();
        return *this;
    }
        
    T& operator * () const { return *ptr_;}
    T* operator -> () const { return ptr_; }
    
    long use_count() const { return count_ ? *count_ : 0; }
    T* get() const { return ptr_; }
    
    void swap(shared_ptr& r)
    {
        std::swap(ptr_, r.ptr_);
        std::swap(count_, r.count_);
        std::swap(deleter_, r.deleter_);
    }
    void reset()
    {
        shared_ptr().swap(*this);
    }
    
private:
    void decrement()
    {
        if (count_) {
            if (--(*count_) == 0) {
                delete count_;
                (*deleter_)(ptr_);
                delete deleter_;
            }
        }
    }
    
    void increment()
    {
        if (count_) {
            ++(*count_);
        }
    }
};

まぁ期待通りに動いてるっぽい.たぶん.

[追記] 後で気付きましたけど,案の定このコードにはバグがあります.

2011年6月15日水曜日

shared_ptr を自分で書いてみる 1

興味本位というか練習のつもりで shared_ptr を自分で書いてみる.

まずは boost::shared_ptr のドキュメントにあるインタフェースを元にして 最低限のものを単純に書いてみた.(ということで例外安全とかスレッドセー フとか難しそうなことはひとまず無視)

template <typename T> class shared_ptr
{
    T* ptr_;
    long* count_;
    
public:
    typedef T element_type;
    
    shared_ptr() :ptr_(0), count_(0) {}
    ~shared_ptr()
    {
        decrement();
    }
    
    template <typename Y> explicit shared_ptr(Y* p)
        :ptr_(p), count_(new long(1))
    {
    }
    shared_ptr(const shared_ptr& r) :ptr_(r.ptr_), count_(r.count_)
    {
        increment();
    }

    shared_ptr& operator = (const shared_ptr& r)
    {
        decrement();
        count_ = r.count_;
        ptr_ = r.ptr_;
        increment();
        return *this;
    }
    
    T& operator * () const { return *ptr_;}
    T* operator -> () const { return ptr_; }

    long use_count() const { return count_ ? *count_ : 0; }
    T* get() const { return ptr_; }

    void swap(shared_ptr& r)
    {
        std::swap(ptr_, r.ptr_);
        std::swap(count_, r.count_);
    }
    void reset()
    {
        shared_ptr().swap(*this);
    }

private:
    void decrement()
    {
        if (count_) {
            if (--(*count_) == 0) {
                delete count_;
                delete ptr_;
            }
        }
    }

    void increment()
    {
        if (count_) {
            ++(*count_);
        }
    }
};

2011年6月3日金曜日

const 型へのポインタの delete

こういう const 型ヘのポインタを delete するのって合法なんですね.

const hoge* ptr = new hoge;
delete ptr;                     // OK

5.3.5 delete のとこに書いてた.

[Note: a pointer to a const type can be the operand of a delete-expression; it is not necessary to cast away the constness (5.2.11) of the pointer expression before it is used as the operand of the delete-expression. ]

いまいち釈然としないなーと思ってテケトーにぐぐる先生に聞いてみてたら ここの説明でしっくりきた.たしかにスタックに const なオブジェクト構築 した場合でもデストラクタ呼べないかんし,そういうもんなんやね

http://stackoverflow.com/questions/755196/deleting-a-pointer-to-const-t-const

2011年5月28日土曜日

bloggerpost のテスト

emacs から編集できるようにしたいので調べ中.

BloggerPost を試してみる.

インストールと設定

MacPorts なり homebrew なりで全部入れば楽ちんなんだけど,流石にそうもいかないので仕方ない.(まぁ本来は自分でレシピとか書いた方がいいんでしょうが…)

1. el4r

<URL:http://www.rubyist.net/~rubikitch/computer/el4r>

ドキュメントに従ってインストール

$ wget http://www.rubyist.net/~rubikitch/archive/el4r-1.0.4.tar.gz
$ tar zxvf el4r-1.0.4.tar.gz
$ cd el4r-1.0.4
$ sudo ruby setup.rb
$ bin/el4r-rctool -p
$ bin/el4r-rctool -i

2. QDBM

<URL:http://qdbm.sourceforge.net/>

これは MacPorts にあるのでそっちで入れた

$ sudo port install qdbm +universal

MacPorts だと ruby バインディングはインストールされないので,こっちはソースからインストール.

$ wget http://fallabs.com/qdbm/qdbm-1.8.78.tar.gz
$ tar zxvf qdbm-1.8.78.tar.gz
$ cd qdbm-1.8.78/ruby
$ LDFLAGS=-L/opt/local/lib ./configure
$ make
$ sudo make install

3. rdtool

BloggerPost の標準書式は RD らしいのでrdtool も必要

$ wget http://www.moonwolf.com/ruby/archive/rdtool-0.6.22.tar.gz
$ tar zxvf rdtool-0.6.22.tar.gz
$ cd rdtool-0.6.22
$ sudo ruby setup.rb

4. bloggerpost

なんとなく svn trunk から取ってきた

$ svn co http://svn.sourceforge.jp/svnroot/bloggerpost/bloggerpost/trunk/ bloggerpost
$ cd bloggerpost
$ sudo ruby setup.rb

そしてコンフィグファイルを用意

$ mkdir ~/.bloggerpost
$ cp config.yml.sample ~/.bloggerpost

blogger の場合,blog: -> list: -> posturl: に入れる feed url はサンプルの XXXXXXXXX の部分を自分の BlogID に替えれば OK.BlogID はウェブから投稿するときのリンクに入ってるのでそれをコピった.

5. emacs 側

load-path を追加して el4r を読み込む.bloggerpost 自体は el4r が自動的に読み込んでくれるそうで.

(add-to-list 'load-path "/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/share/emacs/site-lisp")
(require 'el4r)

;; el4r を実行
(el4r-boot) 

ポストしてみる

ようやくどうにか設定できてポストも一応できるようになった風味.最初に bloggerpost-draft から新規投稿すると↓というエラーが出てるけど投稿自体はちゃんとできてるっぽい?

> Sat May 28 16:19:31 +0900 2011:Error: /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/xmlrpc/client.rb:549:in `do_rpc': HTTP-Error: 301 Moved Permanently (RuntimeError)

ということで一応使えるようになったけど

設定めんどうだし処理も妙に重いのであんまりしっくりは来ない.

googlecl とか使ってもっと単純にポストできるようにした方が楽なような気もするなぁ.

2011年5月25日水曜日

mac の wxpython

MacPorts から py27-wxpython を入れて使ってみようとしたら怒られた.

Python 2.7.1 (r271:86832, May 24 2011, 00:46:07) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import wx
Traceback (most recent call last):
  File "", line 1, in 
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/wx-2.8-mac-unicode/wx/__init__.py", line 45, in 
    from wx._core import *
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/wx-2.8-mac-unicode/wx/_core.py", line 4, in 
    import _core_
ImportError: dlopen(/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/wx-2.8-mac-unicode/wx/_core_.so, 2): no suitable image found.  Did find:
        /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/wx-2.8-mac-unicode/wx/_core_.so: mach-o, but wrong architecture
>>> 

ぐぐる先生に聞いてみたら wxWidgets は i386 でしか使えないらしいので x86_64 な python でインポートすると怒られるそうで.

ということで python も i386 指定で起動してやればいけると.なるほど.


$ arch -i368 /opt/local/bin/python2.7