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 :

Collection & callbacks


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut Collection & callbacks
    Bonsoir,

    Je suis à la recherche de quelques indications pour réaliser l'implémentation d'une collection de callbacks un peu particulière.

    Je compte réaliser une classe utilisant une std::map dans laquelle je pourrais stocker des fonctions.
    Le but est de les appeler lors du retour d'échanges asynchrones avec un serveur distant, typiquement en HTTP.
    Ces fonctions seraient enregistrées sous divers identifiants tels que "success", "error", "timeout"... pour être appelées dans ces différents cas sous cette forme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    int retour = clbk.call("success", arg1, arg2, ..., argN);
    Néanmoins, je rencontre 3 problèmes majeurs, sans également savoir si la formalisation de mon besoin est compatible avec des langages comme le c++.
    La 1ere chose est de savoir quel type C++ utiliser pour représenter une fonction de rappel. Il semble y avoir boost::function mais aussi d'autre types en c++ natif. Lequel choisir?

    Le second problème, il ne sera pas des moindres, est la manière dont je compte appeler mes fonctions : avec un nombre et un type d'argument variable (cf le code ci-dessus). Est-ce possible en C++?
    Il n'est pas exclus que tous mes callbacks "success" puissent avoir la même signature, mais deux callbacks "success" et "error" auront forcément des différences. Utiliser ma méthode call() pour tous les appeler n'est peut-être pas implémentable.

    Le 3ième reste le type de retour. Si call() renvoie lui-même le retour de la fonction appelée et qu'il est sans cesse différent je risque d'être un peu coincé.

    Existe-t-il une solution à ce type de problème?
    Je connais l'exemple de printf, qui peut admettre un nombre variables d'arguments de différents type sans savoir comment déclarer une telle méthode de classe.

    Merci par avance pour vos éclairages éventuels.

  2. #2
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Hello,

    Citation Envoyé par fanfouer Voir le message
    La 1ere chose est de savoir quel type C++ utiliser pour représenter une fonction de rappel. Il semble y avoir boost::function mais aussi d'autre types en c++ natif. Lequel choisir?
    Il y a boost::function et std::function (et probablement d'autres, mais ce sont surement les plus courant)
    Si tu peux utiliser C++11, les std::function c'est cool.

    Citation Envoyé par fanfouer Voir le message
    Le second problème, il ne sera pas des moindres, est la manière dont je compte appeler mes fonctions : avec un nombre et un type d'argument variable (cf le code ci-dessus). Est-ce possible en C++?
    Il n'est pas exclus que tous mes callbacks "success" puissent avoir la même signature, mais deux callbacks "success" et "error" auront forcément des différences. Utiliser ma méthode call() pour tous les appeler n'est peut-être pas implémentable.
    Oui c'est possible, après si tu distribue les appels à d'autres threads pour les exécuter plus tards, faut regarder du coté de std::future (ou de l'équivalent boost)

    Citation Envoyé par fanfouer Voir le message
    Le 3ième reste le type de retour. Si call() renvoie lui-même le retour de la fonction appelée et qu'il est sans cesse différent je risque d'être un peu coincé.
    Tant que tu sais à l'avance quelle fonction retourne quoi, ça devrait aller.
    (Ce qui peut poser problème si tu reçois une chaîne de caractères et que tu exécutes la fonction correspondante)

    Il faut que tu crée une classe non template qui contiendra une std::function ; non template pour pouvoir créer une map de ce type, ici l'héritage permet ça :
    Une classe de base non template, héritée par une classe template qui contiendra la std::function.
    Les template variadic permettent un nombre quelconque de paramètres.

    Après un dynamic_cast permet de tester que les types et le nombre des arguments fournis correspondent au prototype de la fonction.

    Au final ça donne un truc du genre
    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
    #include <functional>
    #include <iostream>
    #include <string>
    #include <map>
    #include <memory>
     
    class FctBase;
     
    template <class Ret, class... Args>
    class Fct;
     
    class FctBase {
     
    	// oui c'est sale :/
    	virtual void virtualFctPourDynamicCast() { }
     
    public:
    	template<class Ret, class... Args>
    	Ret doJob(Args... args) const {
    		const Fct<Ret, Args...> *_this = dynamic_cast<const Fct<Ret, Args...>*>(this);
    		if(!_this) {
    			throw std::exception();
    		}
     
    		return _this->call(args...);
    	}	
    };
     
    template <class Ret, class... Args>
    class Fct: public FctBase {
     
    	const std::function<Ret(Args...)> m_fct;
     
    public:
     
    	Fct(const std::function<Ret(Args...)> fct):
    		m_fct(fct)
    	{ }
     
    	~Fct() { }
     
    	Ret call(Args... args) const {
    		return m_fct(args...);
    	};
     
    };
     
     
    template <class Ret, class... Args>
    Ret call(const Fct<Ret, Args...> *fct, Args... args) {
    	return fct->call(args...);
    } 
     
    int main(int argc, char** argv) {
     
    	typedef std::unique_ptr<FctBase> ptr;
     
    	std::map<std::string, ptr> fctMap;
     
    	auto fct1 = [](int i, int j)->int { return i+j; };
    	auto fct2 = [](char c)->float { return c/255.f; };
     
    	// insert
    	fctMap.insert(std::make_pair("fct1", ptr(new Fct<int, int, int>(fct1))));
    	fctMap.insert(std::make_pair("fct2", ptr(new Fct<float, char>(fct2))));
     
    	// recherche et exec si trouvé	
    	auto itFct1 = fctMap.find("fct1");
    	if(itFct1 != fctMap.end()) {
    		try {
    			int ret = itFct1->second->doJob<int, int, int>(1, 2);
    			std::cout << "fct1(1, 2) = " << ret << std::endl;
    		}
    		catch(...) {
    			std::cout << "fct1(1, 2) fail\n";
    		}		
    	}
     
    	auto itFct2 = fctMap.find("fct2");
    	if(itFct2 != fctMap.end()) {
    		try {
    			float ret = itFct2->second->doJob<float, char>('h');
    			std::cout << "fct2('h') = " << ret << std::endl;
    		}
    		catch(...) {
    			std::cout << "fct2('h') fail\n";
    		}
    	}
     
    	// mauvais appel
    	if(itFct1 != fctMap.end()) {
    		try {
    			int ret = itFct1->second->doJob<int, char>('h');
    			std::cout << "fct1('h') = " << ret << std::endl;
    		}
    		catch(...) {
    			std::cout << "fct1('h') fail\n";
    		}
    	}
     
    	return 0;
    }
    sortie:
    fct1(1, 2) = 3
    fct2('h') = 0.407843
    fct1('h') fail

    Code pas spécialement propre mais ça donne une idée.
    D'ailleurs t'aura surement besoin d'une std::multimap vu que plusieurs fonctions ont le même nom.

  3. #3
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Il est intéressant dans ce genre de cas de différencier deux types d'arguments pour tes fonctions :
    - Ceux qui ont une valeur qui peut être connue quand on enregistre la fonction dans la map
    - Ceux qui ont une valeur uniquement lorsqu'on veut appeler la fonction

    Assez souvent, on se retrouve avec un nombre variable d'arguments dans la première catégorie, mais un nombre identique dans la seconde. Il est alors possible de mettre des [boost/std]::fonction dans le conteneur qui prennent uniquement les arguments variables, et d'adapter les fonctions ayant trop d'arguments vers ces xxx::function à l'aide de [boost/std]::bind, de lambdas, ou autres.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  4. #4
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Bonsoir,

    merci pour vos réponses

    Citation Envoyé par Iradrille Voir le message
    Il y a boost::function et std::function (et probablement d'autres, mais ce sont surement les plus courant)
    Si tu peux utiliser C++11, les std::function c'est cool.
    A condition que Visual Studio supporte cette fonctionnalité de C++11 je suis d'accord pour utiliser la STL plutôt que boost

    Oui c'est possible, après si tu distribue les appels à d'autres threads pour les exécuter plus tards, faut regarder du coté de std::future (ou de l'équivalent boost)
    Normalement ça n'est pas prévu. Il faut uniquement tenir compte d'appels dans le même thread que la routine asynchrone (peut-être était-ce ton idée? Je ne prévois pas de threader particulièrement l'appel au callback).

    Tant que tu sais à l'avance quelle fonction retourne quoi, ça devrait aller.
    (Ce qui peut poser problème si tu reçois une chaîne de caractères et que tu exécutes la fonction correspondante)
    Du point de vue du déclencheur des callbacks ca n'est pas le cas (mine de rien).
    Bien souvent, mes callbacks retournent void mais sait-on jamais ce qui peut se présenter demain.

    Il faut que tu crée une classe non template qui contiendra une std::function ; non template pour pouvoir créer une map de ce type, ici l'héritage permet ça :
    Une classe de base non template, héritée par une classe template qui contiendra la std::function.
    Les template variadic permettent un nombre quelconque de paramètres.
    Je comprends très bien le besoin de l'extension pour obtenir à un moment un type non template à manipuler.
    Cependant pourquoi ne pas stocker un std::function directement dans la map?

    En fait, une seule fonction portera un nom donné dans la collection.
    Je peux avoir deux callback "success" mais ils seront dans deux collections séparées. Nul besoin d'user de multimap ici.

    Cela permet-il de simplifier quelque peu l'implémentation proposée?

    Après un dynamic_cast permet de tester que les types et le nombre des arguments fournis correspondent au prototype de la fonction.
    Oui, le dynamic_cast peut permettre de vérifier que la bonne signature est fournie sans pour autant permettre un quelconque choix puisqu'il n'y a qu'une seule signature pour chaque nom de callbacks.


    Au niveau du code, cela correspond globalement à ce que j'ai dans l'idée si ce n'est que je pense faire quelque chose de plus intégré au niveau de l'appel.
    La classe qui gèrerait tout mes callbacks peut avoir deux principales méthodes :

    - insert (std::string clbk_name, std::function clbk_func) : pour insérer les callback dans la collection

    - call (std::string clbk_name, Args... args); : pour appeler le callback.

    Il est intéressant dans ce genre de cas de différencier deux types d'arguments pour tes fonctions :
    - Ceux qui ont une valeur qui peut être connue quand on enregistre la fonction dans la map
    - Ceux qui ont une valeur uniquement lorsqu'on veut appeler la fonction
    Je ne pense pas.
    Si ces arguments peuvent obtenir une valeur fixe dès l'enregistrement du callback (soit bien avant l'appel de la routine qui va s'en servir), c'est que ce sont des "constantes" non?
    Enfin, j'en suis peut-être encore trop au stade javascript et son scope "sables mouvants". Surement qu'en C++ il faudra prévoir ce genre de valeurs, qui ne sont pas fournies par la routine et qui dépendent du contexte d’exécution.
    Je pense que si je peux m'en passer ce ne sera pas de refus, mais c'est une question de design

    Quel est votre avis?

    Bonne soirée!

  5. #5
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    A condition que Visual Studio supporte cette fonctionnalité de C++11 je suis d'accord pour utiliser la STL plutôt que boost
    std::function est supporté par VS2012, par contre pour les templates variadic, il te faudra le CTP de novembre

    Citation Envoyé par fanfouer Voir le message
    Je comprends très bien le besoin de l'extension pour obtenir à un moment un type non template à manipuler.
    Cependant pourquoi ne pas stocker un std::function directement dans la map?
    Car justement std::function est une classe template, si toutes tes fonctions ont le même prototype alors oui tu peux.
    Par exemple si elles prennent toutes un int en paramètre et retournent rien, tu pourrais déclarer map<string, function<void(int)> > maMap;. Mais lorsque les prototypes sont différents, tu ne peux plus donner les arguments template de std::function
    Citation Envoyé par fanfouer Voir le message
    En fait, une seule fonction portera un nom donné dans la collection.
    Je peux avoir deux callback "success" mais ils seront dans deux collections séparées. Nul besoin d'user de multimap ici.

    Cela permet-il de simplifier quelque peu l'implémentation proposée?
    Dans ce que je proposais, je prenais pas en compte le fait que 2 fonctions aient le même nom (je disais juste qu'il fallait le faire :p). Mais oui ça simplifie l'implémentation, pas besoin d'itérer sur une liste de fonctions possible.

  6. #6
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Merci pour cette réponse.

    Citation Envoyé par Iradrille Voir le message
    std::function est supporté par VS2012, par contre pour les templates variadic, il te faudra le CTP de novembre
    Excellent, je l'ai déjà installé.

    Car justement std::function est une classe template
    Oui, très juste. Je comprends la nécessité d'avoir un wrapper un peu sophistiqué.

    Dans ce que je proposais, je prenais pas en compte le fait que 2 fonctions aient le même nom (je disais juste qu'il fallait le faire :p). Mais oui ça simplifie l'implémentation, pas besoin d'itérer sur une liste de fonctions possible.
    D'accord. Je verrai donc ça demain pour l'implémentation et je reviendrai par ici donner le résultat.

  7. #7
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Pour compléter la réponse de Loïc, si tu veux être générique sur tes callbacks, elles doivent un minimum répondre à la même signature (tu peux définir plusieurs « types » éventuellement). Sinon, cela veut dire que tu as besoin de savoir quelle callback tu appelles pour savoir quels arguments lui passer, et toute l’extensibilité run-time de ton système est cassée.

    Après, libre à tes callbacks d’ignorer certains paramètres qui leurs sont passés.

    Idem pour la valeur de retour : elle ne sera exploitée que par l’exécutant de la callback, pas celui qui l’a enregistrée. De fait, elle devrait donc avoir une sémantique fixe pour toutes les callbacks. Une callback doit respecter un certain prototype, qui est défini par ton manager de callbacks.

  8. #8
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Quelques questions sur le code à proprement dit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    // oui c'est sale :/
    virtual void virtualFctPourDynamicCast() { }
    A quoi cette fonction virtuelle sert-elle?
    Pourquoi est-ce si sale, surtout si on ne s'en sert pas par la suite?

    De plus, ne serait-il pas possible de fusionner FctBase::doJob et Fct::call()?
    Je vois bien que doJob fait le dynamic_cast pour ensuite pouvoir en gros appeler un "call" correct avec les bons types.
    D'un autre côté ca reste un niveau de plus dans ma pile d'appels.
    Si on peut tout faire en même temps je ne m'en porterais que mieux

    J'ai également des erreurs de compilation autour de:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    public: template <class Ret, class... Args> Ret doJob(Args... args) const;
    dans mon fichier FctBase.h :

    error C2332: 'class'*: nom de balise manquant
    error C2993: ''*: type non conforme pour le paramètre de modèle sans type '<unnamed-tag>'
    error C2143: erreur de syntaxe*: absence de ',' avant '...'
    error C2988: impossible de reconnaître la définition/déclaration de modèle
    error C2059: erreur de syntaxe*: 'const'
    A peu près idem dans le .cpp quand je tente la déclaration.

    Merci pour vos retours

  9. #9
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    La fonction virtuelle comme son nom l'indique sert à faire marcher le dynamic_cast, "error C2683: 'dynamic_cast' : 'FctBase' is not a polymorphic type" sans.
    C'est sale, car ya très certainement une erreur de conception quelques part pour que je sois obligé de rajouter cette fonction.

    Pour fusionner FctBase::doJob et Fct::call(), je ne pense pas que ce soit possible, mais les 2 fonctions seront probablement inline de toute façon.

    Les erreurs de compilation doivent venir du non support des templates variadic.
    (Sous VS2012 avec le ctp de'installé, propriétés du projet > ensemble d'outil de la plateforme (dans Général) > VC++ Compiler 2012 CTP)

    edit: Fonction template donc à implémenter dans le .h.

  10. #10
    Membre éclairé
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2008
    Messages
    253
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2008
    Messages : 253
    Par défaut
    Pour l'instant ça compile, nous verrons prochainement pour l’exécution

    Citation Envoyé par Iradrille Voir le message
    La fonction virtuelle comme son nom l'indique sert à faire marcher le dynamic_cast, "error C2683: 'dynamic_cast' : 'FctBase' is not a polymorphic type" sans.
    C'est sale, car ya très certainement une erreur de conception quelques part pour que je sois obligé de rajouter cette fonction.
    D'accord. Personnellement ca ne me gêne pas. Si quelqu'un à mieux, je pourrai modifier le code.

    Pour fusionner FctBase::doJob et Fct::call(), je ne pense pas que ce soit possible, mais les 2 fonctions seront probablement inline de toute façon.
    Oui en effet je pense avoir compris pourquoi.

    Les erreurs de compilation doivent venir du non support des templates variadic.
    (Sous VS2012 avec le ctp de'installé, propriétés du projet > ensemble d'outil de la plateforme (dans Général) > VC++ Compiler 2012 CTP)

    edit: Fonction template donc à implémenter dans le .h.
    Merci pour la modification, ça fonctionne mieux.

    J'ai indiqué la signature des fonctions template dans le .h, le reste du code est dans le cpp sans que je trouve d'erreur de compilation.
    Cela va-t-il en créer au runtime?

  11. #11
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Citation Envoyé par fanfouer Voir le message
    J'ai indiqué la signature des fonctions template dans le .h, le reste du code est dans le cpp sans que je trouve d'erreur de compilation.
    Cela va-t-il en créer au runtime?
    Ca compile car tu ne dois pas utiliser les fonctions, dès que tu les utiliseras, tu aura une erreur de link te disant que le corps de la fonction est introuvable.

    (Petite question HS: Même si ya des erreurs de syntaxe dans le corps d'une fonction template, VS ne relève pas d'erreur à la compilation tant que la fonction n'est pas instanciée.
    Quelqu'un sait ce que dit la norme à ce sujet ?)

  12. #12
    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
    Citation Envoyé par Iradrille Voir le message
    La fonction virtuelle comme son nom l'indique sert à faire marcher le dynamic_cast, "error C2683: 'dynamic_cast' : 'FctBase' is not a polymorphic type" sans.
    C'est sale, car ya très certainement une erreur de conception quelques part pour que je sois obligé de rajouter cette fonction.
    Tu ne peux pas plutot virtualiser le destructeur?

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

Discussions similaires

  1. [VB6] Sauvegarder une collection d'objets
    Par Sayagh dans le forum VB 6 et antérieur
    Réponses: 7
    Dernier message: 19/09/2003, 11h58
  2. [VB6] la collection controls
    Par tomnie dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 30/04/2003, 17h03
  3. Comment créér une collection sous Delphi
    Par PsyKroPack dans le forum Langage
    Réponses: 6
    Dernier message: 11/02/2003, 13h20
  4. [VB6] Modifier la clé d'un élément d'une collection
    Par Ricou13 dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 21/11/2002, 14h49

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