Concept Check in STL

来源:互联网 发布:二维码合成软件 编辑:程序博客网 时间:2024/04/27 20:49

Concept Check in STL

转载请注明出处:http://hi.baidu.com/walkandsing

Matt Austern在Generic Programming and the STL中提出了Concept、Model、Refinement的概念,实际上是换汤不换药,就是类似于Class、Object、SubClass的概念,只不过是在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.h、container_concepts.h、sequence_concepts.h和assoc_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):

用于在算法generate和generate_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;

};

….

然后基于这些atomic的static 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和_TypeY是convertiable的,__check_equal<_TypeX>和__check_equal<_TypeY>的赋值也是非法的。

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

原创粉丝点击