在繼續往下閱讀之前,請先了解何謂泛型機制中的concept及model相關議題,可以參閱以下文章:侯捷觀點(系列書評 2/2)【Genericity/STL 大系】
重點節錄:
『STL 的六大組件 containers, algorithms, iterators, function objects, allocators, adaptors, 全都是 concepts,實作品如 vector, list, sort(), swap() 等等 templates, … 全都是 models。』
『所謂 concept 和 model
所謂 concept,描述某個抽象型別的條件(或說需求,requirements)。concept 並不是一個 class,也不是一個變數或是一個 template 參數;C++ 語言之中沒有任何東西可以直接代表一個concept。然而,在每一個用到泛型程式設計方法的 C++ 程式中,concept 非常重要。由 concepts 所構成的階層體系,正是 STL 的主體結構。
當某個型別滿足某個 concept 的所有條件,我們便說此型別是該 conecpt 的一個model。concept 可被視為一組型別條件。如果型別 T 是 concept C 的一個 model,那麼 T 就一定滿足 C 的所有條件。因此,concept 亦可被視為是一組型別。如果型別 T 是 concept C 的一個 model,我們便可說 T 隸屬於「C 所表現的一組型別」。』
使用版本:boost 1.50.0
通常程式的bug大致上可以區分為三種:
第一種是編譯時期的錯誤
第二種是run-time時期的錯誤:通常是因為物件被刪除、pointer指到null或memory leak而導致程式執行期間的crash。
第三種則是程式邏輯計算上的錯誤:因為整個程式運作上不會有任何問題,因此非常難以定出錯誤的問題所在。
boost中的concept check機制的目的,便是想要處理第一種bug--目的是要讓C++中因泛型編輯所產生的編譯錯誤能夠更清楚的顯示出他的問題到底出在哪,而不是一堆對使用者而言毫無意義的錯誤訊息。
例如,如果我們編譯下面的內容:
#include <vector> #include <algorithm> #include <complex> #include "boost/concept/assert.hpp" #include "boost/concept/requires.hpp" #include "boost/concept_check.hpp" int main(int, char*[]) { std::vector<std::complex<float> > v; std::stable_sort(v.begin(), v.end()); system("pause"); return 0; }
在VS2008下產生的編譯錯誤訊息如下:
1>------ Build started: Project: BoostLibraryPractice, Configuration: Debug Win32 ------ 1>Compiling... 1>main.cpp 1>c:\program files\microsoft visual studio 9.0\vc\include\xutility(294) : error C2784: 'bool std::operator <(const std::basic_string<_Elem,_Traits,_Alloc> &,const _Elem *)' : could not deduce template argument for 'const std::basic_string<_Elem,_Traits,_Alloc> &' from 'std::complex<float>' 1> c:\program files\microsoft visual studio 9.0\vc\include\string(150) : see declaration of 'std::operator <' 1> c:\program files\microsoft visual studio 9.0\vc\include\algorithm(2760) : see reference to function template instantiation 'bool std::_Debug_lt<std::complex<float>,std::complex<float>>(_Ty1 &,_Ty2 &,const wchar_t *,unsigned int)' being compiled 1> with 1> [ 1> _Ty1=std::complex<float>, 1> _Ty2=std::complex<float> 1> ] 1> c:\program files\microsoft visual studio 9.0\vc\include\algorithm(3400) : see reference to function template instantiation 'void std::_Buffered_merge<_BidIt,_Diff,_Ty>(_BidIt,_BidIt,_BidIt,_Diff,_Diff,std::_Temp_iterator<_Ty> &)' being compiled 1> with 1> [ 1> _BidIt=std::_Vector_iterator<std::complex<float>,std::allocator<std::complex<float>>>, 1> _Diff=__w64 int, 1> _Ty=std::complex<float> 1> ] 1> c:\program files\microsoft visual studio 9.0\vc\include\algorithm(3412) : see reference to function template instantiation 'void std::_Stable_sort<_BidIt,_Diff,_Ty>(_BidIt,_BidIt,_Diff,std::_Temp_iterator<_Ty> &)' being compiled 1> with 1> [ 1> _BidIt=std::_Vector_iterator<std::complex<float>,std::allocator<std::complex<float>>>, 1> _Diff=__w64 int, 1> _Ty=std::complex<float> 1> ] 1> c:\program files\microsoft visual studio 9.0\vc\include\algorithm(3421) : see reference to function template instantiation 'void std::_Stable_sort<std::_Vector_iterator<_Ty,_Alloc>,__w64 int,std::complex<float>>(_BidIt,_BidIt,_Diff *,_Ty *)' being compiled 1> with 1> [ 1> _Ty=std::complex<float>, 1> _Alloc=std::allocator<std::complex<float>>, 1> _BidIt=std::_Vector_iterator<std::complex<float>,std::allocator<std::complex<float>>>, 1> _Diff=__w64 int 1> ] 1> c:\workspace\dropbox\workspace\boostlibrarypractice\boostlibrarypractice\main.cpp(13) : see reference to function template instantiation 'void std::stable_sort<std::_Vector_iterator<_Ty,_Alloc>>(_BidIt,_BidIt)' being compiled 1> with 1> [ 1> _Ty=std::complex<float>, 1> _Alloc=std::allocator<std::complex<float>>, 1> _BidIt=std::_Vector_iterator<std::complex<float>,std::allocator<std::complex<float>>> 1> ] 1>Build log was saved at "file://c:\workspace\Dropbox\workspace\BoostLibraryPractice\BoostLibraryPractice\Debug\BuildLog.htm" 1>BoostLibraryPractice - 1 error(s), 0 warning(s)
對我們程式設計師而言,其實這堆訊息顯示的錯誤都不是我們所關心的。我們想知道的是,到底是什麼原因造成這段程式編譯錯誤。到底,傳入的型別在操作上的概念上出了什麼問題?
先回頭來看看concept跟model的定義:型別T如果符合concept C所條件,那麼型別T就是concept C的一個model。
型別T也可以同時符合多個不同concept,當然,也會是多個不同concept的model。在上面的例子中,std::stable_sort這個方法會有許多預設在操作上的概念,因為它STL中的一個方法,因此一定會符合其規範的一些基本 concepts:Assignable、Default Constructible、Equality Comparable、LessThan Comparable。
從錯誤訊息中,對比較有經驗的程式設計師而言,其實是可以推斷出錯誤是由於型別std::complex<float>沒辦法在std::stable_sort中進行大小的比較所造成的。以比較專業的術語來說的話,便是其不符合LessThanComparable concept條件而產生的錯誤。因此std::complex<float>不是LessThanComparable concept的model。
因此,這個錯誤是由於傳入的型別不對所造成的,因此顯示STL內部的錯誤訊息沒辦法很直覺的告知問題是出在user code上而不是library code中。
在對std::stable_sort導入concept check的機制之後,其型式變成這樣:
#include <vector> #include <algorithm> #include <complex> #include "boost/range/concepts.hpp" using namespace boost; template <class RandomAccessIter> void my_stable_sort(RandomAccessIter first, RandomAccessIter last){ //所有basic concept在boost/range/concepts.hpp都有實作了 function_requires<RandomAccessIteratorConcept<RandomAccessIter> >(); typedef typename std::iterator_traits<RandomAccessIter>::value_type value_type; function_requires< LessThanComparableConcept<value_type> >(); //do stable sort algorithm std::stable_sort(first, last); } int main(int, char*[]) { std::vector<std::complex<float>> v; // std::vector<float> v; my_stable_sort(v.begin(), v.end()); system("pause"); return 0; }
產生的錯誤碼:
1>------ Build started: Project: BoostLibraryPractice, Configuration: Debug Win32 ------ 1>Compiling... 1>main.cpp 1>c:\workspace\library\boost_1_50_0\boost\concept_check.hpp(247) : error C2784: 'bool std::operator <(const std::basic_string<_Elem,_Traits,_Alloc> &,const _Elem *)' : could not deduce template argument for 'const std::basic_string<_Elem,_Traits,_Alloc> &' from 'value_type' 1> c:\program files\microsoft visual studio 9.0\vc\include\string(150) : see declaration of 'std::operator <' 1> c:\workspace\library\boost_1_50_0\boost\concept_check.hpp(246) : while compiling class template member function 'boost::LessThanComparable<TT>::~LessThanComparable(void)' 1> with 1> [ 1> TT=value_type 1> ] 1> c:\workspace\library\boost_1_50_0\boost\concept_check.hpp(244) : see reference to class template instantiation 'boost::LessThanComparable<TT>' being compiled 1> with 1> [ 1> TT=value_type 1> ] 1> c:\workspace\library\boost_1_50_0\boost\concept\detail\has_constraints.hpp(42) : see reference to class template instantiation 'boost::LessThanComparableConcept<TT>' being compiled 1> with 1> [ 1> TT=value_type 1> ] 1> c:\workspace\library\boost_1_50_0\boost\concept\detail\msvc.hpp(53) : see reference to class template instantiation 'boost::concepts::not_satisfied<Model>' being compiled 1> with 1> [ 1> Model=boost::LessThanComparableConcept<value_type> 1> ] 1> c:\workspace\library\boost_1_50_0\boost\concept_check.hpp(45) : see reference to class template instantiation 'boost::concepts::require<Model>' being compiled 1> with 1> [ 1> Model=boost::LessThanComparableConcept<value_type> 1> ] 1> c:\workspace\dropbox\workspace\boostlibrarypractice\boostlibrarypractice\main.cpp(14) : see reference to function template instantiation 'void boost::function_requires<boost::LessThanComparableConcept<TT>>(Model *)' being compiled 1> with 1> [ 1> TT=value_type, 1> Model=boost::LessThanComparableConcept<value_type> 1> ] 1>Build log was saved at "file://c:\workspace\Dropbox\workspace\BoostLibraryPractice\BoostLibraryPractice\Debug\BuildLog.htm" 1>BoostLibraryPractice - 1 error(s), 0 warning(s)
我們可以發現,錯誤變的更加顯而易見了。
參考:
Leave a Reply