替代模板参数的转换构造函数

时间:2014-08-29 19:34:17

标签: c++ implicit-conversion crtp

我在C ++应用程序中使用GLFW进行窗口和输入管理。为了使用成员函数作为键事件的回调,我在答案here中建议使用单例。

但是,我需要实现不同的输入处理程序。我的方法是使用Singleton作为实际输入处理程序(controller_)的包装器以允许多态。但是,对于要实例化的Singleton,基类不能是抽象的。解决方案涉及使用CRTP,以便能够从基类的实现中调用特定的输入处理方法。

  template <class T>
  class Controller : public BC{ //BC is just for using this class as a template parameter itself
    public:
    Controller(){}                    
    Controller(Controller &controller){
      controller_ = &controller;
    }

    static Controller& getInstance(Controller *controller){
      static Controller instance(*controller);
      return instance;
    }   

    //This is the key move, where the concrete implementation is invoked.
    static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods){
      Controller *aux;
      (static_cast<T>(getInstance(aux))).keyCallbackImpl(window, key, scancode, action, mods); 
    }

    //Stub to be overridden by the concrete input handler        
    virtual void keyCallbackImpl(GLFWwindow* window, int key, int scancode, int action, int mods){}

    //This is the wrapped input handler
    Controller *controller_;   

  };

这很好用。但是,派生类存在问题。为了能够执行转换,我必须定义转换构造函数。

SMController(Controller<SMController> &c){
  controller_ = c.controller_;
  std::cout << "Constructor" << std::endl;
}

这有两个原因:

  • 从Controller派生的用户必须明确定义此构造函数
  • 每次按下按键时构造一个新的包装器似乎很昂贵

使用此设计是否可以替代此转换?

修改 我最终选择了T.C。的建议,尽管略有不同。由于我需要具有自己的参数集的子类,因此能够在构造函数中提供它们是理想的。初始化单例的单独调用容易出错,因为它可以使用错误的模板参数完成,或者只是被遗忘。

为了在一次调用中实现具有参数及其相应单例的专用对象的实例化,我继续使用CRTP并将此构造函数添加到基类:

Controller<T>(){
  T::getInstance((T*)this);
}

现在,只需拨打一个电话,我就可以获得所需的一切:

std::shared_ptr<BaseController> c(new SMController(params_, window_));

2 个答案:

答案 0 :(得分:2)

实际上,我并没有真正看到在这里使用CRTP的重点。将指针直接存储到模板参数的问题是什么?

template <class T>
class Controller : public BC { 
public:
  static Controller& getInstance(T * target = nullptr){
    static Controller instance(target);
    return instance;
  }   

  static void keyCallback(GLFWwindow* window, int key, int scancode,
                          int action, int mods){
    getInstance().target_->keyCallbackImpl(window, key, scancode, action, mods); 
  }
private:              
  Controller(T* target) : target_(target) { }   
  //This is the wrapped input handler
  T* target_;
};

答案 1 :(得分:1)

据我所知,你想要的是能够用它的一个子类初始化Controller实例来处理输入。为此,您不需要CRTP,而且您根本不需要模板。见代码:

// Controller.h
class Controller {
    // disallow copying
    Controller(const Controller&) = delete;
    Controller& operator=(const Controller&) = delete;

    //This is the wrapped input handler
    static Controller* instance_;

protected:    
    Controller() = default;

public:
    virtual ~Controller() = default;

    static void initInstance(Controller* controller) {
        // ensures you set instance only once
        // you can also put run-time assert here
        static Controller* instance = controller;
        instance_ = instance;
    }

    static Controller* getInstance() {
        return instance_;
    }

    //This is the key move, where the concrete implementation is invoked.
    static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods){
        getInstance()->keyCallbackImpl(window, key, scancode, action, mods); 
    }

    //Stub to be overridden by the concrete input handler        
    virtual void keyCallbackImpl(GLFWwindow* window, int key, int scancode, int action, int mods) = 0;
};

class SMController : public Controller {
public:
    virtual void keyCallbackImpl(GLFWwindow* window, int key, int scancode, int action, int mods) override {
        std::cout << "in SMController::keyCallbackImpl()\n";
    }
};


// Controller.cpp
Controller* Controller::instance_ = nullptr;

// test.cpp
int main()
{
    SMController smc;
    Controller::initInstance(&smc);
    Controller::keyCallback(nullptr, 0, 0, 0, 0);
}