用于初始化派生类型的通用switch语句

时间:2012-06-26 05:51:45

标签: c++ templates polymorphism

我正在创建一个DNS名称解析器。在解析返回的数据包时,我读取了RR类型,然后通过switch传递它来初始化派生类Record的类型。记录类型是一种大型枚举类型。我使用模板特化将枚举映射到结构。

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'tors调用operator new,placement new。这是需要维护的大量代码。我想只有一个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 ) );
    /*...*/
    }
}

将Action结构定义为:

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 );

2 个答案:

答案 0 :(得分:2)

您可以创建一个函数模板,该模板将执行仿函数模板。如果没有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参数,如放置副本构造)

虽然复制常用的方法是使用虚拟克隆成员方法。但这不适用于建筑。

编辑:注意,我在内部模板中将返回和参数类型定义为typedef(使用标准unary_function帮助程序),但它们也可以作为单独的模板参数传递,尤其是在使用{时{1}}将包含在另一个函数中。也许返回类型甚至不必是模板参数,因为问题中提到的所有情况都是RecordCall

答案 1 :(得分:0)

我必须承认,我不太明白通过......虚拟虚函数无法实现的目标。

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);

并完成了工作。