10
votes

C ++ 0x: Stocker tout type de std :: Fonction dans une carte STD :: Carte

J'essaie de stocker un ensemble de STD :: Fonction sur une carte (sous GCC 4.5)

J'aimerais avoir 2 types de choses: p>

  • stocker des fonctions avec des arguments déjà passés; alors tu as juste appeler f () li>
  • stocker des fonctions sans arguments; Ensuite, vous devez appeler f (...) li> ul>

    Je pense que j'ai atteint le premier avec une commande de classe et un gestionnaire: p> xxx pré>

    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;
        }
    


6 commentaires

Pourquoi ne pas simplement utiliser une liste de paramètres de jeu, et laissez l'utilisateur utiliser std :: lid 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 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 pour effectuer les ajustements.


Plutôt que de jouer avec std :: Fonction Ce serait probablement une meilleure conception de la commande 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")) THELMO? Ou juste quelque chose comme Bind (string ("test1"), chaîne ("test2")) ?


3 Réponses :


2
votes

Votre Constructeur de classe nécessite une fonction . Vous essayez de vous nourrir une fonction . Cela ne va pas à Tipecheck.

Si vous avez besoin de fonctions qui acceptent des arguments variables (comme printf ), vous aurez besoin de fonction <> et exécution () 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 .

Si vous avez juste besoin d'un nombre variable d'arguments de chaîne, utilisez des fonctions qui acceptent E.G. vecteurs de cordes.

Tout cela n'a rien à faire avec std :: map . Quoi que vous puissiez stocker dans une variable ancienne simple, vous pouvez stocker dans std :: map aussi.


0 commentaires

10
votes

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;
};


3 commentaires

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 ++.



4
votes

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> ( any_function 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 pour correspondre à la signature souhaitée.

Edit: Dans votre schéma actuel, je ne vois aucune raison pour CommandManager pour stocker Commande * sur la carte.

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.

Edit3: J'ai élaboré un exemple de travail de votre code en utilisant tout . Je pense qu'il y a une faille et je ne vois vraiment pas ce que cela devrait atteindre mais ici, il va: xxx

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 commande , je suppose que c'est std :: fonction . Stockez les fonctions de ce type et chaque fois que vos utilisateurs essaient de l'utiliser, il doit BIND Quelle que soit la fonction qu'il souhaite utiliser, il correspond donc à cette signature.


5 commentaires

Vos utilisateurs peuvent ensuite modifier leurs fonctions à l'aide de la liaison pour correspondre à la signature que vous avez besoin. 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 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 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.