詳細內容能夠參考[1]。這裏單純列舉出相關的代碼示例:html
// 使用繼承的方式實現不一樣圖形的繪製 class Shape { public: Shape() {} virtual ~Shape() {} virtual void Draw() = 0; }; class Triangle : public Shape { public: Triangle() {} ~Triangle() {} void Draw() { cout << "Draw a Triangle" << endl; } }; class Rectangle : public Shape { public: Rectangle() {} ~Rectangle() {} void Draw() { cout << "Draw a Rectangle" << endl; } }; // 利用Curiously Recurring Template Pattern template <typename Derived> class Shape { public: void Draw() { return static_cast<Derived*>(this)->Draw(); } }; class Triangle : public Shape<Triangle> { public: void Draw() { cout << "Draw a Triangle" << endl; } }; class Rectangle : public Shape<Rectangle> { public: void Draw() { cout << "Draw a Rectangle" << endl; } };
經過Kernel須要解決的主要問題是代碼的適配性和可擴展性。那爲何能夠提升適配性和可擴展性能夠在後續的內容中獲得答案。算法
常見的數據結構和算法的設計,數據結構爲獨立的類,算法爲全局或類的成員函數。示例以下:數據結構
K::Point_2 p(0,1), q(1,-4); // 數據結構 K::Line_2 line(p,q); if (less_xy_2(p, q)) { ... } // 算法成員函數
幾何Kernel包含須要操做的類型,以及針對這些類型的操做。Kernel會將上述相關的內容進行打包處理。示例以下:架構
K k; K::Construct_line_2 c_line = k.construct_line_2_object(); K::Less_xy_2 less_xy = k.less_xy_2_object(); K::Point_2 p(0,1), q(1,-4); K::Line_2 line = c_line(p, q); if (less_xy(p, q)) { ... }
Kernel將數據結構和算法相關的細節放到了內部。總體的架構能夠分爲三層,Kernel, Geometric Primitives,Numeric Primitives,具體以下:less
template <class K> struct MyPoint { }; template <class K> struct MyLine { }; template <class K> struct MyConstruct { }; template <class K> struct MyLess { }; struct Kernel { typedef MyPoint<Kernel> Point_2; typedef MyLine<Kernel> Line_2; typedef MyConstruct<Kernel> Construct_line_2; typedef MyLess<Kernel> Less_xy_2; }; // Generate new Kernel template <class K> struct NewPoint { }; template <class K> struct MyLeftTurn { }; struct New_kernel : public Kernel { typedef NewPoint<New_kernel> Point_2; typedef MyLeftTurn<New_kernel> Left_turn_2; }; int main() { New_kernel::Point_2 p, q; New_kernel::Construct_line_2 construct_line_2; New_kernel::Line_2 l = construct_line_2(p, q); return 0; }
測試環境能夠見: https://ideone.com/MrXCDD數據結構和算法
編譯錯誤爲:ide
prog.cpp: In function ‘int main()’: prog.cpp:28:49: error: no match for call to ‘(Kernel::Construct_line_2 {aka MyConstruct<Kernel>}) (New_kernel::Point_2&, New_kernel::Point_2&)’ New_kernel::Line_2 l = construct_line_2(p, q);
從編譯錯誤中可見,New_kernel::Construct_line_2
其實調用的是MyConstruct<Kernel>
的實現,而咱們想要的調用是MyConstruct<New_kernel>
。依賴關係見下圖:函數
這個版本中另外一個隱含的問題是,循環引用的問題,具體以下:post
template <class K> struct P { typedef K::A B; }; struct Kernel { typedef P<Kernel>::B B; typedef int A; };
爲了解決上面的問題,進行了第二版本的改進。測試
爲了下降不一樣Kernel之間的關聯性,引入Kernel_base,具體以下:
template <class K> struct MyPoint { }; template <class K> struct MyLine { }; template <class K> struct MyConstruct { }; template <class K> struct MyLess { }; template <class K> struct Kernel_base { typedef MyPoint<K> Point_2; typedef MyLine<K> Line_2; typedef MyConstruct<K> Construct_line_2; typedef MyLess<K> Less_xy_2; }; struct Kernel : public Kernel_base<Kernel> { }; // Generate new Kernel template <class K> struct NewPoint { }; template <class K> struct MyLeftTurn { }; template<class K> struct New_kernel_base : public Kernel_base<K> { typedef NewPoint<K> Point_2; typedef MyLeftTurn<K> Left_turn_2; }; struct New_kernel : public New_kernel_base<New_kernel> {}; int main() { New_kernel::Point_2 p, q; New_kernel::Construct_line_2 construct_line_2; New_kernel::Line_2 l = construct_line_2(p, q); return 0; }
測試環境能夠見:https://ideone.com/40wOCa
編譯錯誤以下:
prog.cpp: In function ‘int main()’: prog.cpp:35:49: error: no match for call to ‘(Kernel_base<New_kernel>::Construct_line_2 {aka MyConstruct<New_kernel>}) (New_kernel_base<New_kernel>::Point_2&, New_kernel_base<New_kernel>::Point_2&)’ New_kernel::Line_2 l = construct_line_2(p, q); ^
從編譯結果中可得,Construct_line_2對應的New_kernel正是咱們所預期的。接下來須要解決的問題是,construct_line_2並非能夠調用的函數。調整後kernel之間的依賴關係以下:
該版本中,利用函數對象來處理操做邏輯。
template <class K> struct MyPoint { }; template <class K> struct MyLine { }; template <class K> struct MyConstruct { typedef typename K::Line_2 Line_2; typedef typename K::Point_2 Point_2; Line_2 operator() (Point_2, Point_2) const { return Line_2(); } }; template <class K> struct MyLess { typedef typename K::Point_2 Point_2; bool operator() (Point_2, Point_2) const { return true; } }; template <class K> struct Kernel_base { typedef MyPoint<K> Point_2; typedef MyLine<K> Line_2; typedef MyConstruct<K> Construct_line_2; typedef MyLess<K> Less_xy_2; Construct_line_2 construct_line_2_object(); Less_xy_2 less_xy_2_object(); }; struct Kernel : public Kernel_base<Kernel> { }; // Generate new Kernel template <class K> struct NewPoint { }; template <class K> struct MyLeftTurn { }; template<class K> struct New_kernel_base : public Kernel_base<K> { typedef NewPoint<K> Point_2; typedef MyLeftTurn<K> Left_turn_2; }; struct New_kernel : public New_kernel_base<New_kernel> {}; int main() { New_kernel::Point_2 p, q; New_kernel::Construct_line_2 construct_line_2; New_kernel::Line_2 l = construct_line_2(p, q); return 0; }
示例程序見:https://ideone.com/6ISelp
整個編譯過程成功經過。
到此處,整個kernel的結構基本完善了。
以2D點集凸包計算的實現來舉例:https://doc.cgal.org/latest/Convex_hull_2/index.html。僅僅針對算法實現過程當中Kernel的使用進行簡單說明,對算法的具體實現此處不進行介紹。
// 暴露給外部調用的接口 template <class InputIterator, class OutputIterator> inline OutputIterator ch_graham_andrew( InputIterator first, InputIterator last, OutputIterator result) { typedef std::iterator_traits<InputIterator> ITraits; typedef typename ITraits::value_type value_type; typedef CGAL::Kernel_traits<value_type> KTraits; // 根據value_type獲取KernelTraits typedef typename KTraits::Kernel Kernel; // 進一步獲取Kernel return ch_graham_andrew(first, last, result, Kernel()); // 傳入Kernel,調用具體實現 } // 具體實現 template <class InputIterator, class OutputIterator, class Traits> OutputIterator ch_graham_andrew( InputIterator first, InputIterator last, OutputIterator result, const Traits& ch_traits) { typedef typename Traits::Point_2 Point_2; // 獲取Kernel中的類型 typedef typename Traits::Equal_2 Equal_2; // 獲取Kernel中的類型 Equal_2 equal_points = ch_traits.equal_2_object(); // 獲取kernel中的算法 if (first == last) return result; std::vector< Point_2 > V (first, last); std::sort( V.begin(), V.end(), ch_traits.less_xy_2_object() ); // 獲取Kernel中的算法 if (equal_points( *(V.begin()), *(V.rbegin())) ) { *result++ = *(V.begin()); return result; } #if defined(CGAL_CH_NO_POSTCONDITIONS) || defined(CGAL_NO_POSTCONDITIONS) \ || defined(NDEBUG) OutputIterator res(result); #else Tee_for_output_iterator<OutputIterator,Point_2> res(result); #endif // no postconditions ... ch__ref_graham_andrew_scan( V.begin(), V.end(), res, ch_traits); ch__ref_graham_andrew_scan( V.rbegin(), V.rend(), res, ch_traits); CGAL_ch_postcondition( \ is_ccw_strongly_convex_2( res.output_so_far_begin(), \ res.output_so_far_end(), \ ch_traits)); CGAL_ch_expensive_postcondition( \ ch_brute_force_check_2( \ V.begin(), V.end(), \ res.output_so_far_begin(), res.output_so_far_end(), \ ch_traits)); #if defined(CGAL_CH_NO_POSTCONDITIONS) || defined(CGAL_NO_POSTCONDITIONS) \ || defined(NDEBUG) return res; #else return res.to_output_iterator(); #endif // no postconditions ... }
從上面簡單的示例可得,通常在算法構建的時候會在最外層生成調用接口,而後,在具體實現中,經過分別對Kernel中的數據結構和算法的調用,最後組裝成一個完整的算法實現。
此處將文章最後的示例代碼貼出來,用於進一步完善對Kernel的認知。
//------------------------------------------------------------ // bottom layer: number type based function toolbox // template <class FT> FT determinant2x2(FT a00, FT a01, FT a10, FT a11) { return a00*a11 - a10*a01; } template <class FT> void line_from_pointsC2(FT px, FT py, FT qx, FT qy, FT &a, FT &b, FT &c) {} //------------------------------------------------------------ // mid layer: representations, predicates and constructions // template <class K_> struct Point_2 { typedef K_ K; typedef typename K::FT FT; Point_2() {} Point_2(FT x_, FT y_) : x(x_), y(y_) {} FT x, y; }; template <class K_> struct Line_2 { typedef K_ K; typedef typename K::Point_2 Point_2; Line_2() {} Line_2(Point_2 p, Point_2 q) { *this = K::Construct_line_2(p,q); } typename K::FT a, b, c; }; template <class K_> struct Segment_2 { typedef K_ K; typename K::Point_2 s, e; }; template <class K_> struct Less_xy_2 { typedef typename K_::Point_2 Point_2; bool operator()(Point_2 p, Point_2 q) const { return p.x < q.x || p.x == q.x && p.y < q.y; } }; template <class K_> struct Left_turn_2 { typedef typename K_::Point_2 Point_2; bool operator()(Point_2 p, Point_2 q, Point_2 r) const { return determinant2x2(q.x - p.x, q.y - p.y, r.x - p.x, r.y - q.y) > 0; } }; template <class K_> struct Construct_line_2 { typedef typename K_::Point_2 Point_2; typedef typename K_::Line_2 Line_2; Line_2 operator()(Point_2 p, Point_2 q) const { Line_2 l; Line_from_pointsC2(p.x, p.y, q.x, q.y, l.a, l.b, l.c); return l; } }; //------------------------------------------------------------ // top layer: geometric kernel // template <class K_, class FT_> struct Kernel_bae { typedef K_ K; typedef FT_ FT; typedef Point_2<K> Point_2; typedef Line_2<K> Line_2; typedef Segment_2<K> Segment_2; typedef Less_xy_2<K> Less_xy_2; typedef Left_turn_2<K> Left_turn_2; typedef Construct_line_2<K> Construct_line_2; Less_xy_2 less_xy_2_object() const { return Less_xy_2(); } Left_turn_2 Left_turn_2_object() const { return Left_turn_2(); } Construct_line_2 construct_line_2_object() const { return Construct_line_2(); } }; template <class FT_> struct Kernel : public Kernel_base<Kernel<FT_>, FT_> {}; //------------------------------------------------------------ // convenience layer: global functions // template < class K >inline bool less_xy_2(typename K::Point_2 p,typename K::Point_2 q, K k = K()) { returnk.less_xy_2_object()(p, q); } template < class K >inline bool left_turn_2(typenameK::Point_2 p, typenameK::Point_2 q, typenameK::Point_2 r, K k = K()) { returnk.left_turn_2_object()(p, q, r); } //------------------------------------------------------------ // enve more convenience: specializations for kernel // template < class FT > inline bool left_turn_2(Point_2< Kernel< FT > > p, Point_2< Kernel< FT > > q, Point_2< Kernel< FT > > r) { returnleft_turn_2(p, q, r, Kernel< FT >()); } template < class FT >inline bool less_xy_2(Point_2< Kernel< FT > > p, Point_2< Kernel< FT > > q) { returnless_xy_2(p, q, Kernel< FT >()); }