Entry

プログラミングメモ - C++ で整数も浮動小数点数も格納できるクラスを作る

2011年03月10日

JSON や YAML のようなもんを読むとき,通常数値は整数と浮動小数点数を区別しません。もっとも,C++ では型をちゃんと意識しないといけないので,parse 後の内部構造をどう設計するか,ちと困ったちゃんだったのでした。普通は共用体なんかを使って,整数と浮動小数点数はメンバのフラグで制御するんだろうけれども,ちと取り回しが面倒です。

つことで,どっちも代入できるクラスを書いてみた。でも,こりゃほとんど Boost.Any とおんなじだわな。

全部は載せないけれど,こんな感じ。

class numeric {
private:
  template<typename T> friend T numeric_cast(const cute::numeric& rhs);
  friend std::ostream& operator<<(std::ostream& lhs, cute::numeric& rhs);
public:
  numeric() : content_(new holder<int>()) {}
  numeric(const numeric& other) : content_(other.content_->clone()) {}
  numeric(int value) : content_(new holder<int>(value)) {}
  numeric(double value) : content_(new holder<double>(value)) {}
  virtual ~numeric() {
    delete content_;
  }
public:
  numeric& operator=(const numeric& rhs) {
    if (this != &rhs) {
      numeric(rhs).swap(*this);
    }
    return *this;
  }
public:
  numeric& swap(numeric& other) {
    std::swap(content_, other.content_);
    return *this;
  }
public:
  const std::type_info& type() const {
    return content_->type();
  }
private:
  class placeholder {
  public:
    placeholder() {}
    virtual ~placeholder() {}
  private:  // no implement
    placeholder(const placeholder& other);
    placeholder& operator=(const placeholder& rhs);
  public:
    virtual placeholder* clone() const = 0;
    virtual const std::type_info& type() const = 0;
    virtual std::ostream& stream_out(std::ostream& lhs) = 0;
  };
  template<typename T>
  class holder : public placeholder {
  public:
    holder() : held_(T()) {}
    holder(const T& value) : held_(value) {}
    virtual ~holder() {}
  private:  // no implement
    holder(const holder<T>& other);
    holder& operator=(const holder<T>& other);
  public:
    virtual placeholder* clone() const {
      return new holder(held_);
    }
    virtual const std::type_info& type() const {
      return typeid(held_);
    }
    virtual std::ostream& stream_out(std::ostream& lhs) {
      return lhs << held_;
    }
  public:
    T get_value() const {
      return held_;
    }
  private:
    T held_;
  };
private:
  placeholder* content_;
};

Boost.Any は,デフォルトコンストラクタを呼んだ時,空のオブジェクトを作るわけですけれど,ここでは int 型をデフォルトの型として生成することにしました。んなもんで,boost::any::empty() のようなメソッドはありません。また,double と int しか取らないので,引数付きのコンストラクタはテンプレートにしなかった。

問題があるとすれば,遅いことだろうか。内部クラスの仮想関数呼び出しとメモリ割り当てのオーバーヘッドが問題になるんだろうけれども,特に後者はひどいと思います。C++ の new 演算子で割り当てるメモリは,小規模で多数のオブジェクトを生成するのに向きません。自前で小規模オブジェクトのアロケータを定義する必要がありそうです。小規模オブジェクトアロケータについては,Modern C++ Design に記述があります。

Modern C++ Design―ジェネリック・プログラミングおよびデザイン・パターンを利用するための究極のテンプレート活用術 (C++ In‐Depth Series)
Modern C++ Design
posted with amazlet at 11.03.10
アンドレイ アレキサンドレスク
ピアソンエデュケーション
売り上げランキング: 118557

つことで,作ったはいいけれど使い出からするとあまり役には経たないかも。一時的な確保場所にはよさげな感じ。演算子を定義すれば,それなりに使えるようになるのかも。

そういえば,最近コーディングスタイルを変えたのだった。なかなか良い感じ。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN