2014-08-29 65 views
0

我正在使用GLFW进行C++应用程序中的窗口和输入管理。为了使用成员函数作为关键事件的回调函数,我按照答案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

对不起,如果aux未初始化,getInstance(aux)应该如何工作?我错过了什么吗? – 2014-08-29 19:45:08

+0

单例是静态的,所以它仅在第一次调用'getInstance()'时被实例化(这是在别处完成的)。 – broncoAbierto 2014-08-29 19:49:36

+0

典型的CRTP实现从'Base *'到'T *'或'* this'从'Base &'到'T&'投射'this'。 – 2014-08-29 20:01:43

回答

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_; 
}; 
+0

出于某种原因,它看起来像目标永远不会被初始化。我有一个seg故障。 – broncoAbierto 2014-08-29 21:10:03

+0

此外,控制器的每个专业都必须能够访问不同的参数。 – broncoAbierto 2014-08-29 21:20:49

+0

@bronco你仍然需要用一个指向目标的指针来初始化getInstance一次.... – 2014-08-29 23:12:43

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); 
} 
+1

如果我正确地阅读它,OP似乎希望能够使用不同类型的控制器,因此是模板。 – 2014-08-29 20:58:54

+0

这是正确的。 – broncoAbierto 2014-08-29 21:05:45

+0

@broncoAbierto然后T.C.的答案会做。 – 2014-08-29 21:10:46

相关问题