2014-08-29 66 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

相關問題