徒然なる日々を送るソフトウェアデベロッパーの記録(2)

技術上思ったことや感じたことを気ままに記録していくブログです。さくらから移設しました。

minosys script を作ろう (8)

スマートポインタの定義

boost の smart pointer を使ってもよいのですが、
折角なので、ここでは独自に smart pointer を作成してみます。

template <class T>
class PtrRef {
 private:
  int count;
  T *value;

 public:
  Ptr<T>(T *value) : count(0), value(value) {}
  Ptr<T>() : count(0), value(NULL) {}
  ~Ptr<T>() { delete value; }
  void inc() {
    ++count;
  }
  int release() {
    return --count;
  }
  T *get() const {
    return value;
  }
  T *getvalue() {
    return value;
  }
};

template<class T>
class Ptr {
 private:
  PtrRef<T> *ref;

 public:
  Ptr<T>() : ref(NULL) {}
  Ptr<T>(T *value) : ref(new PtrRef<T>(value)) {}
 ~Ptr<T>() {
    if (ref && ref->release() < 0) {
      delete ref;
    }
  }
  T *get() const {
    if (!ref) {
      return NULL;
    }
    return ref->get();
  }
  T *getvalue() {
    if (!ref) {
      return NULL;
    }
    return ref->getvalue();
  }
  Ptr<T>(const Ptr<T> &p) : ref(p.ref) {
    if (ref) {
      ref->inc();
    }
  }
  Ptr<T> &operator = (const Ptr<T> &p) {
    if (ref && ref->release() < 0) {
      delete ref;
    }
    ref = p.ref;
    if (ref) {
      ref->inc();
    }
    return *this;
  }
};

変数クラス Var

変数に相当するクラス Var を構築します。
変数には以下の型があります。
型ごとに有効になるメンバー変数が異なります。

意味 有効なメンバー
VT_NULL null値 なし
VT_INT 整数 inum
VT_DNUM 浮動小数点数 dnum
VT_STRING 文字列 str
VT_INST あるクラスのインスタンス inst
VT_POINTER anonymous pointer(FILE *など) pointer
VT_ARRAY 辞書配列 arrayhash
VT_FUNC 関数 func
VT_MEMBER Memeber関数 member

関数とメンバー関数が別扱いになっている理由は、両者が単なる
文字列では表現できないためです。

内容 変数型
VT_FUNC <関数名>.<関数名> pair< string, string>
VT_MEMBER <変数>.<関数名> pair< Ptr< Var>, string>

デフォルトパッケージ(実行時に要求されたパッケージ)は
別名として "" を持つものとします。

配列添字クラス VarKey

minosys script では配列添え字として整数、浮動小数点数、文字列の3つを
許可しています。ただし、これらは互いに独立で、相互変換されません。
(整数の 0 と文字列の "0" は異なるということです。)
unordered_map のキーとして使えるようにするため、いくつかのメンバー関数を
追加します。

struct VarKey {
  VTYPE vtype;
  union {
    int inum;
    int dnum;
    string *str;
  } u;
  VarKey(int inum) : vtype(VT_INT) { u.inum = inum; }
  VarKey(double dnum) : vtype(VT_DNUM) { u.dnum = dnum; }
  VarKey(const string &c) : vtype(VT_STRING) { u.str = new string(c); }
  VarKey(const VarKey &v) : vtype(v.vtype) {
    switch (vtype) {
    case VT_INT:
      u.inum = v.u.inum;
      break;

    case VT_DNUM:
      u.dnum = v.u.dnum;
      break;

    case VT_STRING:
      u.str = new string(*v.u.str);
      break;
    }
  }
  ~VarKey() {
    if (vtype == VT_STRING) {
      delete u.str;
    }
  }
  bool operator == (const VarKey &v) const {
    if (vtype != v.type) {
      return false;
    }
    switch (vtype) {
    case VT_INT:
      return u.inum == v.u.inum;

    case VT_DNUM:
      return u.dnum == v.u.dnum;

    case VT_STRING:
      return *u.str == *v.u.str;
    }
    return false;
  }
  bool operator != (const VarKey &v) const {
    return !(*this == v);
  }
  struct Hash {
    size_t operator()(const VarKey &v) {
      switch (v.vtype) {
      case VT_INT:
        return hash<int>()(v.u.inum);

      case VT_DNUM:
        return hash<double>()(v.u.dnum);

      case VT_STRING:
        return hash<string>()(*v.u.str);
      }
    }
    return 0;
  };
};

hash 関数を使うときに括弧()が必要になることに注意してください。

VarKey を使うと、変数 Var の辞書配列のメンバーは

unordered_map<VarKey, Ptr<Var>, VarKey::Hash> araryhash;

と表現されます。

次回は実行エンジンの構成について説明します。