IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Gestionnaire de services


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut Gestionnaire de services
    Bonjour,

    Dans le cadre d'un développement en C++ sur micro-contrôleur, je cherche un mettre un place un gestionnaire de services. L'idée est :
    • Au démarrage de l'application, on détecte certains paramètres et on instancie les services concrets (qui répondent à des interfaces) correspondants.
    • On enregistre les services concrets auprès d'un gestionnaire de services.
    • Par la suite, quand on a besoin d'un service, on demande au gestionnaire l'implémentation d'un service répondant à l'interface souhaitée.
    • On utilise de manière polymorphe l'implémentation concrète du service en utilisant l'interface à laquelle il répond.


    J'ai déjà utilisé des mécanismes similaires pour des projets Java embarqués et j'aimerai retrouver quelque chose de similaire. En Java, on utilisait un objet de type Class pour enregistrer et récupérer le service. Voilà à quoi ressemble une implémentation triviale d'un tel gestionnaire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package com.gradot.serviceproviding;
     
    import java.util.HashMap;
     
    public class ServiceManager {
     
    	public static ServiceManager INSTANCE = new ServiceManager();
     
    	private HashMap<Class<?>, Object> serviceMap = new HashMap<>();
     
    	private ServiceManager() {
    		// Hide constructor
    	}
     
    	public <T> void register(Class<T> clazz, T service) {
    		serviceMap.put(clazz, service);
    	}
     
    	@SuppressWarnings("unchecked")
    	public <T> T get(Class<T> clazz) {
    		return (T) serviceMap.get(clazz);
    	}
     
    }
    Lors de l’utilisation, on aura quelque chose comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public class Main {
     
    	public static void main(String[] args) {
    		init();
    		run();
    	}
     
    	private static void init() {
    		// Create and register services
    		TemperatureService temp = new OutsideTemperature();
    		ServiceManager.INSTANCE.register(TemperatureService.class, temp);
     
    		PrinterService printer = new StandardPrinter();
    		ServiceManager.INSTANCE.register(PrinterService.class, printer);
    	}
     
    	private static void run() {
    		// Retrieve the service implementations
    		TemperatureService temperatureService = ServiceManager.INSTANCE.get(TemperatureService.class);
    		PrinterService printerService = ServiceManager.INSTANCE.get(PrinterService.class);
     
    		// Use them
    		double temperature = temperatureService.getTemperature();
    		String unit = temperatureService.getUnit();
    		printerService.print(temperature + unit);
    	}
    }
    Auriez-vous des idées pour faire quelque chose de similaires en C++ ? Suis-je obligé de passer par un ID numérique pour la clé d'enregistrement ?

    Merci d'avance !

  2. #2
    Expert éminent

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par défaut
    Je partirai vers un identifiant unique constant (et membre statique).
    Le choix d'un identifiant numérique est assez bon, mais un char const * const pourrait aller.

    Sinon, il faut passer par une usine à gaz compliquée pour quasiment aucune valeur ajoutée.
    On pourrait partir vers un type dédié à l'identification des classes, template.
    par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template <typename T>
    struct ID {
        bool operator == (ID const&) const {return true;}
     
        template <typename U>
        bool operator == (ID<U> const&) const {return false;}
    };
    Mais ca ne résoud ni le hash (pour une unordered_map), ni le operator< (pour une map)

  3. #3
    Membre expérimenté Avatar de RPGamer
    Homme Profil pro
    Ingénieur en systèmes embarqués
    Inscrit en
    Mars 2010
    Messages
    168
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ingénieur en systèmes embarqués

    Informations forums :
    Inscription : Mars 2010
    Messages : 168
    Par défaut
    Salut,

    L'opérateur typeid() retourne un std::type_info en C++. std::type_info::name() retourne un identifiant du type. Avec un petit traitement sur cet identifiant, tu peux en extraire le nom de la classe.

    Ainsi, tu pourras instancier un service MyService avec quelques chose comme:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ServiceManager::getInstance().get("MyService");

  4. #4
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Re-bonjour,

    J'ai fait quelque essais ce matin et je suis arrivé à quelque chose de satisfaisant. Et j'ai justement utilisé typeid

    Voici le résultat, n'hésitez pas à donner vos commentaires !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    #include <cstdlib>
    #include <ctime>
    #include <iostream>
    #include <typeinfo>
    #include <unordered_map>
     
     
    #include <sstream>
    namespace std {
    // Patch because MinGW doesn't know std::to_string()
    // See http://stackoverflow.com/questions/12975341/to-string-is-not-a-member-of-std-says-g
    template<typename T> std::string to_string(const T& n) {
    	std::ostringstream stm;
    	stm << n;
    	return stm.str();
    }
    }
     
    //--- Service manager-----------------------------------------------
    class Service {
    	// The purpose of this class is meant to have a common type for all service classes
    	// so that the ServiceManager can handle them
    };
     
    class ServiceHasher {
    public:
    	unsigned long operator()(const std::type_info* info) const {
    		return (unsigned long) info;
    	}
    };
     
    class ServiceManager {
    public:
    	template<class ServiceInterface>
    	void registerService(ServiceInterface* service) {
    		service_map[&typeid(ServiceInterface)] = service;
    	}
     
    	template<class ServiceInterface>
    	ServiceInterface& getService() {
    		return (ServiceInterface&) *service_map[&typeid(ServiceInterface)];
    	}
     
    private:
    	std::unordered_map<const std::type_info*, Service*, ServiceHasher> service_map;
    };
     
    //--- Service interfaces--------------------------------------------
    class PrinterService: public Service {
    public:
    	virtual ~PrinterService() {
    	}
    	virtual void print(std::string) = 0;
    };
     
    class TemperatureService: public Service {
    public:
    	virtual ~TemperatureService() {
    	}
    	virtual double getTemperature() = 0;
    	virtual std::string getUnit() = 0;
    };
     
    //--- Service implementations---------------------------------------
    class StandardPrinter: public PrinterService {
    public:
    	void print(std::string text) {
    		std::cout << text << std::endl;
    	}
    };
     
    class OutsideTemperature: public TemperatureService {
    public:
    	double getTemperature(void) {
    		std::srand(std::time(0));
    		return (double) std::rand() + (double) std::rand() / 10;
    	}
     
    	std::string getUnit(void) {
    		return "°C";
    	}
    };
     
    //--- Application---------------------------------------------------
    ServiceManager manager;
     
    void init(void) {
    	// Create services
    	OutsideTemperature* temperature = new OutsideTemperature;
    	StandardPrinter* printer = new StandardPrinter;
     
    	// Register them
    	manager.registerService<TemperatureService>(temperature);
    	manager.registerService<PrinterService>(printer);
    }
     
    void run(void) {
    	TemperatureService& temperatureService = manager.getService<
    			TemperatureService>();
    	PrinterService& printerService = manager.getService<PrinterService>();
     
    	double temperature = temperatureService.getTemperature();
    	std::string unit = temperatureService.getUnit();
     
    	std::string message = std::to_string(temperature) + unit;
    	printerService.print(message);
    }
     
    int main(int argc, char **argv) {
    	init();
    	run();
    }
    Le dernier truc qui me dérange en l'état actuel des choses est l'existence de la classe Service, qui sert uniquement à avoir un typage commun pour la map privée au gestionnaire de services.

  5. #5
    Membre expérimenté Avatar de RPGamer
    Homme Profil pro
    Ingénieur en systèmes embarqués
    Inscrit en
    Mars 2010
    Messages
    168
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ingénieur en systèmes embarqués

    Informations forums :
    Inscription : Mars 2010
    Messages : 168
    Par défaut
    Le propre d'un service est en général de pouvoir être démarré et arrêté quand bon nous semble, l'interface Service n'est donc pas totalement inutile puisque son code pourrait être :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    struct Service
    {
        virtual void start() = 0;
        virtual void stop() = 0;
    }
    Quelques remarques sur ton code :

    • Evite les instances et variables globales.
    • Il existe de bien meilleures outils que la fonction rand() du langage C pour de l'aléatoire en C++.
    • Je ne suis pas certain qu'une fonction init() soit judicieux dans un programme C++ (au passage l'argument void est inutile).
    • Pas de cast à la C en C++
    • Qui libère la mémoire ?

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Merci pour tes commentaires.

    Le propre d'un service est en général de pouvoir être démarré et arrêté quand bon nous semble, l'interface Service n'est donc pas totalement inutile puisque son code pourrait être :
    Ici, l'instanciation des services se fera au démarrage et ne s'arrêteront pas. Mais c'est effectivement une idée d'extension, je retiens.

    Evite les instances et variables globales.
    Oui, naturellement. Je voulais faire un singleton avec un getInstance() pour la classe ServiceManager mais je n'ai pas encore pris le temps de le faire.

    Il existe de bien meilleures outils que la fonction rand() du langage C pour de l'aléatoire en C++.
    As-tu un lien à me donner ?

    Je ne suis pas certain qu'une fonction init() soit judicieux dans un programme C++ (au passage l'argument void est inutile).
    L'idée ici était plus de bien séparée les 2 phases de création et d'utilisation des services. Je ne sais pas si le code réel ressemblera à ça.
    Effectivement, lors de la définition de la fonction, void n'est pas utile, mais j'ai l'habitude de le mettre Sans doute au cas où plus tard j'ai besoin d'extraire le prototype pour en faire une déclaration dans un header.

    Pas de cast à la C en C++
    Faut que je vois ça... Je présume que je vais devoir chercher du côté de static_cast ?

    Qui libère la mémoire ?
    Comme dit plus haut, je ne prévois pas d'éteindre mes services donc je n'ai pas besoin de libérer la mémoire. Si cela devait arriver un jour, je pense faire une fonction unregisterService() qui s'occuperait de la libération, en faisant un delete après avoir appelé une possible méthode Service.stop() . Je me dit en même temps que je n'ai pas de garantie que le service ait bien été alloué avec new. C'est peut-être l'adresse d'une variable locale toujours existante, d'une variable globale, etc. Que se passe t-il si j'appelle delete dessus ?

    J'avais d'ailleurs fait une version où je ne passais pas un pointeur nu pour enregistrer le service mais un shared_ptr. Comme je n'avais pas de besoin de libération, je ne voyais pas l'utilité. D'ailleurs, je suis étonné que tu n'es pas fait de remarque sur ce pointeur nu, comme tu l'as fait dans ma précédente discussion.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 13/02/2012, 14h09
  2. Gestionnaire de services Oracle
    Par khayyam90 dans le forum Contribuez
    Réponses: 0
    Dernier message: 28/12/2010, 22h59
  3. Connecter au gestionnaire des service d'un pc sous reseaux
    Par youcef81 dans le forum Sécurité
    Réponses: 1
    Dernier message: 25/12/2006, 14h23
  4. Se connecter au gestionnaire de services windows d'un pc en reseau
    Par youcef81 dans le forum Autres Logiciels
    Réponses: 2
    Dernier message: 17/08/2006, 09h41
  5. Gestionnaire de Service Onglet Connexion
    Par yosthegost dans le forum Delphi
    Réponses: 5
    Dernier message: 22/05/2006, 17h55

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo