Boost.ASIO源码:service_registry::use_service()详解以及相关type_traits解析

这都是神仙写的代码吧

没什么,这个标题只是忍不住表达一下对ASIO的惊叹。
曾经看《STL源码剖析》对里面的type_traits的设计惊为天人,没想到看ASIO库的时候又看到了同样的设计模式,虽然对于C++功底还不深的我来说看起来十分的费劲,但我还是决定好好的自己理解一遍,并把它记录下来。由于不喜欢黑盒法,所以几乎每个函数我都会跳进去瞅瞅,所以虽然use_service逻辑不复杂,但这里介绍依旧会很细。

use_service()的功能与内部逻辑

首先,解释一下,use_service存在于execution_context.hpp中,是一个全局模板函数,用于得到一个指定type的Service对象。以下为函数声明:

template <typename Service> Service& use_service(execution_context&);
template <typename Service> Service& use_service(io_context&);

Service是ASIO中的一种概念,如scheduler和epoll_reactor都是Service,其实Service具体是啥我也没有理解得特别深 ,但它们都有一个明显的共同点,就是都继承自execution_context::service

下面回到use_service函数上来,这里以一个use_service的使用情况来开始说明。

// scheduler类的一个成员函数
void scheduler::init_task()
{
  mutex::scoped_lock lock(mutex_);
  if (!shutdown_ && !task_)
  {

    task_ = &use_service<reactor>(this->context());  //关注这一行就行了
    /* typedef class epoll_reactor reactor; */  // 这是在一个遥远的山卡拉角落对于reactor的定义
    
    op_queue_.push(&task_operation_);
    wake_one_thread_and_unlock(lock);
  }
}

先说明作用,那一句能得到一个reactor实例并赋给task_,这个reactor就是epoll_reactor类,它也是个execution_context::service的子类。
进入这个use_service函数内部:

template <typename Service>
inline Service& use_service(execution_context& e)
{
  // Check that Service meets the necessary type requirements.
  (void)static_cast<execution_context::service*>(static_cast<Service*>(0));

  return e.service_registry_->template use_service<Service>();
}

对于中间那一行看起来执行不了任何功能的连续强转刚开始我也是一头雾水,后来才发现实际上这行就是保证传进来的这个模板类型Service,是execution_context::service的子类,如果这个Service实际上是vector或者double之类的类型,那么这一句就会报编译错误。
嗯很巧妙的写法,看起来效率很高。不过为什么不用 template <typename Service extends execution_context::service>的写法呢。希望有人能给我指正一下。
同时在这里能看出来传进来的这个execution_context对象是干啥的了,在return 那一句可以看到它取出了这个execution_context对象的service_registry_,并转而调用这货的use_service。下面为了解释service_registry这个成员,截出了execution_context类中的两个个片段:

private:
  // The service registry.
  boost::asio::detail::service_registry* service_registry_;
execution_context::execution_context()
  : service_registry_(new boost::asio::detail::service_registry(*this))
{
}

execution_context::~execution_context()
{
  shutdown();
  destroy();
  delete service_registry_;
}

每一个execution_context对象都与一个service_registry对象互相绑定,他们生一起生,死一起死。而这个service_registry类是用来管理所有的execution_context::service对象的,具体怎么管理这里不细讲了,反正就是个管理器。
再进到这个service_registry的use_service方法中:

template <typename Service>
Service& service_registry::use_service(io_context& owner)
{
  execution_context::service::key key;   // 这个key对每种类型的Service是唯一的。
  init_key<Service>(key, 0);   // 根据Service类型初始化(也可以说得到)它的key。这句是关键!!
  factory_type factory = &service_registry::create<Service, io_context>;   // 这里得到一个创建Service的方法
  return *static_cast<Service*>(do_use_service(key, factory, &owner));//具体创建还是不创建再说,反正这句能得到一个目标Service对象
}

这里先不探究key长啥样,反正知道它对每种类型的Service是唯一的就行了。init_key是关键,它能根据Service的类型信息得到一个唯一的key(令人惊艳的是大部分判断逻辑都是编译时完成,也就是type_traits思想,太6了),后面解释内部逻辑,内部涉及到了type_traits机制。方法体内第三行得到的是一个方法指针,这个方法可以用来创建一个Service对象:

  // The type of a factory function used for creating a service instance.
  	typedef execution_context::service*(* factory_type )(void*);  // 在另一个角落对factory_type的定义
	
	// ... 十万光年的距离

	template <typename Service, typename Owner>
	execution_context::service* service_registry::create(void* owner)
	{
	  return new Service(*static_cast<Owner*>(owner));
	}

回到service_registry的use_service函数,里面最后一行的do_use_service方法逻辑其实非常简单,简单到不想贴那一坨代码来增加我的篇幅。service_registry中维护了一个execution_context::service链表(其实每个execution_context::service都有个next指针),do_use_service就是到service_registry中找它里面有没有保存这个类型的Service对象,有就拿出来,没有就用传进来的factory方法创建一下,并把这个owner也就是某个execution_context(或者说io_context)给绑到这个创建出来的Service上(execution_context::service的构造函数要传入一个execution_context对象)。

init_key()详解

先看代码:

	// Initalise a service's key when the key_type typedef is not available.
	template <typename Service>
	inline void service_registry::init_key(
	    execution_context::service::key& key, ...)
	{
	  init_key_from_id(key, Service::id);
	}

	// Initalise a service's key when the key_type typedef is available.
	template <typename Service>
	void service_registry::init_key(execution_context::service::key& key,
	    typename enable_if<
	      	is_base_of<typename Service::key_type, Service>::value>::type*)
	{
	  key.type_info_ = &typeid(typeid_wrapper<Service>);
	  key.id_ = 0;
	}

	// Initalise a service's key when the key_type typedef is available.
	template <typename Service>
	void service_registry::init_key_from_id(execution_context::service::key& key,
	    const service_id<Service>& /*id*/)
	{
	  key.type_info_ = &typeid(typeid_wrapper<Service>);
	  key.id_ = 0;
	}

这里init_key方法有2个重载,如果Service有定义Service::key_type这个字段,就走那个参数一大坨的重载,反之,就走那个参数列表比较短的重载。如果没有定义Service::key_type,就会根据Service::id来初始化Service的key。
接下来稍微解释一下这个key。execution_context::service::key就那两个属性:type_info_和id_。type_info_比较好理解,就是STL里面那个type_info,描述类型信息的,type_wrapper是一个模板空类,(我猜)应该是为了简化typeid返回的type_info中数据。另一个id_就是一个execution_context的内部类id的对象,这个execution_context::id类是空的,也是不可复制的(继承自noncopyable),至于用法现在还没看懂。。。为了方便理解再贴上key的匹配方法:

扫描二维码关注公众号,回复: 4571788 查看本文章
	bool service_registry::keys_match(
	    const execution_context::service::key& key1,
	    const execution_context::service::key& key2)
	{
	  if (key1.id_ && key2.id_)
	    if (key1.id_ == key2.id_)
	      return true;
	  if (key1.type_info_ && key2.type_info_)
	    if (*key1.type_info_ == *key2.type_info_)
	      return true;
	  return false;
	}

可以看出优先匹配id_,再匹配type_info_。

enable_if和is_base_of中的细节

这是我最感兴趣的一部分,虽然与use_service的主要逻辑关系并不大,但还是想仔细研究下。
先把关键代码再贴上来一次:

template <typename Service>
void service_registry::init_key(execution_context::service::key& key,

	// 就是这里
    typename enable_if<
      is_base_of<typename Service::key_type, Service>::value>::type*)
      
{
  key.type_info_ = &typeid(typeid_wrapper<Service>);
  key.id_ = 0;
}

先解释enable_if,先上代码:

  template<bool, typename _Tp = void>
    struct enable_if 
    { };

  template<typename _Tp>
    struct enable_if<true, _Tp>
    { typedef _Tp type; };

可以看到下面那个是一个偏特化版本,其中也只有偏特化版本才有type的定义。可以看到enable_if的调用都是typename enable_if <[一坨东西]>,如果这传过来的[一坨东西]是个bool值,那么实例化上面那个版本,如果传进来的是bool以外的什么鬼东西,就走下面那个版本。
这里涉及到一个概念SFINAE:Substitution Failure Is Not An Error(匹配失败不是错误),翻译成普通话就是在进行模板特化的时候,会去选择那个正确的模板,避免失败。以我们的例子来说明,前面说过init_key有两个版本,当我们调用init_key(key, 0)时,按道理会匹配enable_if那个版本的init_key重载,但是编译器在根据我们传进来的参数实例化enable_if模板时发现实例化出来的类没有type定义(实例化出来的是上面那个enable_if版本),但我们却倔强地用了enable_if<[一坨东西]>::type*,照理说这里是要报错的,但按照SFINAE原则,这不算错,于是编译器放弃这个函数重载版本,用另一个init_key的重载版本,于是程序得以正常运行。
接下来看is_base_of,以及它的基类integral_constant:

	template<typename _Base, typename _Derived>
	    struct is_base_of
	    : public integral_constant<bool, __is_base_of(_Base, _Derived)>
	    { };
template<typename _Tp, _Tp __v>
    struct integral_constant
    {
      static constexpr _Tp                  value = __v;
      typedef _Tp                           value_type;
      typedef integral_constant<_Tp, __v>   type;
      constexpr operator value_type() const { return value; }

      constexpr value_type operator()() const { return value; }
    };

  /// The type used as a compile-time boolean with true value.
  typedef integral_constant<bool, true>     true_type;

  /// The type used as a compile-time boolean with false value.
  typedef integral_constant<bool, false>    false_type;

这里引用一下网上对integral_constant的解释:用integral_constant方便地定义编译期常量,而无需再通过enum和static const变量方式。
is_base_of实际上只用到了下面的2个偏特化版本的integral_constant:true_type和false_type,根据__is_base_of返回的值是true还是false决定is_base_of<Base, Derived>::value返回的true还是false,从而影响enable_if的实例化。
至于最关键的__is_base_of()函数我没找到源码。。。网上也找不到资料。。。不过根据我的第六感,这应该是返回_Derived是不是_Base的基类的判断。若有对该函数知情者望告知多谢。。。

猜你喜欢

转载自blog.csdn.net/weixin_43827934/article/details/84581900
今日推荐