SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

C++でプロパティを実現するココロミ

  • このエントリーをはてなブックマークに追加

 昨年末からC++/CXで遊んでいます。C++/CXは"ほぼC++"ではありますが、C++にはない機能も散見されます。GC(garbage collection)のおかげでnewしたものの後始末は勝手にやってくれますし、あらゆるものがPlatform::Objectから(直接/間接的に)導出されているのも特徴の一つです。今回のお題はC++にはない機能の一つ:プロパティをC++で実現したらどうなるの、ってオハナシ。

  • このエントリーをはてなブックマークに追加

C++/CXのプロパティ

 C++/CXあるいはC++/CLI、C#、VB.NETのプロパティとは、あたかも変数であるかのようにふるまう関数です。例えば:

list-01
using namespace Platform;

ref class Person sealed {
public:

  property String^ Name {
    String^ get();
    void    set(String^ value);
  }

  property int Age {
    int  get();
    void set(int value);
  }

private:
  String^ name_;
  int     age_;
};

String^ Person::Name::get()              { return name_; }
void    Person::Name::set(String^ value) { name_ = value; }

int     Person::Age::get()               { return age_; }
void    Person::Age::set(int value)      { age_ = value; }

 このように、property名前に対しget/set関数を定義することで、あたかもメンバ変数であるかのごとくふるまいます。

list-02
int main(Platform::Array<Platform::String^>^) {
  Person man;

  // メンバ変数への値の代入のようにふるまう
  man.Name = L"Adam"; 
  man.Age  = 21;

  // メンバ変数の持つ値を読みだしているかのようにふるまう
  String^ name = man.Name;
  int     age  = man.Age;

  Details::Console::WriteLine(L"Name : " + name);
  Details::Console::WriteLine(L"Age  : " + age.ToString());
}

 man.Name、man.Ageが左辺値と解釈されるならset(),右辺値と解釈されるならget()が呼び出されます。変数の読み書きに見せかけた関数の呼び出しです。これをC++でも実現した例がないかと探してみたら、ありました。C++標準員会:SC22/WG21にプロポーザル(提案書):N1615として提出されていました。十数ページの小さなドキュメントですし、英語の勉強がてら読んでみるのもよいでしょう。N1615はプロパティを言語仕様の拡張によってではなく、ライブラリで実現する方法を提案しています。そこにはこんな小さなコードがありました。

list-03
/* excerpt from
 *   SC22/WG21/N1615 "C++ Properties -- a Library Solution" 2004-04-09
 */
template<class T, class Object,
    typename T(Object::*real_getter)() const,
    typename T(Object::*real_setter)(T const&)>
class RWProperty {
    Object* my_object;
public:
    void operator()(Object* obj) { my_object = obj; }

    T get() const { return (my_object->*real_getter)(); }
    T set(T const& value) { return (my_object->*real_setter)(value); }

    T operator()() const { return get(); }
    T operator()(T const& value) { return set(value); }

    operator T() const { return get(); }
    T operator=(T const& value) { return set(value); }

    typedef T value_type;
};

 このRWPropertyを使って作られたPersonがコチラ。

list-04
#include <iostream>
#include <string>

class Person {
private:
  int age_;
  // getter/setter for age
  int get_age() const { return age_; }
  int set_age(int const& value) { return age_ = value; }

  std::string name_;
  // getter/setter for name
  std::string get_name() const { return name_; }
  std::string set_name(std::string const& value) { return name_ = value; }

public:
  Person() { Age(this); Name(this); /* do this before use! */ }

  // property age/name
  RWProperty<int,         Person, &Person::get_age,  &Person::set_age > Age;
  RWProperty<std::string, Person, &Person::get_name, &Person::set_name> Name;

  ostream& print_on(std::ostream& out) const { return out << name_ << ':' << age_; }
};

std::ostream& operator<<(std::ostream& stream, const Person& p) {
    return p.print_on(stream);
}

using namespace std;

int main() {
    Person adam;
    adam.Name = "Adam";
    adam.Age  = 20;
    cout << adam << endl;
    string name = adam.Name;
    int    age  = adam.Age;
    cout << name << ':' << age << endl << endl;
}

 ...なるほど。プロパティの型とプロパティの持ち主の型、そしてget/setするメンバ関数をテンプレート引数に与え、持ち主のコンストラクタ内でthisで初期化しておけば、man.Ageが右辺値ならoperator int(),左辺値ならoperator=(int const&)の中から(テンプレート引数で与えておいた)get/set関数が呼び出されるってカラクリですな。関数へのポインタはコンパイル時に決定する定数なのでテンプレート引数に与えることができ、RWPropertyのメンバは持ち主のポインタだけで済ませています。

 このプロポーザル、2004年に提出されています。2004年というとC++03が決まったばかり。テンプレートの仕様も今ほどにはきっちり定まっていない時期としては妥当な実装ではないかと思います。要はプロパティが右辺/左辺に現れたとき、あらかじめ用意しておいた関数が呼ばれればいい。この実装をお手本に"イマ風"に書き直してみましょうか。

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
プロパティの実装:今ならこうする(?)

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/7571 2014/01/28 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング