开关箱运行时类型检查

Runtime type checking for switch case

本文关键字:检查 类型 运行时 开关箱      更新时间:2023-10-16

我正在查看bencode规范,我正在编写洪水比特流客户端的bencode实现的c++端口(用python编写)。Python实现包括一个{data_types: callback_functions}的字典,通过该字典,函数包装器可以轻松地通过字典查找提供给包装器函数的变量的数据类型来选择要使用的编码函数。我搜索了在运行时识别与python原语等价的c++变量类型,并发现typeid()可能提供了我正在寻找的东西。我知道我的代码不会存储typeid()函数返回值的结果,但我宁愿不依赖于提供编译器特定字符串文字的函数。我找到了一个解决方案,它允许我提供一种可移植的方法,将类型手动指定为字符串字面量,这对我来说似乎很难理解,模板对我来说是一场噩梦。

下面是使用类型选择适当函数的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(),并将子类变量转换为父类,如果我调用variable.name(),我还调用子类的name()函数调用吗?或者我是否会因为调用父类的name()函数而导致编译时错误?强制类型转换会改变类的作用域吗?

我认为我的问题源于对模板及其在c++中的用途的误解,以及对继承的缺乏理解。

模板入门

与动态Python相比,c++是一种静态类型语言。这意味着对象的类型在编译时是已知的(更多的区别在这里)。在c++中泛化代码的一种方法是模板,它为静态或编译时多态性提供了工具。让我们以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";} };
    

    我们得知,当type_name中的T类型为int时,成员函数name()返回"int"

  • 这里没有子类,你没有建立一个层次结构。name是类模板中的成员函数,您可以针对不同类型的模板参数对该类进行专门化。

大图

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

…通过字典查找提供给包装器函数的变量的数据类型,函数包装器可以轻松地选择使用哪个编码函数。

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

一个的例子是重载函数

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

另一个例子是使用模板和策略来参数化每种类型的行为。

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

  1. template<typename T> struct type_name { ... };正在定义模板类。如果没有它,下面的定义将没有任何意义,因为它们将专门化不存在的东西。
  2. 这些是特定类型的专门化。它们是而不是子类。
  3. 因为它不是继承,所以不能强制类型转换。

注: