読者です 読者をやめる 読者になる 読者になる

ゆとりーなの日記

日記的な事を書いて行くと思はれる

C++でプロパティ

いろんなところで見かけるこのネタですが、Obj-Cをやってるとどうしても気になってくるのでついやっちゃいました。

// 読み込みのみ
struct readonly {};
// 読み書き両用
struct readwright {};

// メンバ変数からプロパティを作成
template <typename T, typename Class, T Class::* Pointer>
class synthesize {
  using result_type = T;
  using class_type = Class;
  
  synthesize() = default;
  
  static T get(Class *self) {
    return self->*Pointer;
  }
  
  static void set(Class *self, T t) {
    self->*Pointer = t;
  }
  
  template <typename State, typename Proc>
  friend struct propaty;
};

// メンバ関数からプロパティを作成
template <typename T,
           typename Class,
           T (Class::* Getter)() const,
           void (Class::* Setter)(T) = nullptr>
struct dynamic {
  using result_type = T;
  using class_type = Class;
  
  dynamic() = default;
  
  static T get(Class *self) {
    return (self->*Getter)();
  }
  
  static void set(Class *self, T t) {
    (self->*Setter)(t);
  }
  
  template <typename State, typename Proc>
  friend struct propaty;
};

// 読み書き両用のプロパティ
template <typename State, typename Proc>
struct propaty {
  propaty &operator =(typename Proc::result_type t) {
    Proc::set(self_, t);
    return *this;
  }
  
  operator typename Proc::result_type() const {
    return Proc::get(self_);
  }
  
 private:
  explicit propaty(typename Proc::class_type *self) : self_(self) {}
  
  propaty(const propaty &rhs) {}
  
  propaty &operator =(const propaty &rhs) {
    return *this;
  }
  
  friend typename Proc::class_type;
  
  typename Proc::class_type *self_;
}; 

// 読み込みのみプロパティ
template <typename Proc>
struct propaty<readonly, Proc> {
  operator typename Proc::result_type() const {
    return Proc::get(self_);
  }
  
 private:
  explicit propaty(typename Proc::class_type *self) : self_(self) {}
  
  propaty(const propaty &rhs) {}
  
  propaty &operator =(const propaty &rhs) {
    return *this;
  }
  
  friend typename Proc::class_type;
  
  typename Proc::class_type *self_;
}; 

Obj-Cに合わせた?のでdynamicとか特に謎な命名になってしまいましたがまぁ気にしない方向で。一応読み書きと読み込みのみとかの対応、メンバ変数をそのまま使うか自前で作ったアクセッサを使うか等を選べるようにしてみました。あと多分コピーしても問題なく動くようにしたつもりです。
使ってみた感じはこんな雰囲気です。

class hoge {
  // メンバ変数がプロパティより上にいないといけない...
  int a_;
  double b_;
 
 public: 
  hoge() : a_(165), b_(113.0), a(this), b(this) {}
   
  void print() const {
    std::cout << a_ << "," << b_ << std::endl;
  }
  
  double get_b() const {
    std::cout << "getter b" << std::endl;
    return b_;
  }
  
  propaty<readwright, synthesize<int, hoge, &hoge::a_>> a; 
  propaty<readonly, dynamic<double, hoge, &hoge::get_b>> b;
};

int main() {
  hoge h, g;
  h.a = 313;
  h.print();
  std::cout << h.a << "," << h.b << std::endl;
  g = h;
  h.print();
  std::cout << h.a << "," << h.b << std::endl;
  g.print();
  std::cout << g.a << "," << g.b << std::endl;
  g.a = 5000;
  h.print();
  std::cout << h.a << "," << h.b << std::endl;
  g.print();
  std::cout << g.a << "," << g.b << std::endl;
}

実行結果

313,113
getter b
313,113
313,113
getter b
313,113
313,113
getter b
313,113
313,113
getter b
313,113
5000,113
getter b
5000,113

まぁ、微妙ですね。