2010-07 << 2010-08 >> 2010-09

2010-08-02 (月)

*C++でもDやC#2.0みたいなプロパティが欲しい

なんでもかんでもgetX()やsetX()を定義するのは面倒ですが,メンバ変数を直に参照していると後で困りそうで心配です.多くの言語では心配しなくて良い仕組みが用意されていますが,残念ながらC++にはありません.

ただC++は何でも出来てしまう言語なので上手くすれば実現できるはず.

  • 透過的にgetterやsetterを呼びたい
  • 余計なメモリを使いたくない
  • getter,setter内で色々やりたい
  • できるだけ簡潔に

多分,みんな書いていると思ったのだけど,全部満たすものが見つからなかったので書いてみた.特に,プロパティ毎に親オブジェクトを持たせるとかは非効率的なので絶対に避けたい.最初はマクロで頑張ろうかと思ったけど,テンプレートだけでも結構いけます.

template<class CLASS,typename T>
struct _property{
    typedef T _type;
    typedef typename CLASS _type_parent;
protected:
    friend CLASS;
    T value;
    _property() {}
    _property(T v) : value(v){}
    template<typename TT>
    inline _type_parent& parent(TT _type_parent::*mp) {
        return *(_type_parent*)((char*)this - (size_t)&(((_type_parent*)NULL)->*mp));
    }
public:
    inline T operator =(const T &v){return value=v;}
    inline operator const T&() const {return value;}
};

// ついでに読み取り専用のやつも
template<class CLASS,typename T>
struct _property_r : _property<CLASS, T>{
    friend CLASS;
protected:
    _property_r() {}
    _property_r(T v) : _property(v){}
    inline T operator =(const T &v){return value=v;}
};
#define _property_setter inline _type operator =(const _type &v)
#define _property_getter inline operator const _type&() const

こんな感じ.

使用例

class Hoge{
public:
    _property<Hoge,long> foo; // 読み書き可(あまり意味無い)
    _property_r<Hoge,long> bar; // 読み取りだけ許可

    struct : _property<Hoge,long>{
        _property_setter {
            cout << "set hoe and bar" << endl;
            parent(&Hoge::hoe).bar = v*2; // ちょっと面倒…
            return value=v;
        }
    } hoe;

    Hoge(){
        foo = bar = 123; // Hogeの中では普通にアクセス可能
    }
};

int main(int argc, char* argv[])
{
    Hoge hoge;
    
    // fooは読み書き可能
    hoge.foo = 321;
    cout << hoge.foo << endl;

    hoge.hoe = 1; // barには2倍した値が入る
    cout << hoge.hoe << endl;

    //hoge.bar = 0; // エラー
    cout << hoge.bar << endl; // 読み取りは可能

    cout << sizeof(hoge.foo) << "==" << sizeof(long) << endl; // 多分余計なメモリは食わない
    return 0;
}

実行結果

321
123
set hoe and bar
1
2
4==4

getX(),setX()を実装するより良い点は,コードの見栄えが良くなるだけでなく,メンバを持っているクラス内でも,普通に書く限りは必ずgetter,setterが呼ばれる事です.直接書き換えたい場合は,foo.valueを書き換えなければいけません.

コンパイル結果を真面目に見てないので速度的なオーバーヘッドがあるかもしれないけど,一応は条件を満たせました.親クラスを取得するためのparent()とかが酷いけど,綺麗に書く方法を思いつかなかったです.

やっぱり,今時の言語なら簡単に出来ることが,C++だと意外と面倒だ.

追記:

autoで受けた時の事を考えて無かったな…

2010-07 << 2010-08 >> 2010-09