几何库中算法和数据的分离(需要三重调度?)
Separation of algorithms and data in a geometry library (triple-dispatching needed?)
我在设计应用程序中处理几何图形的部分时遇到问题。特别是,我希望有一个类的层次结构和用于交集的单独方法。
问题
层次结构是这样的:
- 几何
- 网格
- 参数化
- 方框
- 球体
交集方法类似于:
namespace intersections
{
bool intersection( const Box &, const Box &);
bool intersection( const Box &, const Sphere &);
}
这很简单。现在,当我想将所有几何图形存储在一个结构中时,问题就出现了,例如std::vector
(或KD树,或其他什么)。
要做到这一点,我需要使用std::vector<Geometry*>
。然而,从这个向量中读取会得到Geometry*
对象,因此我无法调用适当的交集函数。
问题示例:
std::vector<Geometry*> arrGeometry;
// add the elements
arrGeometry.push( new Box() );
arrGeometry.push( new Sphere() );
// ... some more code
// try to intersect them?
Geometry* g1 = arrGeometry[0];
Geometry* g2 = arrGeometry[1];
bool intersecting = intersections::intersection( g1, g2 ); //< As expected, this does
// not work
如果我在几何对象内部实现算法,那么问题可以通过访问者和一些非常奇怪的函数调用弹跳来解决。
但是,我希望将交集算法保留在Geometry类之外。原因是:
以避免决定哪个应该拥有所有权(例如,在
Box
或Sphere
中,在哪里实现长方体和球体之间的交集?)为了避免混淆几何对象,所有可以做的都是几何,这是相当多的(仅举几个例子:体素化,计算交集,应用构造几何运算符…)。因此,在这里,将逻辑与数据分离是非常可取的。
另一方面,我需要一个层次结构而不是模板,因为对于某些事情,特定的几何体可以抽象掉。。。(例如,用于将其存储在std::vector
或KD树中,或…)。
你将如何解决这个问题?有适合这个的设计模式吗?我试着看了一些图书馆,但最终我更加困惑,因为我已经。。。
最简单的方法(有时也会使用)是使用RTTI(或伪造它)和下广播,但这并不完全可维护。。。(添加一个新的几何体意味着通过所有代码更改许多switch语句)。
有什么想法吗?
事先非常感谢。
我想到了另一个解决方案,如果你关心速度,这有O(1)的复杂性,但你可能需要为模式几何类型自动生成c++代码的程序。您可以制作交叉函数数组(伪代码,我手头没有任何编译器):
enum GeometryType
{
TypeMesh,
TypeParamBox,
TypeParamSphere,
MaxType,
};
bool intersection( const Mesh*, const Mesh* );
bool intersection( const Mesh*, const Box* );
bool intersection( const Mesh*, const Sphere* );
bool intersection( const Box*, const Mesh* );
bool intersection( const Box*, const Box* );
bool intersection( const Box*, const Sphere* );
bool intersection( const Sphere*, const Mesh* );
bool intersection( const Sphere*, const Box* );
bool intersection( const Sphere*, const Sphere* );
template<class T1,class T2>
bool t_intersection( const Geometry* first, const Geometry* second )
{
return intersection( static_cast<const T1*>( first ), static_cast<const T1*>( second ) );
}
typedef bool (*uni_intersect)( const Geometry*, const Geometry* );
const uni_intersect IntersectionArray[] = // 2D array all possible combinations
{
t_intersection<Mesh,Mesh>,
t_intersection<Mesh,Box>,
t_intersection<Mesh,Sphere>,
t_intersection<Box,Mesh>,
t_intersection<Box,Box>,
t_intersection<Box,Sphere>,
t_intersection<Sphere,Mesh>,
t_intersection<Sphere,Box>,
t_intersection<Sphere,Sphere>,
};
bool main_intersection( const Geometry* first, const Geometry* second )
{
const unsigned index = (unsigned)(first->Type) * (unsigned)MaxType + (unsigned)(second->Type);
return IntersectionArray[ index ]( first, second );
}
或替代方案:
const uni_intersect IntersectionArray[] = // 2D array all possible combinations
{
t_intersection<Mesh,Mesh>,
t_intersection<Mesh,Box>,
t_intersection<Mesh,Sphere>,
nullptr, // skip mirrored functions
t_intersection<Box,Box>,
t_intersection<Box,Sphere>,
nullptr,
nullptr,
t_intersection<Sphere,Sphere>,
};
bool main_intersection( const Geometry* first, const Geometry* second )
{
const unsigned Type1 = unsigned(first->Type);
const unsigned Type2 = unsigned(second->Type);
unsigned index;
if( Type1 < Type2 )
index = Type1 * (unsigned)MaxType + Type2;
else
index = Type1 + Type2 * (unsigned)MaxType;
return IntersectionArray[ index ]( first, second );
}
这个问题与此类似。
class Geometry
{
public:
enum eType
{
TypeMesh,
TypeParamBox,
TypeParamSphere,
};
Geometry( eType t ): Type( t ) { }
~Geometry() { }
const eType Type;
};
class Mesh : public Geometry
{
public:
Mesh(): Geometry( TypeMesh ) { }
};
class Parametric : public Geometry
{
public:
Parametric( eType t ): Geometry( TypeMesh ) { }
};
class Box : public Parametric
{
public:
Box(): Parametric( TypeParamBox ) { }
};
class Sphere : public Parametric
{
public:
Sphere(): Parametric( TypeParamSphere ) { }
};
namespace intersections
{
bool intersection( const Geometry* first, const Geometry* second );
template <typename T>
bool t_intersection( const T* first, const Geometry* second );
bool intersection( const Box*, const Box* );
bool intersection( const Box*, const Sphere* );
bool intersection( const Sphere*, const Box* );
bool intersection( const Sphere*, const Sphere* );
}
void foo_test()
{
std::vector<Geometry*> arrGeometry;
// add the elements
arrGeometry.push_back( new Box() );
arrGeometry.push_back( new Sphere() );
// ... some more code
// try to intersect them?
Geometry* g1 = arrGeometry[0];
Geometry* g2 = arrGeometry[1];
bool intersecting = intersections::intersection( g1, g2 );
}
bool intersections::intersection( const Geometry* first, const Geometry* second )
{
switch( first->Type )
{
case Geometry::TypeParamBox: return t_intersection( static_cast<const Box*>( first ), second );
case Geometry::TypeParamSphere: return t_intersection( static_cast<const Sphere*>( first ), second );
default: return false;
}
}
template <typename T>
bool intersections::t_intersection( const T* first, const Geometry* second )
{
switch( second->Type )
{
case Geometry::TypeParamBox: return intersection( first, static_cast<const Box*>( second ) );
case Geometry::TypeParamSphere: return intersection( first, static_cast<const Sphere*>( second ) );
default: return false;
}
}
或者,如果您不使用继承:
struct Mesh{};
struct Box{};
struct Sphere{};
enum GeometryType
{
TypeMesh,
TypeParamBox,
TypeParamSphere
};
struct Geometry
{
GeometryType Type;
union
{
Mesh* pMesh;
Box* pBox;
Sphere* pSphere;
} Ptr;
};
namespace intersections
{
bool intersection( const Geometry* first, const Geometry* second );
template <typename T>
bool t_intersection( const T* first, const Geometry* second );
bool intersection( const Box*, const Box* );
bool intersection( const Box*, const Sphere* );
bool intersection( const Sphere*, const Box* );
bool intersection( const Sphere*, const Sphere* );
}
void foo_test()
{
std::vector<Geometry*> arrGeometry;
// add the elements
// arrGeometry.push_back( new Box() );
// arrGeometry.push_back( new Sphere() );
// ... some more code
// try to intersect them?
Geometry* g1 = arrGeometry[0];
Geometry* g2 = arrGeometry[1];
bool intersecting = intersections::intersection( g1, g2 );
}
bool intersections::intersection( const Geometry* first, const Geometry* second )
{
switch( first->Type )
{
case TypeParamBox: return t_intersection( first->Ptr.pBox, second );
case TypeParamSphere: return t_intersection( first->Ptr.pSphere, second );
default: return false;
}
}
template <typename T>
bool intersections::t_intersection( const T* first, const Geometry* second )
{
switch( second->Type )
{
case TypeParamBox: return intersection( first, second->Ptr.pBox );
case TypeParamSphere: return intersection( first, second->Ptr.pSphere );
default: return false;
}
}
注:若你们知道一个几何体的类型,你们可以使用模板函数:
Box* pBox;
Geometry* pUnknownGeometry;
bool intersecting = intersections::t_intersection( pBox, pUnknownGeometry );
注2:您可以编写类似这样的相同函数:
bool intersection( const Box* first, const Sphere* second );
bool intersection( const Sphere* first, const Box* second )
{
return intersection( second, first );
}
编辑:
若您将问题减少为双重调度,则可以使用此技术。
对于交叉点,将层次结构视为:
- 几何
- 网格
- 方框
- 球体
以及其他操作:
- 几何
- 网格
- 参数化
- 方框
- 球体
不如将交集算法表示为类,这些类可以决定它们是否可以处理它们获得的几何图形集,并提供一个具有原始接口的交集代理。这看起来像这样:
class IIntersection
{
public:
bool intersection( const Geometry &, const Geometry &);
bool accept( const Geometry &, const Geometry &);
}
class IntersectionBoxSphere : public IIntersection
{
public:
bool intersection( const Geometry &, const Geometry &);
bool accept( const Geometry & a, const Geometry &b)
{
return ((dynamic_cast<Box>(a) != NULL || dynamic_cast<Box>(b) != NULL)
(dynamic_cast<Sphere>(a) != NULL || dynamic_cast<Sphere>(b) != NULL))
}
}
class IntersectionBoxbox : public IIntersection
...
/**collect some where all algorithms*/
IIntersection* myintersections[2];
myintersections[0] = new IntersectionBoxSphere()
myintersections[1] = new IntersectionBoxbox()
...
/**decide here which to use */
bool intersection( const Geometry &a, const Geometry &b)
{
for ( i ... )
if (myintersections[i]->appect(a,b))
return myintersections[i]->intersection(a,b)
}
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 在静态库中嵌入类方法
- 将静态库链接到不带-fPIC的共享库中
- 如何在 nlohmann 的 json 库中获取数组长度?
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 为什么我的共享库中存在展开符号
- 如何避免在仅标头库中C++类/变量重定义
- CLANG格式在缺少libtinfo.so.5库中不起作用
- 遍历 std::set 中包含的所有三重不同值?
- 如何在C++中进行耗时的三重组合
- 如何修复我的 c++ 毕达哥拉斯三重查找器中的'access violation reading location'错误?
- 在 C 编程中可以有三重减号吗?什么意思
- 几何库中算法和数据的分离(需要三重调度?)
- 特征库中Schur因子分解中的重排序特征值
- 点云库中的凸包计算在二维和三维都失败
- 三重与号"&&&"在C++中代表什么?
- Xcode:为什么重命名为 .mm 失败,仅在静态库中"___gxx_personality_sj0"未定义的符号?
- 在另一个函数中传递一个三重指针来分配内存,sscanf异常
- 在c++代码中是否有任何例子表明三重冒号(:::)是有效的语法?
- CUDA中的三重For循环