J'essaie de stocker un ensemble de STD :: Fonction sur une carte (sous GCC 4.5)
J'aimerais avoir 2 types de choses: p>
Je pense que j'ai atteint le premier avec une commande de classe et un gestionnaire: p> peut être utilisé comme ceci: p> int main()
{
Print p = Print();
function<void(string, string)> f1(bind(&Print::print1, &p, placeholders::_1, placeholders::_2));
CommandManager cmdMgr = CommandManager();
cmdMgr.add("print1", new Command(f1));
cmdMgr.execute("print1", string("test1"), string("test2"));
return 0;
}
3 Réponses :
Votre Si vous avez besoin de fonctions qui acceptent des arguments variables (comme Si vous avez juste besoin d'un nombre variable d'arguments de chaîne, utilisez des fonctions qui acceptent E.G. vecteurs de cordes. p>
Tout cela n'a rien à faire avec Constructeur de classe nécessite une fonction
printf code>), vous aurez besoin de
fonction <> code> et
exécution () code> qui accepte la variable arguments. Vous devez savoir comment travailler avec cela (en particulier, vous avez besoin d'un premier argument fixe). Vous êtes alors responsable de la sécurité de type, tout comme avec
printf code>. P>
std :: map code>. Quoi que vous puissiez stocker dans une variable ancienne simple, vous pouvez stocker dans
std :: map code> aussi. P>
Vous pouvez utiliser Dynamic Cast pour déterminer le type de fonction de la liste au moment de l'exécution. Veuillez noter que j'ai ajouté Shared_Ptr pour supprimer la fuite de mémoire dans l'échantillon d'origine. Peut-être que vous voulez jeter une exception si la méthode d'exécution est appelée avec les mauvais arguments (si le dynamic_cast donne 0).
Utilisation: p>
#include <functional> #include <map> #include <string> #include <memory> using namespace std; class Ignored; class BaseCommand { public: virtual ~BaseCommand() = 0 {}; }; template <class A1 = Ignored> class Command : public BaseCommand { typedef std::function<void(A1)> FuncType; FuncType f_; public: Command() {} Command(FuncType f) : f_(f) {} void operator()(const A1& a1) { if(f_) f_(a1); } }; template <> class Command<Ignored> : public BaseCommand { typedef std::function<void()> FuncType; FuncType f_; public: Command() {} Command(FuncType f) : f_(f) {} void operator()() { if(f_) f_(); } }; class CommandManager { typedef shared_ptr<BaseCommand> BaseCommandPtr; typedef map<string, BaseCommandPtr> FMap; public : template <class T> void add(string name, const T& cmd) { fmap1.insert(pair<string, BaseCommandPtr>(name, BaseCommandPtr(new T(cmd)))); } template <class A1> void execute(string name, const A1& a1) { typedef Command<A1> CommandType; FMap::const_iterator it = fmap1.find(name); if(it != fmap1.end()) { CommandType* c = dynamic_cast<CommandType*>(it->second.get()); if(c) { (*c)(a1); } } } void execute(string name) { typedef Command<> CommandType; FMap::const_iterator it = fmap1.find(name); if(it != fmap1.end()) { CommandType* c = dynamic_cast<CommandType*>(it->second.get()); if(c) { (*c)(); } } } private : FMap fmap1; };
En fait, je suis sous GCC 4.5 afin que je puisse utiliser des modèles variadiques; Il n'y aurait que quelques changements dans votre code, bien que
@Codablank Après avoir changé votre question, j'ai changé la réponse pour utiliser des modèles variadiques. Il modifie la nécessité d'avoir une classe pour chaque nombre d'arguments et la surcharge de la fonction d'exécution pour le nombre d'arguments -> Pour moi, il est beaucoup plus lisible avec le support de modèle variadique
Très bien! Belle base pour la mise en œuvre de la réflexion en C ++.
Qu'est-ce que vous essayez de faire n'est pas possible sans quelques travaux d'exécution sérieux et le coût associé. La solution la plus simple ne ferait bien sûr que stocker un boost :: n'importe quel < / a> ( Edit: Dans votre schéma actuel, je ne vois aucune raison pour Edit2: Vous déposez également le type de retour. Cela pourrait être ok pour votre cas d'utilisation mais rend cela beaucoup moins de générique. P> Edit3: J'ai élaboré un exemple de travail de votre code en utilisant comme pour l'exemple en utilisant une signature fixe. Pensez simplement à ce qui serait la représentation la plus naturelle d'une fonction que vous allez stocker (en regardant votre any_function code> ne s'est jamais rendu en boost) à l'intérieur de votre carte et faites les coulées nécessaires (ou ajoutez des données d'exécution qui vous indique qui moulant à faire), bien que vous puissiez éviter cela à tout prix et aller avec des arguments fixes ou aucun argument.
Vos utilisateurs peuvent ensuite modifier leurs fonctions à l'aide de
lidez code> pour correspondre à la signature souhaitée.
CommandManager Code> pour stocker
Commande * code> sur la carte. P>
tout code>. Je pense qu'il y a une faille et je ne vois vraiment pas ce que cela devrait atteindre mais ici, il va: p>
commande code>, je suppose que c'est
std :: fonction
BIND CODE> Quelle que soit la fonction qu'il souhaite utiliser, il correspond donc à cette signature. P> p>
Vos utilisateurs peuvent ensuite modifier leurs fonctions à l'aide de la liaison pour correspondre à la signature que vous avez besoin. I> Ouais, Nicolbolas m'a fourni le même conseil, mais je ne le vois pas, pourriez-vous me montrer du code?
@Codablank j'ai ajouté des charges de code pour votre exemple original et une explication.
@Codablank n'est pas aussi loin que je sache. J'ai vu deux implémentations différentes de ce genre de chose et boost :: Tout code> est le plus efficace. Quoi qu'il en soit, c'est votre seule option et si la vitesse est ce que vous êtes après, votre approche est certainement fausse et vous auriez dû l'avoir dit dans la question.
@PMR J'aime votre approche car elle stocke directement la fonction. Pourquoi avez-vous besoin de deux surcharges de la fonction d'appel? Avec un gabarit variadique, il pourrait être 0 à ~ arguments.
@David exactement, d'où le commentaire. Ceci est juste un exemple de SO OP sait comment la mettre en œuvre. Je pense qu'il serait également important d'empêcher les arguments de modèle de la fonction code> code> d'être déduits, car il peut entraîner des résultats inattendus si l'utilisateur ne comprend pas complètement le mécanisme de déduction.
Pourquoi ne pas simplement utiliser une liste de paramètres de jeu, et laissez l'utilisateur utiliser
std :: lid code> pour lier des paramètres supplémentaires au besoin?
C ++ n'a pas d'effacement de type. Les modèles de classe sont Modèles i> pour les classes (pas «classes génériques» comme dans Java ou C #). Lorsque vous instaniez un modèle de classe, les instances sont de nouvelles classes qui n'ont aucune relation entre elles (à l'exception de leur nom).
> Nicol Que voulez-vous dire par une liste de paramètres définis? Pourriez-vous fournir un exemple, je ne le vois pas
@CODABLANK: Je veux dire une liste fixe de paramètres. Toutes les fonctions prennent les mêmes paramètres. Et si quelqu'un veut utiliser une fonction qui prend d'autres paramètres, il doit utiliser
std :: lid code> pour effectuer les ajustements.
Plutôt que de jouer avec
std :: Fonction Code> Ce serait probablement une meilleure conception de la commande code> une classe abstraite (interface) et de créer deux sous-classes différentes pour deux types de commandes différents.
@Nicolbolas: OK mais cela signifie que les utilisateurs devront ajouter
Bind (& imprimer :: print1, & p, chaîne ("test1"), chaîne ("test2")) CODE> THELMO? Ou juste quelque chose comme
Bind (string ("test1"), chaîne ("test2")) code>?