Item 1: 理解模版类型推导

如果你正在使用一套复杂的系统,在没有明白其原理的情况下也能够正常使用,你可以认为这套系统设计的非常不错。按照这个思路,C++的模版类型推导绝对是巨大的成功。虽然这些使用模版的大部分程序员都很难描述出这些类型是怎么推导的,但是仍有数百万的程序员已经在使用模版来给函数传递参数并且得到正确的结果。

如果你也是其中之一,我将给你带来一个好消息和坏消息。好消息是C++的模版编程是现代C++编程中最酷炫的一个特性的基础:auto 关键字。如果你很喜欢C++98中模版类型推导,那么你也将同样喜欢 C++ 11 中 auto 的推导。而坏消息就是,auto 的推导规则看上去不如模版来的直观。因此,深刻理解 auto 所建立的模版类型推导十分的重要,它涵盖了你需要了解的知识点。

阅读更多 »Item 1: 理解模版类型推导

第一章 类型推导

C++98 只有一套类型推导规则,这套规则用于函数模版类型的推导。例如:

template <typename T>
void Print(T v) {
  std::cout << v;
}

Print(1);  // T 推导成 int
Print(1.0f);  // T 推导成 float

C++11 修改了一些规则,并且增加了两套新规则,一套用于 auto 的推导, 一套用于 decltype 的推导。然后,C++14 又扩展了 auto 和 decltype 的用法。类型推导的广泛应用使得编程人员不需要在拼命敲打各种类型的单词,并且增加了 C++ 程序的适应性,只要在源代码的一个地方修改,类型推导就可以自动传播到代码的其他相关部分。然后,这样的写法会让代码更加难以理解,毕竟编译器的类型推导可能与你想象的情况不太一样。

阅读更多 »第一章 类型推导

1 模板参数表指定类型判断

  • rttr/detail/misc/misc_type_traits.h

判断一个类型是不是在类型列表中

比如有这样一个场景,有 as_raw_pointer, as_object, as_std_shared_ptr 三种类型,需要判断用户模板传入的一个类型是不是这三个其中的一个。

struct as_raw_pointer {};
struct as_object {};
struct as_std_shared_ptr {};

using constructor_policy_list = type_list<as_raw_pointer, as_object, as_std_shared_ptr>;

// 希望得到的方式
template <T>
struct Check {
    // 比如这里需要判断这个T是不是 as_raw_pointer, as_object, as_std_shared_ptr 其中的一种。
}
阅读更多 »1 模板参数表指定类型判断

8 type注册

  • rttr/detail/type/type_register.cpp

type data构造完了后会加入到registration_manager,通过调用registration_manager的单例的add_item函数。

type_data* add_item(std::unique_ptr<type_data> obj)
{
    auto reg_type = type_register::register_type(obj.get());
    const auto was_type_stored = (reg_type == obj.get());
    if (was_type_stored)
        m_type_data_list.push_back(std::move(obj)); // so we have to unregister it later
    return reg_type;
}
阅读更多 »8 type注册

9 class注册

类的注册,通过调用 registraton::class_<T>(“name”),尖括号内是要注册的类型,小括号内是自定义名称。 注册过程首先会调用 type::get<T>(),拿到属于这个类的type实例,然后调用detail::type_register::custom_name(t, name),将类与自定义的名称进行绑定,绑定完以后在后面的代码里面就可以通过 type::get_by_name(“class name”) 来获取该类的type实例,而无需通过类本体来获取。

custom_name 实际是调用 type_register_private 的register_custom_name 来实现的,register_custom_name首先调用update_custom_name,它的功能就是修改m_type_data上的name,但是不修改m_type_data的type_name。然后以name为key放入map,这样就可以通过自定义的名称来寻找type了,也就是通过字符串反射类型。

阅读更多 »9 class注册

7 type_data构造

  • rttr/type.h
  • rttr/wrapper_mapper.h
  • rttr/detail/type/type_impl.h
  • rttr/detail/type/type_data.h
  • rttr/detail/type/type_data.cpp
  • rttr/detail/impl/wrapper_mapper_impl.h

type实例的保存在第一章已经讲过,是通过模板类的单例来实现的,因此创建type实例的过程就是通过 type::get<T> 的方式去创建,既可以是主动的,也可以是被动的。

  • 被动就是在使用的时候去get一个type,如果从程序启动开始一直没有去get这个类型的type,那么rttr就会创建一个。
  • 主动就是在程序初始化的某一刻,将需要的type全部都get一遍,这样,在程序真正运行过程中就不需要去创建这些类型了。这种方式在后面注册class的时候会用到。
阅读更多 »7 type_data构造

5 variant policy代码解读1

  • rttr/detail/variant/variant_data_policy.h

1. variant_data_policy_empty

variant_data_policy_empty用作构造空的variant的时候,也就是variant构造时不传参数时m_policy使用的默认policy,因此这个policy也没有太多的可操作的部分,基本上取值就是返回nullptr,取类型拿到的就是invalid_type,也没有提供create的操作,因此这个policy不做详解。

2. variant_data_policy_void

variant_data_policy_void用于操控void类型的变量,对于void类型来说,大多数的操作都有没有意义的,比如create,clone,swap,接下来只解读有意义的操作,具体的源代码可以去查阅 variant_data_policy_void::invoke

阅读更多 »5 variant policy代码解读1

4 variant policy选择调用

  • rttr/detail/variant/variant_data_policy.h

variant_data_policy的选择

variant_policy的功能就是对包裹在variant里的数据本体进行操作,由于C++的数据类型过多,rttr将policy分成了以下9种。

  • variant_data_policy_empty //构造policy时的默认值
  • variant_data_policy_void // void类型policy
  • variant_data_policy_nullptr_t // nullptr类型的policy
  • variant_data_policy_string // std::string类型和一维char类型的policy
  • variant_data_policy_arithmetic // 算术类型的policy(int, short …)
  • variant_data_policy_small // 小容量类型的类, 容量小于variant_data
  • variant_data_policy_array_small // 小容量的数组,容量小于variant_data
  • variant_data_policy_big // 大容量的类,容量大于 variant_data
  • variant_data_policy_array_big // 大容量类型,超过variant_data容量的类型

对于给定的类型,首先要给定这个类型选用合适的policy,判定方法如下。下面的代码看似很吓人,其实用逻辑代码来表示的话无非就是套了好几层的 if-else,注释也都写在里面了,最终的目的就是从上述的9个policy里面找到一个合适 T类型 的policy。

阅读更多 »4 variant policy选择调用