2016-06-09 2 views
0

Je travaille actuellement sur un HttpServer et ont le problème suivant:C++ pointeur d'acteur de classe dérivée de pointeur de classe de base

Je veux enregistrer mes contrôleurs et quand je reçois une demande que je veux vérifier si un de ces contrôleurs a un gestionnaire pour l'URL demandée, puis appeler le gestionnaire. Jusqu'ici tout va bien, mais je ne sais pas comment le résoudre en C++, en PHP ce serait facile. Dans la fonction de contrôleur Easteregg getControllerCallbacks, je souhaite renvoyer mes rappels disponibles mais ne compilera pas car je ne peux pas convertir un pointeur de fonction membre d'une classe dérivée en un pointeur de fonction membre (EastereggController) de la classe de base (Manette). J'ai pensé faire du ControllerCallback un "template class" mais ensuite je dois connaître la classe du Controller dans le ControllerHandler, que je ne connais pas car j'ai un std :: vector

Je suis assez nouveau en C++, j'ai peut-être oublié quelque chose.

Ceci est mon code maintenant:

Controller.h

#ifndef CONTROLLER_H 
#define CONTROLLER_H 

#include "Config.h" 
#include "Request.h" 
#include "ControllerCallback.h" 

namespace HttpServer { 

class ControllerCallback; 

class Controller { 
    public: 
     Controller(); 
     virtual std::vector<ControllerCallback> getControllerCallbacks() = 0; 

    private: 
     Config *config; 
     const Request *request; 
}; 
} 
#endif // CONTROLLER_H 

EastereggController.h

#ifndef EASTEREGGCONTROLLER_H 
#define EASTEREGGCONTROLLER_H 

#include "../HttpServer/Controller.h" 

namespace Controller { 

    class EastereggController: public HttpServer::Controller { 
     public: 
      EastereggController() {}; 
      std::vector<HttpServer::ControllerCallback> getControllerCallbacks(); 
      HttpServer::Reply easteregg(); 
    }; 
} 

#endif // EASTEREGGCONTROLLER_H 

EastereggController.cpp

#include "EastereggController.h" 

namespace Controller { 
    std::vector<HttpServer::ControllerCallback> EastereggController::getControllerCallbacks() { 
     std::vector<HttpServer::ControllerCallback> controllerCallbacks; 
     HttpServer::ControllerCallback callback; 

     callback.pathTemplate = "/easteregg"; 
     callback.handlerFunctionPtr = &EastereggController::easteregg; 
     return controllerCallbacks; 
    } 

    HttpServer::Reply EastereggController::easteregg() { 
     HttpServer::Reply rep; 
     rep.content.append("you have found an easteregg\n"); 
     rep.status = HttpServer::Reply::ok; 
     rep.headers.resize(2); 
     rep.headers[0].name = "Content-Length"; 
     rep.headers[0].value = std::to_string(rep.content.size()); 
     rep.headers[1].name = "Content-Type"; 
     rep.headers[1].value = "text/plain"; 
     return rep; 
    } 
} 

ControllerCallback.h

#ifndef CONTROLLERCALLBACK_H 
#define CONTROLLERCALLBACK_H 


#include "Reply.h" 
#include <string> 

namespace HttpServer { 

    class Controller; 

    class ControllerCallback { 

     public: 
      ControllerCallback() 
       : pathTemplate("") {}; 
      //,handlerFunctionPtr(nullptr){} 

      std::string pathTemplate; 
      Reply(Controller::*handlerFunctionPtr)(); 
    }; 
} 

#endif 

ControllerHandler.h

#ifndef CONTROLLERHANDLER_H 
#define CONTROLLERHANDLER_H 

#include <vector> 
#include <string> 
#include "Controller.h" 
#include "Config.h" 
#include "Request.h" 
#include "Reply.h" 

namespace HttpServer { 

    class ControllerHandler { 
     public: 
      ControllerHandler(Config &conf); 
      void registerController(Controller &controller); 
      bool invokeController(std::string requestPath, Request &req, Reply &rep); 

     private: 
      Config &config; 
      std::vector<Controller *> controllers; 

    }; 

} 

#endif // CONTROLLERHANDLER_H 

ControllerHandler.cpp

#include "ControllerHandler.h" 
#include "Controller.h" 
#include "PathHandler.h" 

namespace HttpServer { 
    ControllerHandler::ControllerHandler(Config &conf) 
     : config(conf) { 
    } 

    void ControllerHandler::registerController(Controller &controller) { 
     controllers.push_back(&controller); 
    } 

    bool ControllerHandler::invokeController(std::string requestPath, Request &req, Reply &rep) { 
     PathHandler pathHandler(requestPath, ':'); 

     for(Controller *controller : controllers) { 
      std::vector<ControllerCallback> callbacks = controller->getControllerCallbacks(); 
      for(ControllerCallback controllerCallback : callbacks) { 
       if(pathHandler.compare(controllerCallback.pathTemplate)) { 
        rep = ((*controller).*(controllerCallback.handlerFunctionPtr))(); 
        return true; 
       } 
      } 
     } 

     return false; 
    } 
} 
+0

Je pense que le pointeur vers la fonction membre est basé sur un décalage statique dans la classe, qui ne fonctionnera pas avec les types polymorphes. Vous voulez une fonction virtuelle - passez juste une référence/pointeur vers l'objet, puis appelez la méthode virtuelle Reply() de l'objet. –

+0

donc vous voulez dire que je devrais implémenter la logique où la fonction de gestionnaire est trouvée dans la classe? –

+0

Je suggérais juste de lui donner un objet puis d'appeler Reply() sur cet objet. Vous pouvez toujours utiliser la même logique pour trouver le bon objet pour gérer la requête. –

Répondre

0

J'ai finalement trouvé la réponse (ce qui était une erreur de syntaxe je dois admettre)

Tout en essayant différentes solutions (avant de poster ici) j'avais l'extrait suivant:

EastereggController.cpp

callback.handlerFunctionPtr = reinterpret_cast<HttpServer::Reply HttpServer::Controller::*>(&EastereggController::easteregg); 

qui était syntactilly mal, mais je ne l'ai pas remarqué parce que j'avais l'erreur de compilation suivante:

error: invalid cast from type 'HttpServer::Reply (Controller::EastereggController::*)()' to type 'HttpServer::Reply HttpServer::Controller::*()' 
    callback.handlerFunctionPtr = reinterpret_cast<HttpServer::Reply HttpServer::Controller::*()>(&EastereggController::easteregg); 

Maintenant, la version correcte:

callback.handlerFunctionPtr = reinterpret_cast<HttpServer::Reply(HttpServer::Controller::*)()>(&EastereggController::easteregg); 

vient d'ajouter les parenthèses et maintenant il fonctionne.