vinowan的专栏

vinowan@foxmail.com

Concept Checks in STL

Matt AusternGeneric Programming and the STL中提出了ConceptModelRefinement的概念,实际上是换汤不换药,就是类似于ClassObjectSubClass的概念,只不过是在Template的语义下面解释的而已,唯一的差别就是在Template下,类型是implicit的,对类型的要求比较松散。而所谓的Concept Checks就是类似于assert的东西,专业点讲就是static assertion,就是利用编译器在Compiling的时候的检查来做一些检查,例如:

#define STATIC_ASSERTION(_cond,_name)  typedef char  STATIC_ASSERT_FAILED_ ## _name[(_cond)?1:-1]
这里_cond必须是编译期间能判定的结果,若_cond为真,上式相当于定义了一个一维char数组类型,反之,数组的维度为-1,不能通过编译,这时数组类型名包含了错误信息。
一个典型的应用就是判断类型的字节数。可定义如下宏:
#define STATIC_SIZE_ASSERT(_type,_size) STATIC_ASSERT ( sizeof(_type) == _size, _type##_MUST_BE_##_size##_BYTES)

STL中和概念检查有关的文件有:concept_checks.hcontainer_concepts.hsequence_concepts.hassoc_container_concepts.h

先从concept_checks.h开始,这个文件定义了一些基础的概念和iterator的概念,主要的接口是:

#define __STL_CLASS_REQUIRES(__type_var, __concept)

检查__type_var是否满足__concept,当前定义的概念有:_Allocator_Assignable_DefaultConstructible_EqualityComparable_LessThanComparable_TrivialIterator_InputIterator_OutputIterator_ForwardIterator_BidirectionalIterator_RandomAccessIterator_Mutable_TrivialIterator_Mutable_ForwardIterator_Mutable_BidirectionalIterator_Mutable_RandomAccessIterator

#define __STL_CONVERTIBLE(__type_x, __type_y)

检查类型_x能否转换为类型_y

#define __STL_CLASS_REQUIRES_SAME_TYPE(__type_x, __type_y)

检查类型_x能否和类型_y为同一个类型。

#define __STL_CLASS_GENERATOR_CHECK(__func, __ret)

用于在算法generategenerate_n中用来检查generator合法性。

#define __STL_CLASS_UNARY_FUNCTION_CHECK(__func, __ret, __arg)

检查__func的参数为__arg类型,返回值为__ret类型。

#define __STL_CLASS_BINARY_FUNCTION_CHECK(__func, __ret, __first, __second)

检查__func的参数为__first__second类型,返回值为__ret类型。

#define __STL_CLASS_REQUIRES_BINARY_OP(__opname, __ret, __first, __second)

检查__ret __opname(__first, __second)是否合法。

 

在看这些接口的具体实现前,还需要了解一点他们的实现细节,在concept_checks.h中定义了一个类_STL_ERROR,里面定义了一些static模板函数,这些方法组成了static assertion的最小单元:

template <class _Type>

  static _Type

  __default_constructor_requirement_violation(_Type) {

    return _Type();

  }

  template <class _Type>

  static _Type

  __assignment_operator_requirement_violation(_Type __a) {

    __a = __a;

    return __a;

  }

….(省略)

这些方法都是利用一些简单的表达式使得编译器来判断__Type是否支持某一种操作,当其不支持的时候,compiler就会报错,函数名将被打印出来,这样用户就能知道出错的原因了。还有一些模板方法是用来检测类型里是否定义了某些typedef,如:

template <class _Iter>

struct __value_type_type_definition_requirement_violation {

  typedef typename __STD::iterator_traits<_Iter>::value_type value_type;

};

 

template <class _Iter>

struct __difference_type_type_definition_requirement_violation {

  typedef typename __STD::iterator_traits<_Iter>::difference_type

          difference_type;

};

….

然后基于这些atomicstatic assertion组合出了一些概念:

template <class _Type>

struct _Assignable_concept_specification {

  static void _Assignable_requirement_violation(_Type __a) {

    _STL_ERROR::__assignment_operator_requirement_violation(__a);

    _STL_ERROR::__copy_constructor_requirement_violation(__a);

    _STL_ERROR::__const_parameter_required_for_copy_constructor(__a,__a);

    _STL_ERROR::__const_parameter_required_for_assignment_operator(__a,__a);

  }

};

template <class _TrivialIterator>

struct _TrivialIterator_concept_specification {

static void

_TrivialIterator_requirement_violation(_TrivialIterator __i) {

  typedef typename

    __value_type_type_definition_requirement_violation<_TrivialIterator>::

    value_type __T;

  // Refinement of Assignable

  _Assignable_concept_specification<_TrivialIterator>::

    _Assignable_requirement_violation(__i);

  // Refinement of DefaultConstructible

  _DefaultConstructible_concept_specification<_TrivialIterator>::

    _DefaultConstructible_requirement_violation(__i);

  // Refinement of EqualityComparable

  _EqualityComparable_concept_specification<_TrivialIterator>::

    _EqualityComparable_requirement_violation(__i);

  // Valid Expressions

  _STL_ERROR::__dereference_operator_requirement_violation(__i);

}

};

template <class _InputIterator>

struct _InputIterator_concept_specification {

static void

_InputIterator_requirement_violation(_InputIterator __i) {

  // Refinement of TrivialIterator

  _TrivialIterator_concept_specification<_InputIterator>::

    _TrivialIterator_requirement_violation(__i);

  // Associated Types

  __difference_type_type_definition_requirement_violation<_InputIterator>();

  __reference_type_definition_requirement_violation<_InputIterator>();

  __pointer_type_definition_requirement_violation<_InputIterator>();

  __iterator_category_type_definition_requirement_violation<_InputIterator>();

  // Valid Expressions

  _STL_ERROR::__preincrement_operator_requirement_violation(__i);

  _STL_ERROR::__postincrement_operator_requirement_violation(__i);

}

};

….

到目前为止,这些函数都是些函数调用,怎么达到在编译的时候判断的目的呢?谜底就在接口定义中,以__STL_REQUIRES为例,它的定义为:

#define __STL_REQUIRES(__type_var, __concept) /

do { /

  void (*__x)( __type_var ) = __concept##_concept_specification< __type_var >/

::__concept##_requirement_violation; __x = __x; } while (0)

 

比如在很多算法的开头都有检查:

__STL_REQUIRES(_InputIter, _InputIterator);

 

这时相当于在block里定义了一个函数指针__x,而__x=__x将触发模板的实例化,这时编译器开始对_InputIterator_concept_specification::_InputIterator_requirement_violation中的所有的static assertion进行检查,但又不会调用这些函数,这样只会在编译期间会产生开销。

其他的接口定义也都类似,值得一提的还有__STL_CONVERTIBLE__STL_REQUIRES_SAME_TYPE,实际上在Modern C++ Design中有提到这两个模板,但我发现在STL中这两个模板的实现更加简洁。

template <class _TypeX, class _TypeY>

struct _STL_CONVERT_ERROR {

  static void

  __type_X_is_not_convertible_to_type_Y(_TypeX __x, _TypeY) {

    _TypeY __y = __x;

    __sink_unused_warning(__y);

  }

};

#define __STL_CONVERTIBLE(__type_x, __type_y) /

do { /

  void (*__x)( __type_x , __type_y ) = _STL_CONVERT_ERROR< __type_x , /

  __type_y >::__type_X_is_not_convertible_to_type_Y; /

  __x = __x; } while (0)

很简单,就是通过赋值来判断的。

template <class _Type> struct __check_equal { };

 

template <class _TypeX, class _TypeY>

struct _STL_SAME_TYPE_ERROR {

  static void

  __type_X_not_same_as_type_Y(_TypeX , _TypeY ) {

    __check_equal<_TypeX> t1 = __check_equal<_TypeY>();

  }

};

#define __STL_REQUIRES_SAME_TYPE(__type_x, __type_y) /

do { /

  void (*__x)( __type_x , __type_y ) = _STL_SAME_TYPE_ERROR< __type_x, /

    __type_y  >::__type_X_not_same_as_type_Y; /

  __x = __x; } while (0)

这个稍微麻烦一点,虽然也是通过赋值来判断但引入了一个空模板__check_equal,当_TypeX_TypeY类型不同的时候,__check_equal<_TypeX>__check_equal<_TypeY>肯定不同,即使_TypeX_TypeYconvertiable的,__check_equal<_TypeX>__check_equal<_TypeY>的赋值也是非法的。

container_concepts.hsequence_concepts.h中的方法和上述类似,在此不再累述。

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭