浅析Any Type的实现

 

首先,Any Type是一种类型,可以用于表达任何类型,如简单类型中的int, float, double 以及自定义的类。Any Type常用于数据库操作,如用于表达查询数据库的输出。

其次,Any Type肯定是一种容器。Any Type是一种抽象概念,要表示任何类型,这种定义肯定不可能,只能是“能够容纳任何类型”,比如STL的容器。虽然STL容器能够满足这种需要,但STL容器缺少一层“类型”的内容,即所谓的cast操作。如果我用AnyType使用为我的类myClass,必须能够把AnyType转换为myClass,并且,如果是转换为otherClass等其他类型时能跑出异常。

综上所述,实现AnyType的两个关键点是:

  • AnyType是容器;
  • 需要定义cast操作。

1容器AnyType

容易误导的是:由于要容纳任意类型,所以AnyType肯定是一个模板类,利用C++的模板实现。但是这样的结果是无处不在的类型指定,例如vector<int>

当然可以typedef 但是在每次实例化的时候typedef稍显累赘。

解决之道在于把AnyType定义为一般的类,但是构造函数是模板函数。如boosthttp://www.boost.org/libs/any)的实现:

 

        template<typename ValueType>

        any(const ValueType & value)

          : content(new holder<ValueType>(value))

        {

        }

模板参数是值类型ValueType。从上面的构造函数中可以看输出,此构造的作用是把一个值转换为一个内部的类型变量content。那么,这个内部类型应该用什么呢?这里应该有三种选择:

1) ValueType* content

2) void * content

3) SomeClass* content

如果AnyType是模板类,我们可以使用1),可惜不是。

如果用2)类型信息就全部丢失了,随后的cast就不能实现了。

最后只剩下3)了,也即用类来承载。那么这个类具有什么特征呢?有哪些接口呢?

第一,为了封装性,这个类应该是AnyType类的内部类,也即指在AnyType内部可见。

第二,这个应该是模板类,因为它将承载任意类型;这里不需要再次使用模板构造函数的方法,因为它是以内部类。

第三,这个类必须有一个接口来提取类型信息,用于类型转换。

第四,这个类必须有一个接口来复制自己,方便使用者拷贝复制,实现深拷贝。

 

基于面向接口的设计思想,可以先定义借口,再定义实现。

 

        class placeholder

        {

        public: // structors

 

            virtual ~placeholder()

            {

            }

 

        public: // queries

 

            virtual const std::type_info & type() const = 0;

 

            virtual placeholder * clone() const = 0;

 

        };

 

看看boost的实现:类holderValueType成员held记录值;用标准C++typeid来取类型信息;调用ValueType的拷贝构造来clone

 

 

template<typename ValueType>

        class holder : public placeholder

        {

        public: // structors

 

            holder(const ValueType & value)

              : held(value)

            {

            }

 

        public: // queries

 

            virtual const std::type_info & type() const

            {

                return typeid(ValueType);

            }

 

            virtual placeholder * clone() const

            {

                return new holder(held);

            }

 

        public: // representation

 

            ValueType held;

 

        };

到这里,AnyType已经成功的保存了任何类型的value;剩下的工作就是提供转换的接口。

2类型转换

 

说到类型转换,标准C++4个常用的类型转换符:

  • static_cast
  • dynamic_cast
  • reinterpret_cast
  • const_cast

4类操作符对指针和引用进行转换。这里可以借助static_cast实现AnyTypeValueType的转换;当然,这是一个模板函数。原理很简单:取出AnyTypecontentheld的地址即可。由于boost多了一次接口placeholder,所以需要转换成其派生类指针之后才使用。这里boost使用的是static_cast而非dynamic_cast,这应该是错误的。dynamic_cast用于继承体系中的向下转换,用在这里正好合适;但是

template<typename ValueType>

    ValueType * unsafe_any_cast(any * operand)

    {

        return &static_cast<any::holder<ValueType> *>(operand->content)->held;

    }

 

 

 

 

 

 

 


dynamic_caststatic_cast的效率稍低。static_cast在更宽的范围内完成映射,但往往是不安全的。

这点瑕疵暂且忽略。为何说是不安全的呢?这是因为它没有检测AnyTypetypeid是否跟ValueType一致就武断的映射了。对比一下如下版本:

template<typename ValueType>

    ValueType * any_cast(any * operand)

    {

        return operand && operand->type() == typeid(ValueType)

                    ? &static_cast<any::holder<ValueType> *>(operand->content)->held

                    : 0;

    }

 

 

 

 

 

 

 

 

 


唯一的不同施加了typeid的比较。

3总结

1)      要实现AnyType,必须使用模板类;为了使用方便,把模板类定义为AnyType的内部类;再AnyType的模板构造函数实例化该模板类。

2)      基于RTTItypeid记录类型信息,用于随后的转换操作。

 

 
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐