C++ Class Property Macro

要完整地封裝 C++ 的 data member 實在是太麻煩了...

像這樣有 6 個 member ,我就要 寫下 (2~3) x 6 行的 declaration。而且 readonly 的屬性我是要去看不存在 setter 才能意會的到。另外這麼多行的宣告,也是會多餘的行數汙染、影響介面的可讀性。

class Man
{public:
  
  // return by value
  std::wstring name() const { return m_name;};
  void name(const std::wstring& name){ m_name = name;};
  
  int age() const { return m_age};
  void age(int val) { m_age = val;};
  
  int friends() const { return m_friends;}
  void friends(int val) { m_friends = val;}
  
  // readonly
  double salary() const { return m_salary;} 
  
  // return by ref
  const std::vector<std::wstring>& accounts() const { return m_accounts;}
  std::vector<std::wstring>& accounts() { return m_accounts;}   
  
  // by ref read only
  const std::vector<std::wstring>& record_table() const { return record_table;}  

private:
  std::wstring m_name;
  int m_age;
  int m_friends;
  double m_salary
  std::vector<std::wstring> m_accounts;
  std::vector<std::vector<int> m_record_table;
};

don't repeat yourself

於是我們可以定義一下簡單的 MACRO,來簡化我們的工作。

return/pass by value 的 mutable 版與 readonly 版。

#define Property(PROPERTY_TYPE, PROPERTY_NAME)\
public:\
    inline PROPERTY_TYPE PROPERTY_NAME() const{ return m_##PROPERTY_NAME;}\    
    inline void PROPERTY_NAME(const PROPERTY_TYPE& val) { m_##PROPERTY_NAME = val;}\
private:\
    PROPERTY_TYPE m_##PROPERTY_NAME

#define PropertyReadonly(PROPERTY_TYPE, PROPERTY_NAME)\
public:\
    inline PROPERTY_TYPE PROPERTY_NAME() const{ return m_##PROPERTY_NAME;}\
private:\
    PROPERTY_TYPE m_##PROPERTY_NAME
    

若覺得 member type 的大小其實有點肥大,想要用 return by reference 的型式來減少 copy 次數。

雖然 Scott Meyer 不推薦傳出 class 內部的 member reference,會有 dangling reference 的問題,但是我覺得這個作法,有他必要的存在,只是代價就是要將 dangling reference 的危險謹記在心。

#define PropertyByRef(PROPERTY_TYPE, PROPERTY_NAME)\
public:\
    inline PROPERTY_TYPE& PROPERTY_NAME() { return m_##PROPERTY_NAME;}\    
    inline const PROPERTY_TYPE& PROPERTY_NAME() const{ return m_##PROPERTY_NAME;}\    
private:\
    PROPERTY_TYPE m_##PROPERTY_NAME
    
    
#define PropertyByRefReadonly(PROPERTY_TYPE, PROPERTY_NAME)\
public:\
    inline const PROPERTY_TYPE& PROPERTY_NAME() const{ return m_##PROPERTY_NAME;}\    
private:\
    PROPERTY_TYPE m_##PROPERTY_NAME    

套用 macro 的結果

於是我們可以從一行的定義式,應該說是 macro 名字,就可以知道

  • 此 data member 的 access 屬性
  • 與 return 方式
  • data member 的 type
  • data member 的 name
  • getter/setter 的 name

有一種跟 Objective C 的 Property keyword 殊途同歸的感覺。

class Man
{
  Property(std::wstring, name);
  Property(int, age);
  Property(int, friends);
  PropertyReadonly(double, salary);
  PropertyByRef(std::vector<std::wstring>, accounts);
  PropertyByRefReadonly(std::vector<std::vector<int>, m_record_table;
};

當然有要有更複雜的屬性,可能就要定義更多的 macro

  • 像是 virtual getter/setter?
  • data member 本身是否為 const?
  • data member 本身是否為 reference?
  • access pattern 有無 writeonly?
    可能就有 2 x 2 x 2 x 2 x 2(ref vs value) x 2 (readonly vs mutable) 種 macro XD

不過目前就只要定義我們常用的其實就夠了。

結論

我覺得如果 C++ 沒有 macro 的話,還真的會變成一個很囉嗦的語言。

comments powered by Disqus