我正在研究仅标头库的代码库。它包含这个Polygon
类,它的问题是它相当大:大约8000行。我试图打破这一点,但一直遇到麻烦。这个类和库的几个约束:
- 我不能随意将库更改为需要预编译的部分。这不适合我们当前的构建街道,人们强烈认为它只是标题。
- 该类对性能非常关键,它的分配和算法占我正在处理的应用程序总运行时间的 99% 以上。
- 有时这个类经常被构造(很多很多三角形),它会经常调用它的方法。因此,如果可能的话,我希望它没有虚拟表,并且除非编译器(GCC -O2)保证将其优化掉,否则不要为组合而追逐指针。
此类包含公共函数中对多边形的多项操作,例如area()
和contains(Point2)
。其中每一个都有针对各种用例的几种实现,主要是小多边形与大多边形,其中小多边形采用简单的单线程方法,但大多边形运行多线程或使用具有更好时间复杂度的算法。基本上是这样的(简化):
class Polygon {
public:
area_t area() {
if(size() < 150) return area_single_thread();
return area_openmp();
}
bool contains(Point2 point) {
if(size() < 75) return contains_single_thread(point);
if(size() < 6000) return contains_openmp(point);
return contains_opencl(point);
}
...
private:
area_t area_single_thread() { ... }
area_t area_openmp() { ... }
bool contains_single_thread(Point2 point) { ... }
bool contains_openmp(Point2 point) { ... }
bool contains_opencl(Point2 point) { ... }
...
}
我的尝试是将这些操作中的每一个都放入一个单独的文件中。这似乎是关注点的逻辑分离,并使代码更具可读性。
到目前为止,我最好的尝试是这样的:
//polygon.hpp
class Polygon {
public:
area_t area() {
if(size() < 150) return area_single_thread();
return area_openmp();
}
bool contains(Point2 point) {
if(size() < 75) return contains_single_thread(point);
if(size() < 6000) return contains_openmp(point);
return contains_opencl(point);
}
...
private:
//Private implementations separated out to different files for readability.
#include "detail/polygon_area.hpp"
#include "detail/polygon_contains.hpp"
...
}
//polygon_area.hpp
area_t area_single_thread() { ... }
area_t area_openmp() { ... }
//polygon_contains.hpp
bool contains_single_thread(Point2 point) { ... }
bool contains_openmp(Point2 point) { ... }
bool contains_opencl(Point2 point) { ... }
然而,这有一个主要缺点,即这些子文件不是完整的头文件。它们包含类的一部分,不应包含在Polygon
类之外。这不是灾难性的,但在一年后肯定很难理解。
我研究的替代方案:
- 混合。但是,mixin 无法访问基类中的数据。
- 自由浮动函数类似于 Boost 的做法。然而,这有几个问题: 自由浮动函数无法访问受保护的字段。当自由浮动函数需要它时,这些文件需要相互包含导致
Polygon
类是不完整的类型。需要提供指向多边形的指针(不确定这是否会得到优化?)。 - 提供实现类的模板参数。这最终类似于自由浮动函数,因为实现类需要访问 的受保护字段,
Polygon
当Polygon
实现需要它时它是不完整的,并且Polygon
仍然需要以某种方式提供给实现。 - 我曾想过通过继承来实现这一点,其中受保护的数据成员位于私有基类中。子类是详细的实现。然后会有一个包含所有公共函数的公共类,它们仍然可以调用详细实现。然而,这是典型的钻石问题,需要虚拟桌子。不过没有对此进行测试,因为这很难设置。
你认为最好的解决方案是什么?你知道我可以尝试的任何替代方案吗?