好き勝手に・げーあにん?

ファミコンと同い年の社会人ヌルオタの日記

ちゃんとアクセサを書こうと思い立つ

アクセサ書いたほうがいいのか、public とか friend 使ったほうがいいのかとかの議論をする気は無くて、ただアクセサを書いたほうがいいなと思ったできごとがあったので*1しばらくやってみようというメモ。

C++はマクロがあるから、アクセサ書くぐらい大した手間じゃなさそうだし。特に目新しいことはやっていない、俺ルール用アクセサ定義マクロメモ。

#include <stdio.h>
#include <string.h>

/* 値コピー */
#define DEF_GETTER(T, var) T var() const {return _##var;}
#define DEF_SETTER(T, var) void var(T o) {_##var = o;}
#define DEF_ACCESSOR(T, var) \
  DEF_GETTER(T, var) DEF_SETTER(T, var)

/* 参照で */
#define DEF_GETTER_REF(T, var) const T& var() const {return _##var;}
#define DEF_SETTER_REF(T, var) void var(const T& o) {_##var = o;}
#define DEF_ACCESSOR_REF(T, var) \
  DEF_GETTER_REF(T, var) DEF_SETTER_REF(T, var)

/* ポインタで */
#define DEF_GETTER_POINTER(T, var) const T* var() const {return &_##var;}
#define DEF_SETTER_POINTER(T, var) DEF_SETTER_REF(T, var)
#define DEF_ACCESSOR_POINTER(T, var) \
  DEF_GETTER_POINTER(T, var) DEF_SETTER_POINTER(T, var)

template<typename T, size_t N>
size_t lengthof(T (&)[N]) {return N;}

/* 固定配列長取得 */
#define DEF_ARRAY_LENGTH(var) \
  size_t var##_length() const {return lengthof(_##var);}

/* 配列先頭 */
#define DEF_ARRAY_GETTER(T, var) const T* var() const {return _##var;}
#define DEF_ARRAY_SETTER(T, var) T* var() {return _##var;}
#define DEF_ARRAY_ACCESSOR(T, var) \
  DEF_ARRAY_GETTER(T, var) DEF_ARRAY_SETTER(T, var)

/* 配列要素:コピー */
#define DEF_ARRAY_AT_GETTER(T, var) \
  DEF_ARRAY_GETTER(T, var)\
  T var(size_t i) const {return _##var[i];}
#define DEF_ARRAY_AT_SETTER(T, var) \
  DEF_ARRAY_SETTER(T, var)\
  void var(size_t i, T o) {_##var[i] = o;}
#define DEF_ARRAY_AT_ACCESSOR(T, var) \
  DEF_ARRAY_AT_GETTER(T, var) DEF_ARRAY_AT_SETTER(T, var)

/* 配列要素:参照 */
#define DEF_ARRAY_AT_GETTER_REF(T, var) \
  DEF_ARRAY_GETTER(T, var)\
  T& var(size_t i) const {return _##var[i];}
#define DEF_ARRAY_AT_SETTER_REF(T, var) \
  DEF_ARRAY_SETTER(T, var)\
  void var(size_t i, const T& o) {_##var[i] = o;}
#define DEF_ARRAY_AT_ACCESSOR_REF(T, var) \
  DEF_ARRAY_AT_GETTER_REF(T, var) DEF_ARRAY_AT_SETTER_REF(T, var)

/* 配列要素:ポインタ */
#define DEF_ARRAY_AT_GETTER_POINTER(T, var) \
  DEF_ARRAY_GETTER(T, var)\
  const T* var(size_t i) const {return &_##var[i];}
#define DEF_ARRAY_AT_SETTER_POINTER(T, var) \
  DEF_ARRAY_AT_SETTER_REF(T, var)
#define DEF_ARRAY_AT_ACCESSOR_POINTER(T, var) \
  DEF_ARRAY_AT_GETTER_POINTER(T, var) DEF_ARRAY_AT_SETTER_POINTER(T, var)


/* 使ってみる */

struct Pos
{
  double x,y;
};

class Hoge
{
public:
  Hoge() {
    b(-2);
    strncpy(_name2, "abcdefghijklmnopqrstuvwxyz", name2_length());
    _name2[name2_length()-1] = '\0';
  }

  DEF_ACCESSOR(int, a)
  DEF_GETTER(int, b)
  DEF_ACCESSOR(const char*, name)

  DEF_ARRAY_LENGTH(f)
  DEF_ARRAY_AT_ACCESSOR(float, f)

  DEF_ARRAY_LENGTH(name2)
  DEF_ARRAY_GETTER(char, name2)

  DEF_ACCESSOR_REF(Pos, pos)

  DEF_ARRAY_LENGTH(pos2)
  DEF_ARRAY_AT_ACCESSOR_POINTER(Pos, pos2)

protected:
  DEF_SETTER(int, b)
  //DEF_ARRAY_SETTER(char, name2) // compile error

private:
  int _a;
  int _b;
  const char *_name;
  float _f[3];
  char _name2[10];
  Pos _pos;
  Pos _pos2[2];
};

int main(int argc, char** args)
{
  Hoge hoge;

  /* 基本型 */
  hoge.a(100);
  //hoge.b(100); // compile error
  printf("%d, %d\n", hoge.a(), hoge.b());

  /* ポインタ(文字列) */
  hoge.name("tanku");
  printf("%s, %d\n", hoge.name(), strlen(hoge.name()));

  /* 基本型の配列 */
  for (size_t i = 0; i < hoge.f_length(); ++i) {
    hoge.f(i, i*10);
    printf("%f\n", hoge.f(i));
    // 先頭のポインタは見れないほうがいいかもなぁ
    hoge.f()[i] = i;
    printf("%f,", hoge.f()[i]);
  }
  printf("\n");

  /* 基本型の配列(文字列) */
  printf("%s, %d/%d\n"
    , hoge.name2(), strlen(hoge.name2()), hoge.name2_length());

  /* 参照 */
  hoge.pos((Pos){10, 20});
  printf("%f, %f\n", hoge.pos().x, hoge.pos().y);

  /* 配列:setは参照渡し、getはポインタ */
  Pos pos[] = {{1000, 2000}, {3000, 4000}};
  for (size_t i = 0; i < hoge.pos2_length(); ++i) {
    hoge.pos2(i, pos[i]);
    printf("%f, %f\n", hoge.pos2(i)->x, hoge.pos2(i)->y);
  }

  return 0;
}

勢いあまって配列も書いてみたけど微妙だ。boost::array ありきで書いたほうがよかったか。

setter は template 使えば型指定いらないけど、コピペの都合で。

  //DEF_ARRAY_SETTER(char, name2) // compile error

あれー、これ解決できなくなるのか‥‥。get_x(), set_x() とかにすれば解決なんだけども。冗長だよなぁ。仕方ないのかなぁ。x() と set_x() にするのが無難なのか。protected の方はアクセサ作らなきゃいいとか‥‥最初の目的、忘れかけてるな。

あんまり演算子オーバーロードしない人なので(いいわけ)。Posにアクセサがないというツッコミ禁止(いいわけ2)。


さらに勢いあまって変数の定義ごとマクロ化してみたりとか。

class Huga
{
#define DEF_MEMBER(T, var, getmod, getter, setmod, setter, init)
#define DEF_MEMBERS \
  DEF_MEMBER(int, x, public, DEF_GETTER, public, DEF_SETTER, 111)\
  DEF_MEMBER(int, y, public, DEF_GETTER, private, DEF_SETTER, 222)\
  DEF_MEMBER(int, z, protected, DEF_GETTER, private, DEF_SETTER, 333)

#include "member.h"

public:
  Huga() {DEF_MEMBERS}
};


int main(int argc, char** args)
{
  Huga huga;
  huga.x(3000);
  printf("%d, %d\n", huga.x(), huga.y());
  return 0;
}

member.h

#undef DEF_MEMBER
#define DEF_MEMBER(T, var, getmod, getter, setmod, setter, init) T _##var;
  private:
  DEF_MEMBERS

#undef DEF_MEMBER
#define DEF_MEMBER(T, var, getmod, getter, setmod, setter, init) \
  getmod: getter(T, var)  setmod: setter(T, var)
  DEF_MEMBERS

#undef DEF_MEMBER
#define DEF_MEMBER(T, var, getmod, getter, setmod, setter, init) \
  _##var = init;

これはやりすぎか。初期化は汚くなるだろうと思いつつ、ついカッとなって。なんか g++ -E の結果みてたら無駄に楽しくなってきてしまった。初期化を除けば悪くない気も‥‥しないか。

C/C++のライブラリで各種命名をラクダ記法で書かれたライブラリが少ないのって、マクロにしづらいからなんだろうなー、という事に今さら気づいたわ。いや、もっと違う理由なのかもしれないけど。


# 追記

文字化けしてたからちょっと直した。

# 追記2

文字化けするの、その場編集したときだけっぽい?

*1:他の人の変なバグを追ってるときになんだかんだと