示例情况:我正在创建一个用于游戏开发的物理引擎。我有两种类型的空间分区方法:
我想允许将使用我的物理引擎的程序员:
通过模板参数
在编译时选择空间分区方法Physics::World<Physics::Grid> world; // chosen at compile-time
通过多态对象
在运行时选择空间分区方法Physics::WorldRunTime world;
world.setSpatialPartitioningMethod(new Physics::Grid); // chosen at run-time
从上面的例子可以看出,我必须使用两个不同的类(World
和WorldRunTime
)。这会导致代码重复,因为我将拥有一个没有运行时多态性的World
模板类和一个具有运行时多态性的WorldRunTime
。
这没关系。
然而,我想知道是否存在处理此问题的模式,允许程序员将某些内容作为模板参数(编译时多态)传递或一个多态对象(运行时多态),代码重复最少。
某种策略模式,可以在编译时或运行时选择策略吗?
示例所需(?)代码:
{
// *** Compile-time world
// No overhead
Physics::World<Physics::Policy::CompileTime, Physics::Grid> world;
}
{
// *** Run-time world
// Allows run-time spatial partitioning method swapping
// Suffers from run-time polymorphism overhead
Physics::World<Physics::Policy::RunTime> world;
world.setSpatialPartitioningMethod(new Physics::Grid);
}
注意我在两个块中如何使用相同的类,以避免必须编写两个类并重复代码。有没有什么方法可以实现上面的代码?
答案 0 :(得分:3)
对于运行时多态性,使用各种具体策略派生自的多态基类来实例化模板。
为了在不使用运行时多态性时鼓励进行虚拟化,请将具体策略类标记为final
。
答案 1 :(得分:1)
我认为只要您愿意将Physics::Policy::CompileTime
作为模板,这是可以实现的。这是完全未编译的,因为我没有足够的代码来尝试编译实际用例。
template <typename Child>
struct CompileTime
{
void operation1(int v) { child_.operation1(v); }
void setSpatialPartitioningMethod(PhysicsPolicy* policy) {} // No work on static policy.
Child child_;
};
struct RunTime
{
RunTime() : policy_(0) {}
void setSpatialPartitioningMethod(PhysicsPolicy* policy) { policy_ = policy; }
void operation1(int v) { policy_->operation1(v); }
PhysicsPolicy* policy_;
};
template <typename Policy>
class World
{
public:
void setSpatialPartitioningMethod(PhysicsPolicy* policy) { policy_.setSpatialPartitioningMethod(policy); }
private:
Policy policy_;
};
{
// *** Compile-time world
// No overhead
Physics::World<Physics::Policy::CompileTime<Physics::Grid> > world;
}
{
// *** Run-time world
// Allows run-time spatial partitioning method swapping
// Suffers from run-time polymorphism overhead
Physics::World<Physics::Policy::RunTime> world;
world.setSpatialPartitioningMethod(new Physics::Grid);
}
答案 2 :(得分:1)
作为一项心理练习,我想看看我是否能够通过一个额外的约束尽可能接近你想要的语法:如果编译时间是,我不想承担vtbl指针或vtbl本身的费用选择。到目前为止,其他解决方案将在运行时或编译时正确绑定,但即使您选择Policy::CompileTime
这是迄今为止我能想到的最好的结果:
这有一个很大的缺点是Grid变成了模板类,因为我需要让它可选地从PhysicsPolicy继承(以避免vtbl生成)。
您需要的语法与下面的内容之间存在一个小的差异:我需要使用空模板列表来实例化运行时网格,即。 new Physics::Grid<>
我已经覆盖了operator-&gt;所以world-&gt; findObject()将调用该方法,而不必编写一大堆额外的方法 - 另一种方法是将PhysicsPolicy中每个方法的定义添加到Policy::Runtime
。
struct Point3 { float x, y, z; };
namespace Physics
{
// This is a dummy base class used to avoid vtbl creation for Grid
class Empty { };
class PhysicsPolicy
{
public:
virtual void* findObject(const Point3& p) = 0;
};
template<typename BASE=PhysicsPolicy>
class Grid : public BASE
{
public:
void* findObject(const Point3& p) { return nullptr; } // Just a placeholder
};
namespace Policy
{
template<template<typename> class T> class CompileTime
{
public:
T<Empty>* operator->() { return &obj; }
private:
T<Empty> obj;
};
// This is just here so that an empty template parameter list is possible
template<template<typename> class... T> class RunTime;
template<> class RunTime<>
{
public:
void setSpatialPartitioningMethod(PhysicsPolicy* aP) { p = aP; }
PhysicsPolicy* operator->() { return p; }
private:
PhysicsPolicy* p;
};
}
// The desired syntax has the number of parameters dependent upon the policy
// This is done using c++11's variadic templates, and passing the extra
// parameters into Binding
template<
template<template<typename> class...> class Binding,
template<typename> class... U>
class World : public Binding<U...>
{
public:
};
}
int main()
{
Physics::World<Physics::Policy::CompileTime, Physics::Grid> compileTimeWorld;
Point3 p{1,2,3};
printf("%p\n", compileTimeWorld->findObject(p));
Physics::World<Physics::Policy::RunTime> runTimeWorld;
runTimeWorld.setSpatialPartitioningMethod(new Physics::Grid<>);
printf("%p\n", runTimeWorld->findObject(p));
return 0;
}
注意:针对clang测试。不知道它是否适用于其他编译器