我正在创建一个DNS名称解析器。在解析返回的数据包时,我读取了 RR 类型,然后通过开关将其传递以初始化类记录的派生类型。记录类型是大型枚举类型。我使用模板专用化将枚举映射到结构。
template < QueryType n >
struct Struct;
template <> struct Struct< DNS_Q_A > { typedef A Type; };
template <> struct Struct< DNS_Q_CNAME > { typedef CNAME Type; };
template < QueryType n > struct Struct { typedef UNKNOWN Type; };
目前我有 4 个 switch 语句,它们都做了非常相似的事情。即调用操作员新,放置新,用于复制和移动c'tor。这是需要维护的大量代码。我只想有 1 个 switch 语句,我可以在其中传入某种类型的对象,其中包含要执行的函数、返回类型和 n 个参数。
switch 语句如下所示:
switch ( nType )
{
case DNS_Q_A:
pInstance = new ( &Container ) Struct< DNS_Q_A >::Type( dynamic_cast< const Struct< DNS_Q_A >::Type& >( Other ) );
break;
case DNS_Q_CNAME:
pInstance = new ( &Container ) Struct< DNS_Q_CNAME >::Type( dynamic_cast< const Struct< DNS_Q_CNAME >::Type& >( Other ) );
break;
}
如您所见,除了对结构类型的依赖之外,每种情况都是相同的。这对我来说是"模板",但我无法弄清楚如何传入对象。
我是否仅限于编码 4 个开关还是有办法?请不要引用"Boost",此代码必须独立于任何其他库。
解决方案:(感谢Jan Hudec)
template< template < class > class Action >
typename Action< Struct< DNS_Q_A >::Type >::result_type
CreateRecord (
unsigned n,
typename Action< Struct< DNS_Q_A >::Type >::first_argument_type arg1,
typename Action< Struct< DNS_Q_A >::Type >::second_argument_type arg2 )
{
typedef typename typename Action< Struct< DNS_Q_A >::Type >::result_type ReturnType;
switch ( n )
{
case DNS_Q_A: return static_cast< ReturnType >( Action< Struct< DNS_Q_A >::Type >()( arg1, arg2 ) );
case DNS_Q_NS: return static_cast< ReturnType >( Action< Struct< DNS_Q_NS >::Type >()( arg1, arg2 ) );
/*...*/
}
}
操作结构定义为:
template < typename T >
struct Copy : std::binary_function< storage_type&, const Record&, Record* >
{
Record* operator() ( storage_type& Storage, const Record& Obj )
{
return new ( &Storage ) T( dynamic_cast< const T& >( Obj ) );
}
};
template < typename T >
struct Move : std::binary_function< storage_type&, Record&&, Record* >
{
Record* operator() ( storage_type& Storage, const Record& Obj )
{
return new ( &Storage ) T( dynamic_cast< const T& >( std::move( Obj ) ) );
}
};
并将 Switch 语句替换为:
pInstance = CreateRecord< Copy >( nType, Container, Other );
您可以创建一个函数模板,该模板将执行函子模板。如果没有 C++11 可变参数,所有函子都需要相同数量的参数,因此您必须将它们打包到结构中或在它们不相关时传递 NULL。使用可变参数模板(我还没有使用它们,所以我不记得确切的语法,也不会在这里写它们),您可以轻松地在每种形式中拥有不同的参数。
这个想法就像(在我的头顶上,所以可能会有一些错别字):
template <template <typename T> class F>
F<Struct<DNS_Q_A>::Type>::return_type RecordCall(RecordType nType, const F<Struct<DNS_Q_A>::Type>::argument_type &arg)
{
switch(nType)
{
case DNS_Q_A:
return F<Struct<DNSK_Q_A>::Type>()(arg);
case DNS_Q_CNAME:
return F<Struct<DNSK_Q_CNAME>::Type>()(arg);
// ...
}
}
现在,您可以编写各个函数,例如:
template <typename T>
struct Clone : std::unary_function<BaseType *, const BaseType *>
{
BaseType *operator()(const BaseType *source)
{
return new<T>(dynamic_cast<const BaseType &>(*source));
}
}
并组合在一起:
target = RecordCall<Clone>(source->GetType(), source);
(并包装另一个函数,它将为多参数形式(如放置副本构造)插入 getter 和/或 pack 参数)
尽管对于复制,执行此操作的常见方法是使用虚拟克隆成员方法。但这不适用于建筑业。
编辑:请注意,我将内部模板中的返回和参数类型定义为typedefs(使用标准unary_function
帮助程序),但它们也可以作为单独的模板参数传递,特别是如果RecordCall
的使用将被包装在另一个函数中。也许返回类型甚至不必是模板参数,因为它将Record *
问题中提到的所有情况。
我必须承认,我不太明白这里无法通过什么来完成......愚蠢的虚拟函数。
class Object {
public:
virtual Object* clone() const = 0;
virtual void clone(Container& c) const = 0;
// ...
};
class A: public Object {
public:
virtual A* clone() const override { return new A(*this); }
virtual void clone(Container& c) const override { new (&c) A(*this); }
// ...
};
然后。。。只需摆脱开关:
pInstance = other.clone(container);
工作完成了。