CGAL的STL扩展

1、介绍

        CGAL是以通用编程范式的精神设计的,与标准模板库(STL)协同工作。本章记录了非几何的STL样组件,这些组件在STL标准中没有提供,但在CGAL中提供:一个管理就地插入项(插入项不复制)的双连接列表,一个紧凑容器,一个使用三值比较的多集类,并提供附加功能、通用算法、迭代器、绑定和交换参数以及组合的函数适配器,以及围绕迭代器和循环器的函数适配器类。在“Handles and Circulators”中的循环器。还提供了一个存储多态对象的类,以及一个管理某些值的不确定性的类。最后,还提供了用于指定数据结构复杂度权衡的标签和策略类,以及一个有助于指定模板参数列表中默认类型的类。

2、

        类 In_place_list 在双连接列表中管理一个就地项目序列。 它的目标是灵活处理内存管理和性能优化。 项目类型必须提供两个必要的指针 &T::next_link 和 &T::prev_link。 获得这些指针的一种可能是从基类 In_place_list_base 继承它们。

        类 In_place_list 是一个与 STL 容器非常相似的容器,其优点是能够通过引用处理存储的元素,而不是复制它们。 仅知道元素的地址而没有迭代器的情况下,可以删除元素。 这用于简化相互指向的数据结构,如平面地图或多面体表面的半边数据结构(当前设计不再需要这一点)。 通常的迭代器也是可用的。

        CGAL::In_place_list_base 是 CGAL 库中的一个类,它提供了一个基于内部列表的存储结构,用于存储和操作几何对象。

        In_place_list_base 类是 CGAL 库中其他存储结构的基础,它提供了一些基本的操作,如插入、删除和遍历等。它的实现方式是通过在内部使用一个动态数组来存储对象,从而提供高效的随机访问和插入/删除操作。

        In_place_list_base 类的主要特点是它可以在原地进行修改,这意味着它可以在不重新分配内存的情况下进行插入和删除操作。这使得它对于需要频繁修改的数据结构非常有用,例如在几何算法中需要频繁更新或调整数据结构的情况。

3、紧凑型容器

        类Compact_container是一个类似STL的容器,它为其元素提供了非常紧凑的存储。它通过要求T提供对其中指针的访问来实现这一目标,该指针将由Compact_container用于其内部管理。traits类Compact_container_traits<T>指定访问该指针的方式。

        类Compact_container_base可以用作提供指针的基类,尽管在这种情况下,您无法获得最紧凑的表示。该指针在有效使用对象期间可以具有的值是指向4字节对齐对象的有效指针值(即,当构建对象时,指针的两个最低有效位需要为零)。这个容器的另一个有趣的特性是迭代器在插入或擦除操作期间不会失效。

        与STL容器概念的主要偏差是迭代器的++和--运算符并非在所有情况下都具有恒定的时间复杂度。实际的复杂性与容器在其生命周期内与当前大小相比的最大大小有关,因为迭代器还必须遍历“已擦除”的元素,所以坏的情况是容器过去包含很多元素,但现在已经少得多了。在这种情况下,我们建议复制容器,以便对内部表示进行“碎片整理”。

        存储在此容器中的对象可以选择存储“擦除计数器”。

        如果它存在,即如果对象是ObjectWithEraseCounter概念的模型,则每次从容器中擦除对象时,该对象的擦除计数器都将递增。例如,可以使用CC_safe_handle辅助类来利用此擦除计数器,这样就可以知道句柄是否仍指向同一元素。请注意,这是有意义的,因为容器在容器的destroy或clear()之前不会释放元素。例如,这个计数器被并行3D网格生成引擎用来延迟管理坏单元的队列:队列中的一个元素是一对,包含单元迭代器和插入时单元的擦除计数器值。当一个元素从队列中弹出时,算法会检查擦除计数器的当前值是否与存储的值匹配。如果不匹配,则意味着单元在此期间已被破坏,算法会忽略它。如果没有这种懒惰的管理,每次单元被破坏时,算法都必须在队列中查找并删除它。这种机制对于网格划分过程的并行版本更为有用,因为每个线程都有自己的队列,并且在所有队列中查找单元将非常缓慢。

        开发此容器是为了有效地处理大型数据结构,如三角剖分和半边缘数据结构。它可能也适用于其他类型的图。

        类Concurrent_compact_container提供了相同的功能,但启用了并发安全的插入和擦除操作。其他操作不是并发安全的。它要求程序链接到英特尔TBB库。

4、具有扩展功能的Multiset

        类Multiset表示Type类型元素的多集,表示为红黑树(有关红黑树的精彩介绍)。它与STL的多集类模板不同,主要是因为它由一个比较函子Compare参数化,该比较函子返回三值Comparison_result(即它返回SMALLER、EQUAL或LARGER),而不是返回bool的less函子。因此,通过较少调用比较函子,可以维护底层红黑树,这可以大大减少运行时间,特别是在比较Type类型的元素是一项昂贵操作时。

        Multiset 还保证发送给比较函子的元素的顺序是固定的。例如,如果我们在集合中插入新元素 x(或从集合中删除元素),则我们总是调用 Compare()(x, y)(而从不调用 Compare()(y, x)),其中 y 是已存储在集合中的元素。这种行为不受 std::multiset 支持,有时对于设计更有效的比较谓词至关重要。

        Multiset的接口通常是从std::multiset派生的。但是,它通过提供一些额外的操作来扩展接口,例如:在给定元素的精确位置(而不仅仅是使用插入提示)将元素插入集合;查找类型可能与Type不同的键,只要用户在键和集合元素之间提供一个比较函子CompareKey;以及连接和分割集合。

5、哈希

        对于顶点、半边、面等的句柄和索引,我们提供了 boost::hash 和 std::hash 的特殊化,以便它们可以与 boost::unordered_map 等类一起使用。

typedef boost::graph_traits::vertex_descriptor vertex_descriptor;

boost::unordered_map bum;

6、多态对象

        类Object可以存储任何其他类型的对象。它可以通过函数来返回不同类型的对象。还提供了一种基于其类型提取存储对象的机制。这个类类似于boost::any。

7、不确定性管理

        类 Uncertain 表示类型 T 的值范围。T 可以代表 bool 或 QGIS 的枚举类型 Sign、Comparison_result、Orientation、Oriented_side、Bounded_side 和 Angle。

        这个想法是,有时您不确定函数的结果,并且您想将其传达给调用者。 Uncertain允许这样做。 它还提供了一些函数,可以自然地扩展Uncertain的布尔运算。

        当使用的数字类型是像 Interint 这样的区间算法时,Uncertain 在博物馆中用作几何谓词的返回类型。最终用户通常看不到它,因为它隐藏在各种过滤内核提供的过滤谓词的实现中,但重要的是,打算由 Filtered_predicate 过滤的谓词的提供者要知道它。

        它也可以在其他环境中使用,因为它是一种通用工具。

8、复杂性标签和策略

        一些数据结构和算法可以在内存使用和时间复杂性之间进行不同的复杂性权衡来实现。CGAL提供标签Fast和Compact,可用于在这些变体之间进行选择。例如,Location_policy类由这些标记参数化,并允许指定点位置的复杂性(目前仅在Delaunay_triangulation_3中)。还提供了方便的typedefs Fast_location和Compact_location。

9、模板参数列表中的默认参数

        在C++中,可以在模板参数列表的末尾指定默认值。指定要使用默认值只需省略即可。但是,这只能在列表的末尾进行。Default提供了一种简单的机制,可以在序列中的任何位置执行等效的操作。

        CGAL::Default::Get 是CGAL库中的一个默认构造函数,用于创建默认的对象。在CGAL中,许多类都有一个默认构造函数,用于创建没有特定参数的对象。

        默认构造函数通常用于在需要对象但不需要特定参数的情况下创建对象。例如,如果你有一个需要几何对象的函数,但该函数不接受任何参数,那么你可以使用默认构造函数来创建一个对象,并将其传递给该函数。

10、检查

        大多数cgal代码都包含检查。例如,内核代码中使用的所有检查都以cgal_ kernels为前缀。其他包有自己的前缀,如相应章节所述。有些是为了检查内核是否正常运行,有些是为了检查用户是否以可接受的方式调用内核例程。

        有五种检查类型。前三种是错误,如果失败会导致程序停止。第四种只会导致警告,最后一种只在编译时使用。

        先决条件:检查例程的调用者是否以正确的方式调用它。如果这种检查失败,则调用者(通常是库的用户)有责任。

        后置条件:检查一个例程是否按照它的承诺去做。如果这样的检查失败,那么这个例程的错误,也就是这个库的错误。

        断言:不属于上述两类检查的其他检查.

        警告信息:失败后果不那么严重的检查。

        静态断言:是编译时断言,用于验证编译时常量的值或比较类型是否相等。

10.1、改变失败行为

        如上所述,如果违反了后置条件、前置条件或断言,则抛出异常,如果不采取任何措施来捕获它,程序将中止。此行为可以通过函数set_error_behaviour()和枚举Failure_behaviour来更改。

        默认值为 THROW_EXCEPTION,它会抛出一个异常。

        如果设置了EXIT值,程序将停止并返回一个表示失败的值,但不会转储核心。CONTINUE值告诉在诊断错误后继续进行检查。请注意,自C++11 3.4以来,CONTINUE对错误具有与THROWN_EXCEPTION相同的效果(但它保留了对警告的意义),因此无法再让断言失败继续(除非完全禁用它们)。

        高级:如果设置了EXIT_WITH_SUCCESS值,程序将停止并返回与成功执行相对应的值,而不会转储内核。

        set_error_ehaviour()返回的值是以前使用的值。

        对于警告,我们提供了set_warning_behaviour(),其工作方式相同。唯一的区别是,对于警告,默认值为CONTINUE。

        设置错误和警告行为是不安全的。

10.2、以更细的粒度进行控制

        到目前为止,所描述的编译时间标志都是对整个库操作的。有时你可能想要更精细的控制。CGAL提供了一种更精细的粒度来打开和关闭检查,即定义例程的模块。模块的名称直接附加在CGAL前缀之后。因此,标记CGAL_kernel_no_assertions仅关闭内核中的断言,标记CGAL_ch_check_expensive打开凸包模块中的昂贵检查。特定模块的名称与该模块一起记录。

10.3、自定义错误报告方式

        通常,错误消息会写入标准错误输出。可以用它们做一些不同的事情。为此,您可以使用set_error_handler(Failure_function handler)注册自己的处理程序。此函数应声明如下。
您可以使用自己的处理程序来做以下几件事。

        您可以以不同的方式显示诊断消息,例如在弹出窗口或日志文件(或组合)中。您还可以实现一个不同的策略来决定出错后要做什么。例如,您可以抛出异常,或者在对话框中询问用户是中止还是继续。

        如果您这样做,最好将错误行为设置为CONTINUE,这样它就不会干扰您的策略。
您可以注册两个处理程序,一个用于警告,另一个用于错误。当然,如果您愿意,也可以对两者使用相同的函数。设置处理程序时,会返回上一个处理程序,因此如果需要,可以恢复它。

你可能感兴趣的:(CGAL,c++,算法)