C++でAny型もどきを実装してみる 拡張編

C++でAny型もどきを実装してみるの続きです。

目的は「整数型が入っているときに浮動小数で取得する、ができない」をどうにかすることです。

テンプレートの特殊化大会ですね。

対応する各変数型に対してテンプレートの特殊化を定義すれば済むが

それだとコードが大変な量になります。

整数型だけで

  • char,unsigned char
  • short,unsigned short
  • int,unsigned int
  • long,unsigned long
  • long long,unsigned long long

と10の型があります。これらの変換をすべて書いていたらたとえマクロを使っても10×10という数は莫大です。

ということで、何とか足し算にするためにベース型に対して

  • 内部型から整数値(符号あり、符号なし)に変換する関数
  • 内部型から浮動小数値に変換する関数

を設定することで乗算ではなく加算にして対応させよう、ということです。

というわけでコード

#ifndef __anytype_h__
#define __anytype_h__
#include <typeinfo>
#include <string>
#include <stdlib.h>
//anytypeによる例外
class anytype_nullreference_exception : public std::exception{
public:
	anytype_nullreference_exception(const char * _Message = "anytype null reference") : std::exception(_Message){ }
	anytype_nullreference_exception(const anytype_nullreference_exception &object) : std::exception(object){ }
	virtual ~anytype_nullreference_exception() { }
};
class anytype_badcast_exception : public std::bad_cast{
public:
	anytype_badcast_exception(const char * _Message = "anytype bad cast") : std::bad_cast(_Message){ }
	anytype_badcast_exception(const anytype_badcast_exception &object) : std::bad_cast(object){ }
	virtual ~anytype_badcast_exception() { }
};
//何でも入るクラス
class anytypeex;
class anytype{
protected:
	//内部値基本クラス
	class anybase{
	public:
		virtual ~anybase(){ } //デストラクタ
		virtual const std::type_info & type(void) const = 0; //型情報の取得
		virtual anybase *clone(void) const = 0; //複製を行う
		virtual long long getint(void) const { return 0; } //符号あり整数値を返す
		virtual unsigned long long getuint(void) const { return 0; } //符号なし整数値を返す
		virtual long double getfloat(void) const { return 0.0; } //浮動小数値を返す
	};
	//内部値保持基本クラス
	template <typename valtype> class anyitembase : public anybase{
	public:
		anyitembase(const valtype &value) : m_item(value) { } //コンストラクタ
		virtual ~anyitembase() { } //デストラクタ
		virtual const std::type_info & type(void) const { return typeid(valtype); } //型情報の取得
		virtual anybase *clone(void) const { return new anyitembase(m_item); } //複製を行う
		valtype & value(void) { return m_item; } //アイテムを取得する
	private:
		valtype m_item; //本体アイテム
	};
	//内部値保持クラス
	template <typename valtype> class anyitem : public anyitembase<valtype>{
	public:
		anyitem(const valtype &value) : anyitembase(value) { } //コンストラクタ
		virtual ~anyitem() { } //デストラクタ
	};
	//テンプレート処理
#define ANYITEM_VALUETYPE(valuetype)	\
	template<> class anyitem<valuetype> : public anyitembase<valuetype>{	\
	private: typedef anyitem<valuetype> holder;	\
	public:	\
		anyitem(const valuetype &value) : anyitembase(value) { }	\
		virtual ~anyitem() { }	\
		virtual long long getint(void) const { return static_cast<long long>(const_cast<holder *>(this)->value()); }	\
		virtual unsigned long long getuint(void) const { return static_cast<long long>(const_cast<holder *>(this)->value()); }	\
		virtual long double getfloat(void) const { return static_cast<long double>(const_cast<holder *>(this)->value()); }	\
	}
	//型定義
	ANYITEM_VALUETYPE(char); ANYITEM_VALUETYPE(unsigned char);
	ANYITEM_VALUETYPE(short); ANYITEM_VALUETYPE(unsigned short);
	ANYITEM_VALUETYPE(int); ANYITEM_VALUETYPE(unsigned int);
	ANYITEM_VALUETYPE(long); ANYITEM_VALUETYPE(unsigned long);
	ANYITEM_VALUETYPE(long long); ANYITEM_VALUETYPE(unsigned long long);
	ANYITEM_VALUETYPE(float); ANYITEM_VALUETYPE(double); ANYITEM_VALUETYPE(long double);
#undef ANYITEM_VALUETYPE
	//文字列型の場合の特殊処理
	template<> class anyitem<std::string> : public anyitembase<std::string>{
	private: typedef anyitem<std::string> holder;
	public:
		anyitem(const std::string &value) : anyitembase(value) { }
		virtual ~anyitem() { }
		virtual long long getint(void) const { return static_cast<long long>(atoi(const_cast<holder *>(this)->value().c_str())); }
		virtual unsigned long long getuint(void) const { return static_cast<long long>(atoi(const_cast<holder *>(this)->value().c_str())); }
		virtual long double getfloat(void) const { return static_cast<long double>(atof(const_cast<holder *>(this)->value().c_str())); }
	};
#ifdef _WIN32
	template<> class anyitem<std::wstring> : public anyitembase<std::wstring>{
	private: typedef anyitem<std::wstring> holder;
	public:
		anyitem(const std::wstring &value) : anyitembase(value) { }
		virtual ~anyitem() { }
		virtual long long getint(void) const { return static_cast<long long>(_wtoi(const_cast<holder *>(this)->value().c_str())); }
		virtual unsigned long long getuint(void) const { return static_cast<long long>(_wtoi(const_cast<holder *>(this)->value().c_str())); }
		virtual long double getfloat(void) const { return static_cast<long double>(_wtof(const_cast<holder *>(this)->value().c_str())); }
	};
#endif //_WIN32
public:
	//コンストラクタ
	anytype() : m_any(NULL) {  }
	//コピーコンストラクタ
	anytype(const anytype &value) : m_any(NULL)
	{
		if(value.m_any != NULL){
			m_any = value.m_any->clone();
		}
	}
	//コンストラクタ(値付き)
	template <typename valtype> anytype(const valtype &value) : m_any(new anyitem<valtype>(value)) { }
	//コピーオペレータ
	anytype & operator = (const anytype &value)
	{
		if(this != &value){
			if(m_any != NULL){ delete m_any; m_any = NULL; }
			if(value.m_any != NULL){ m_any = value.m_any->clone(); }
		}
		return *this;
	}
	anytype & operator = (const anytypeex &value)
	{
		*this = *reinterpret_cast<const anytype *>(&value);
		return *this;
	}
	template <typename valtype> anytype & operator = (const valtype &value) { set(value); return *this; }
	//値の設定
	template <typename valtype> void set(const valtype &value)
	{
		if(m_any != NULL){ delete m_any; }
		m_any = new anyitem<valtype>(value);
	}
	//値の削除
	void clear(void) { if(m_any != NULL){ delete m_any; m_any = NULL; } }
	//値の入れ替え
	void swap(anytype &value)
	{
		anybase *any = m_any; m_any = value.m_any; value.m_any = any;
	}
	//値の取得
	template <typename valtype> valtype & cast(void) const
	{
		if(m_any == NULL) throw anytype_nullreference_exception();
		if(m_any->type() != typeid(valtype)) throw anytype_badcast_exception();
		return static_cast<anyitem<valtype> *>(m_any)->value();
	}
	template <typename valtype> valtype & unsafecast(void) const
	{
		if(m_any == NULL) throw anytype_nullreference_exception();
		return static_cast<anyitem<valtype> *>(m_any)->value();
	}
	//型情報の取得
	const std::type_info & type(void) const
	{
		return m_any != NULL ? m_any->type() : typeid(void);
	}
	const char *name(void) const
	{
		return type().name();
	}
	//値を保持しているかどうか
	bool empty(void) const
	{
		return m_any == NULL;
	}
	
protected:
	anybase *m_any; //アイテム本体
};
//anytypeにstatic_castの応答をつけたもの
class anytypeex : public anytype{
public:
	//コンストラクタ
	anytypeex() { }
	
	//コピーコンストラクタ
	anytypeex(const anytypeex &value) : anytype(static_cast<const anytype &>(value)) { }
	
	//コンストラクタ(値付き)
	template <typename valtype> anytypeex(const valtype &value) : anytype(value) { }
	//コピーオペレータ
	anytypeex & operator = (const anytypeex & value)
	{
		static_cast<anytype &>(*this) = static_cast<const anytype &>(value);
		return *this;
	}
	anytypeex & operator = (const anytype & value)
	{
		static_cast<anytype &>(*this) = value;
		return *this;
	}
	template <typename valtype> anytype & operator = (const valtype &value) { set(value); return *this; }
	//static_cast時の応答処理
	template <typename valtype> operator valtype ()
	{
		if(m_any == NULL) return static_cast<valtype>(0);
		return static_cast<anyitem<valtype> *>(m_any)->value();
	}
#define GETVAL_INTEGER(inttype)	\
	operator inttype ()	\
	{	\
		if(m_any == NULL) return static_cast<inttype>(0);	\
		return static_cast<inttype>(m_any->getint());	\
	}	\
	operator unsigned inttype ()	\
	{	\
		if(m_any == NULL) return static_cast<unsigned inttype>(0);	\
		return static_cast<unsigned inttype>(m_any->getuint());	\
	}	
#define GETVAL_FLOAT(flttype)	\
	operator flttype ()	\
	{	\
		if(m_any == NULL) return static_cast<flttype>(0);	\
		return static_cast<flttype>(m_any->getfloat());	\
	}
	GETVAL_INTEGER(char); GETVAL_INTEGER(short); GETVAL_INTEGER(int); GETVAL_INTEGER(long); GETVAL_INTEGER(long long);
	GETVAL_FLOAT(float); GETVAL_FLOAT(double); GETVAL_FLOAT(long double);
#undef GETVAL_INTEGER
#undef GETVAL_FLOAT
};
#endif //__anytype_h__

一応前のコードとの互換性を保つために変換ができる型をanytypeex型としています。

変換をするために内部のanyitem型が大変な状況に・・・。かなり面倒なテンプレートの特殊化が行われています。

文字列型(といってもstringですが)まで数への変換ができるように定義してあります。

が、対象の型に対して整数型および浮動小数型を返す時の型が「扱える最大の型」となるように定義しています。

そのため、この処理を使うと一部の環境では速度的に問題が出る可能性もあります。

また、コピーオペレータの処理が大変ですね。

単一の型なら楽なのですが、複数の型になってしまったので相互のコピーオペレータの実装がややこしいです。

特にanytypeexからanytypeへの代入はトリックを使ってカバーしています。

使う側のコードはこんな風にかける

#include <iostream>
#include <string>
#include <vector>
#include "anytype.h"
using namespace std;
int main(void)
{
	anytypeex v1,v2,v3,v4;
	v1 = 10; v2 = 5.75f; v3 = string("50");
	v4 = static_cast<double>(v1) + static_cast<double>(v2);
	
	int n1 = static_cast<int>(v1);
	int n2 = static_cast<int>(v2);
	int n3 = static_cast<int>(v3);
	float f4 = static_cast<float>(v4);
	return 0;
}

static_castによる変換でかなりきれいに書けているのがわかると思います。

保持している変数の型が整数型だろうが浮動小数型だろうが文字列型だろうが無視できます。

ただし、これは整数型および浮動小数型への変換だけです。

それ以外の変換、特にポインタのダウンキャストが入るとかなりの場合で正しくないポインタを返すことになるのが問題となります。

static_castでポインタの変換を書くとアップキャストまたはダウンキャストができるはずなのですが、さすがにそこまではできないですね。

格納されているクラスそのものは知っていてもキャストをかませるところまでは・・・。

拡張はしてみたけれど、使えるのか?

速度の問題とコード量の問題が微妙ですね。

作成してはみましたが、実際に使うつもりはほとんどありません。使ってみたい人はどうぞ。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

この記事のトラックバック用URL