为何而写:
为什么再谈一次,因为上次代码实在是不够好。上篇文章 对象工厂给出的代码太过拙劣,限于学识,我自己类型擦除技术仅仅是把对象的指针转换为void* 而已,实际上可以更为巧妙。这次利用新的类型擦出技术,给出一个完美的解决方,请看下文。
前情描述:
我为什么不直接保存用户提供的function的void*而非要把他copy一份利用容器vector储存起来,然后再去取其指针转换为void*。是因为用户传来的function可能是右值或临时值,一个马上要被删除的值,如果我们直接使用用户提供的function的void*,该指针很可能是悬垂指针。
- 首先,存储函数遇到了麻烦。因为一个容器只能存储相同类型的元素,所以我其实创建了许多不同类型的静态vector来存储不同类型的function。将vector转换为void*,把vector的void* ,索引值,键值一并将其加入map中。
- 其次,我在注销工厂函数时遇到了更大的麻烦(必须强迫用户出类型标示),因为我们已经失去了存在function容器的类型,同样失去了function具体类型,没有类型信息我无法删除容器中的元素。
就算以上两个问题都不是是问题,这样的代码实现方式真是拙劣。我自己都无法直视。
真正需求:
所以,我们需要一个能储存任何元素的容器,该容器类型唯一,这样我们解决了以上两个麻烦。
其一,我们不在需要创建多个容器,因为这个类型的容器可以储存任何类型的元素。
其二,我们的注销方法不在需要强迫用户给出具体的类型信息,因为容器知道如何删除元素,原因在于容器类型保留下来。
一个能储存任何元素类型的容器,在Java中也许是这样ArrayList<Object>,因为所有类均继承子Object。恩,在C++中如何实现,C++中缺少这样一个父类!
Boost:
早闻boost大名,但一直以为stl都没有完全搞懂,所以不想去研究boost,但boost真的能为我们提供一个可以储存任何类型的容器。他就是boost::any,我这里深入介绍boost::any,把源码贴出来分享给大家。
class any
{
public: // structors
any() BOOST_NOEXCEPT
: content(0)
{
}
template<typename ValueType>
any(const ValueType & value)
: content(new holder<
BOOST_DEDUCED_TYPENAME remove_cv<BOOST_DEDUCED_TYPENAME decay<const ValueType>::type>::type
>(value))
{
}
any(const any & other)
: content(other.content ? other.content->clone() : 0)
{
}
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
// Move constructor
any(any&& other) BOOST_NOEXCEPT
: content(other.content)
{
other.content = 0;
}
// Perfect forwarding of ValueType
template<typename ValueType>
any(ValueType&& value
, typename boost::disable_if<boost::is_same<any&, ValueType> >::type* = 0 // disable if value has type `any&`
, typename boost::disable_if<boost::is_const<ValueType> >::type* = 0) // disable if value has type `const ValueType&&`
: content(new holder< typename decay<ValueType>::type >(static_cast<ValueType&&>(value)))
{
}
#endif
~any() BOOST_NOEXCEPT
{
delete content;
}
public: // modifiers
any & swap(any & rhs) BOOST_NOEXCEPT
{
std::swap(content, rhs.content);
return *this;
}
#ifdef BOOST_NO_CXX11_RVALUE_REFERENCES
template<typename ValueType>
any & operator=(const ValueType & rhs)
{
any(rhs).swap(*this);
return *this;
}
any & operator=(any rhs)
{
any(rhs).swap(*this);
return *this;
}
#else
any & operator=(const any& rhs)
{
any(rhs).swap(*this);
return *this;
}
// move assignement
any & operator=(any&& rhs) BOOST_NOEXCEPT
{
rhs.swap(*this);
any().swap(rhs);
return *this;
}
// Perfect forwarding of ValueType
template <class ValueType>
any & operator=(ValueType&& rhs)
{
any(static_cast<ValueType&&>(rhs)).swap(*this);
return *this;
}
#endif
public: // queries
bool empty() const BOOST_NOEXCEPT
{
return !content;
}
void clear() BOOST_NOEXCEPT
{
any().swap(*this);
}
const boost::typeindex::type_info& type() const BOOST_NOEXCEPT
{
return content ? content->type() : boost::typeindex::type_id<void>().type_info();
}
#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
private: // types
#else
public: // types (public so any_cast can be non-friend)
#endif
class placeholder
{
public: // structors
virtual ~placeholder()
{
}
public: // queries
virtual const boost::typeindex::type_info& type() const BOOST_NOEXCEPT = 0;
virtual placeholder * clone() const = 0;
};
template<typename ValueType>
class holder : public placeholder
{
public: // structors
holder(const ValueType & value)
: held(value)
{
}
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
holder(ValueType&& value)
: held(static_cast< ValueType&& >(value))
{
}
#endif
public: // queries
virtual const boost::typeindex::type_info& type() const BOOST_NOEXCEPT
{
return boost::typeindex::type_id<ValueType>().type_info();
}
virtual placeholder * clone() const
{
return new holder(held);
}
public: // representation
ValueType held;
private: // intentionally left unimplemented
holder & operator=(const holder &);
};
#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
private: // representation
template<typename ValueType>
friend ValueType * any_cast(any *) BOOST_NOEXCEPT;
template<typename ValueType>
friend ValueType * unsafe_any_cast(any *) BOOST_NOEXCEPT;
#else
public: // representation (public so any_cast can be non-friend)
#endif
placeholder * content;
};
在上述代码中,关键是要理解placeholder和holder,实际上placeholder充当了Java中Object,作为万物的父类。holder是我们的类的一个包装,聚合我们的类对象。而any类持有一个执行包装类的父类指针。另外,在any类的实现这里用到了虚复制构造函数技术。
重头戏,对象工厂:
来吧,先把代码贴上来,talking is cheap,show you the code.
template <typename AbstractProduct ,typename IdentifierType = string> class Factory
{
public:
template <typename... Arg> bool Register(const IdentifierType& id,const function<unique_ptr<AbstractProduct>(Arg...)>& creator)
{
typename AssocMap::const_iterator i =associations_.find(id);
if(i!= associations_.end()) return false;
return associations_.insert(typename AssocMap::value_type(id,creator)).second;
}
bool UnRegister(const IdentifierType& id)
{
typename AssocMap::const_iterator i =associations_.find(id);
return associations_.erase(id)==1;
}
template <typename... Arg> unique_ptr<AbstractProduct> Createobject(const IdentifierType& id,Arg&&... args)
{
typename AssocMap::const_iterator i =associations_.find(id);
if(i != associations_.end())
{
auto funp=boost::any_cast<std::function<unique_ptr<AbstractProduct>(Arg...)> >(&(i->second));//i->second类型是boost::any
assert(funp);//如果无法转换,funp将是空指针
return (*funp)(std::forward<Arg>(args)...);//完美转发
}
assert(false);
}
private:
typedef std::unordered_map<IdentifierType,boost::any> AssocMap;//存储任意类型的map
AssocMap associations_;
};
代码瞬间简单了很多,我再关键位置加入了注释,上述代码使用 变长参数模板技术,完美转发,智能指针,unordered_map,所以要想通过编译,必须启用C++11。据我所知,不成形的C++17标准已经出来了,所以尽快熟悉新标准吧。当然还需要boost库支持。
boost 1.57 boost any不在需要使用RTTI,不适用标准的typeid而使用boost自带的。
template<typename ValueType>
ValueType * any_cast(any * operand) BOOST_NOEXCEPT
{
return operand && operand->type() == boost::typeindex::type_id<ValueType>()
? &static_cast<any::holder<BOOST_DEDUCED_TYPENAME remove_cv<ValueType>::type> *>(operand->content)->held
: 0;
}
你需要灵活性,则需要花费性能,灵活性不是没有代价的,我们的灵活性在于我的工厂支持注册任意形式调用形式,只要求函数的返回值,但我们使用RTTI和模板技术必然影响性能。具体要不要用,取决于实际需要。