Entry

プログラミングメモ - 動的多態の静的呼び出し

2011年06月14日

ダウンキャストみたいなことを dynamic_cast なしでやりたいとゆ場合がたまにあるけれども,基本的な枠組みをメモっておこうと思ったのでメモ。モノによっては静的にも書けるんだけれども,関数テンプレートが特殊化できるようになったとしても,結局ほとんどすべての場合を記述しないといけないような場合(キャストの方法がいちいち違う場合)は,かえってコードが複雑になってしまいます。

とゆことで,一応実装例。基本的な枠組みだけなので,メモリの受け渡しに伴うコスト等々は,まったく考慮していません。

#include <cstdlib>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>

class base {
public:
  // c-tor, d-tor, ...
public:
  virtual void get_value(std::string& /* str */) const {
    throw std::logic_error("logically bad cast");
  }
  virtual void get_value(int& /* num */) const {
    throw std::logic_error("logically bad cast");
  }
};

class integer : public base {
public:
  integer() = delete;
  integer(int value) : value_(value) {};
  integer(const integer& other) = delete;
  virtual ~integer() {};
public:
  integer& operator=(const integer& rhs) = delete;
public:
  // override
  void get_value(std::string& str) const {
    std::stringstream ss;
    ss << value_;
    str = ss.str();
  }
  void get_value(int& num) const {
    num = value_;
  }
private:  
  int value_;
};

class string : public base {
public:
  string() = delete;
  string(const string& other) = delete;
  string(const std::string& value) : value_(value) {}
  virtual ~string() {}
public:
  string& operator=(const string& rhs) = delete;
public:
  // override
  void get_value(std::string& str) const {
    str = value_;
  }
  void get_value(int& num) const {
    char* err = 0;  // dummy
    num = std::strtol(value_.c_str(), &err, 10);
  }
private:  
  std::string value_;
};

template<typename Tp_>
Tp_ foobar_cast(const base& other) {
  Tp_ value;
  other.get_value(value);
  return value;
}

template<typename Tp_>
Tp_ foobar_cast(const base& other, const Tp_& default_value) {
  Tp_ value;
  try {
    other.get_value(value);
  } catch (const std::logic_error&) {
    value = default_value;
  }
  return value;
}

////////////////////////////////////////////////////////////////////////////////
void
print(const base& b) {
  std::cout << foobar_cast<int>(b) << std::endl;
  std::cout << foobar_cast<std::string>(b) << std::endl;
}

int
main(int argc, char* argv[]) {
  // i と s は本来動的に生成される値で,基底(base)のインターフェイスのみ
  // を通じてアクセスできる。
  integer i(123);
  string s("246");
  print(i);
  print(s);

  return 0;
}

基底(base)の get_value() が例外を投げるので,派生クラスで不正なキャストを実装しなければ,bad_cast 的に扱うことができる。dynamic_cast は制御に使っちゃいけないわけだけれども,じゃどうすんのさというと,結局上のような話になるのだと思う。

値を取り出すところが仮想になってるから,foobar_cast なんて使わなくても,get_value を直接呼び出せばいいんだけれども,デフォルト値を返すようにフックしたい場合なんかは,上のようにテンプレートを使って,取り出し方を少しだけ抽象化しておくと楽なのかもしれない。上の例は,メンバの中身の値でキャストしているけれども,当然のことながら,派生クラスにキャストすることもできます。それにしても,コピーが多いな。

ま,ただそれだけ。

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