我在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;
}
这有两个原因:
使用此设计是否可以替代此转换?
修改 我最终选择了T.C。的建议,尽管略有不同。由于我需要具有自己的参数集的子类,因此能够在构造函数中提供它们是理想的。初始化单例的单独调用容易出错,因为它可以使用错误的模板参数完成,或者只是被遗忘。
为了在一次调用中实现具有参数及其相应单例的专用对象的实例化,我继续使用CRTP并将此构造函数添加到基类:
Controller<T>(){
T::getInstance((T*)this);
}
现在,只需拨打一个电话,我就可以获得所需的一切:
std::shared_ptr<BaseController> c(new SMController(params_, window_));
答案 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);
}