允许运行时和编译时多态的灵活方式?

时间:2014-08-04 15:51:21

标签: c++ templates design-patterns c++11 polymorphism

示例情况:我正在创建一个用于游戏开发的物理引擎。我有两种类型的空间分区方法:

  1. 网格
  2. 四叉树

  3. 我想允许将使用我的物理引擎的程序员:

    1. 通过模板参数

      在编译时选择空间分区方法
      Physics::World<Physics::Grid> world; // chosen at compile-time
      
    2. 通过多态对象

      在运行时选择空间分区方法
      Physics::WorldRunTime world; 
      world.setSpatialPartitioningMethod(new Physics::Grid); // chosen at run-time
      

    3. 从上面的例子可以看出,我必须使用两个不同的类(WorldWorldRunTime)。这会导致代码重复,因为我将拥有一个没有运行时多态性的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);
      }
      

      注意我在两个块中如何使用相同的类,以避免必须编写两个类并重复代码。有没有什么方法可以实现上面的代码?

3 个答案:

答案 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

,它们仍将生成vtbl并通过sizeof(指针)增加World对象的大小

这是迄今为止我能想到的最好的结果:

这有一个很大的缺点是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测试。不知道它是否适用于其他编译器