交换机案例的运行时类型检查

时间:2014-07-01 20:37:00

标签: python c++ dictionary types porting

我正在查看bencode规范,我正在编写一个大洪流bit-torrent客户端的bencode实现的C ++端口(用python编写)。 Python实现包括{data_types:callback_functions}的字典,通过该字典,函数包装器可以通过查询提供给包装函数的变量的数据类型的字典查找来轻松选择要使用的编码函数。 我在C ++等效的python原语运行期间搜索了标识变量类型,并发现typeid()可能提供了我正在寻找的东西。我知道我的代码不会存储typeid()函数返回值的结果,但我宁愿不依赖于提供编译器特定字符串文字的函数。我找到了一个solution,它允许我提供一种可移植的手段来手动指定类型作为字符串文字,我似乎很难把头包起来,模板对我来说是一场噩梦。

以下是使用类型选择适当函数的Python代码:

encode_func = {}
encode_func[Bencached] = encode_bencached
encode_func[IntType] = encode_int
encode_func[LongType] = encode_int
encode_func[StringType] = encode_string
encode_func[ListType] = encode_list
encode_func[TupleType] = encode_list
encode_func[DictType] = encode_dict
try:
    from types import BooleanType
    encode_func[BooleanType] = encode_bool
except ImportError:
    pass

def bencode(x):
    r = []
    encode_func[type(x)](x, r)
    return ''.join(r)

以下是我发现的C ++示例,它允许显式类类型枚举:

struct Foobar;

template<typename T> struct type_name
{
    static const char* name() { static_assert(false, "You are missing a DECL_TYPE_NAME"); }
};

template<> struct type_name<int> { static const char* name() {return "int";} };
template<> struct type_name<string> { static const char* name() {return "string";} };
template<> struct type_name<Foobar> { static const char* name() {return "Foobar";} };

int main()
{
        cout << type_name<int>::name();  // prints "int"
    cout << type_name<float>::name();  // compile time error. float wasn't declared.
}

如果在我的课程中适当地实现了这一点,我相信我可以使用switch(variable.name())来选择用于对值进行编码的函数。

我有三个具体问题:

  1. 父类template<typename T> struct type_name { ... };函数中name()的用途是什么?我真的不明白它是什么,模板是否有范围?如果被调用,为什么name()函数只能static_assert()编译错误?
  2. 为什么父类中的template<> struct type_name<type> { static const char* name() {return "type";} }值?简单地为每个不同类型的子类重载name()函数会不会更清楚?
  3. 如果我为每个子类重载name(),并将子类变量强制转换为父类,是否仍然调用子类的name()函数调用我打电话给variable.name()?或者我会导致编译时错误,因为我调用了父类的name()函数?铸造会改变类的范围吗?
  4. 我认为我的问题源于对模板的错误理解及其在C ++中的目的,以及对继承的理解不足。

2 个答案:

答案 0 :(得分:1)

模板的入门

与动态Python相比,C ++是一种静态类型语言。这意味着对象的类型在编译时是已知的(更多关于差异here)。

在C ++中概括代码的方法是模板,它为static or compile time polymorphism提供工具。要了解一个min函数的Python示例:

def min(a, b) : 
    if (a < b) : 
        return a
    return b

要用C ++翻译,你会说

int min(int a, int b) {
    return a < b ? a : b;
}

但问题是这个翻译是不准确的:它只适用于整数(以及任何可转换为整数的类型,这可能会导致截断)

克服这个问题的一种方法是为所有类型定义重载:

double min(double a, double b); // the definition would be the same as above
float  min(float  a, float  b);

但这将是乏味且容易出错并且远非通用的。更好的解决方案是使用模板并概括任何类型T(支持<运算符)的函数:

template<typename T>
T min(T a, T b) { return a < b ? a : b; }

或者更好的是,当两个参数具有不同的类型时,您甚至可以处理这种情况:

template<typename T1, typename T2>
auto min(T1 a, T2 b) -> decltype(a+b) { return a < b ? a : b; }

模板是一种强大的机制,我甚至无法开始提供链接和来源;你必须通过这种方式谷歌。

关于具体问题

  • 语法template<> struct<Atype> { ...称为模板(完整)专门化。它的作用是强制为特定模板参数布置类模板。例如:

    template<> struct type_name<int> { static const char* name() {return "int";} };
    

    我们被告知,当Ttype_name的类型为int时,成员函数name()将返回“int”

  • 此处没有子类,您没有构建层次结构。 name是类模板中的成员函数,您专门针对不同类型的模板参数使用此类。

大局

作为一种静态类型语言,C ++可以为此提供更清晰的解决方案:

  

...函数包装器可以通过字典查找提供给包装函数的变量的数据类型来轻松选择要使用的编码函数。

您可以根据类型直接参数化行为。

示例将重载您的功能

void Encode(int arg)       { /* calls encode_int on arg */  }
void Encode(TupleType arg) { /* calls encode_list on arg */ }

其他示例是使用模板和策略来参数化每种类型的行为。

答案 1 :(得分:0)

您在这里看到的不是正常继承中的父子关系,而是specialization。模板定义了默认实现,并且特殊化替换了与特定类型匹配的参数的实现。所以:

  1. template<typename T> struct type_name { ... };正在定义模板类。如果没有它,以下定义都没有任何意义,因为它们会专门化一些不存在的东西。
  2. 这些是特定类型的专业。他们是子类。
  3. 因为它不是继承,所以你不能施展。
  4. P.S。您无法打开字符串,只能打开整数类型。