Programmcode in Variable speichern/übergeben?



  • Sowas?

    void (*funcPtr)();
    class TestKlasse
    {
    public:
     int x;
     code Code;
    
    private:
     TestKlasse(int x, funcPtr)
     {
      if(x >= 5)
       funcPtr();
     }
    };
    
    void printFunc()
    {
        printf("größer als Fünf");
    }
    
    void whileFunc()
    {
        while(1) {
            printf("Test");
        }
    }
    
    main
    {
     TestKlasse tk1(7 , printFunc);
     TestKlasse tk2(2 , whileFunc);
    }
    


  • Mit einem C++0x-fähigen Compiler (C++0x ist der neueste C++ Standard), sieht das so aus:

    #include <functional>
    
    class TestKlasse
    {
    public:
     TestKlasse(int x, const std::function<void()>& code )
     {
      if(x >= 5)
       code();
     }
    };
    
    main
    {
     TestKlasse tk1(7 ,[](){printf("größer als Fünf")} );
     TestKlasse tk2(2 ,[](){while(1){printf("Test")}} );
     TestKlasse t3(0 , [](){if(x==2){Funktion();} } );
    }
    

    Siehe auch den Artikel zu C++0x im Magazin (da steht glaubich noch C++09).

    Ahja, GCC 4.3 bzw MSVC++ 2010 können das.



  • BTW: wenn man einen Compiler hat der keine Lambda-Expressions kann, kann man immer noch einiges mit Boost.Bind und Boost.Lambda machen. Ist zwar einigermassen hässlich und schwer verständlich, aber geht.



  • So schlimm ist das ja nun wirklich nicht, Fellhuhn. Nur weil der OP gruseligen Code postet heisst das ja nicht, dass es keine elegante Lösung dafür gibt.

    #include <map>
    #include <iostream>
    #include <boost/smart_ptr.hpp>
    
    // abstrakte Basisklasse für alle Actions
    class IAction
    {
    public:
       virtual ~IAction();
    
       // abgeleitete Klassen implentieren execute() mit sinnvollem Code
       virtual void execute() = 0;
    };
    
    // Typdefinition für shared_ptr, können auch TR1::shared_ptr sein
    typedef boost::shared_ptr<IAction> ActionPtr_t;
    
    // Konkrete Action Klasse 1
    class ActionOne : public IAction
    {
    public:
       static const unsigned int ID = 0;
    
       void execute()
       {
          // Verhalten von Action One
          std::cout << "Action one";
       }
    };
    
    // Konkrete Action Klasse 1
    class ActionTwo : public IAction
    {
    public:
       static const unsigned int ID = 1;
    
       void execute()
       {
          // Verhalten von Action Two
          std::cout << "Action two";
       }
    };
    
    // Action, die einfach nix tut
    class NullAction : public IAction
    {
    public:
       void execute()
       {
          // NullAction macht nix
       }
    };
    
    // Funktionen zum Erzeugen konkreter Action Objekte
    ActionPtr_t create_action_1()
    {
       // neues ActionOne Objekt in shared_ptr verkapseln und zurückgeben
       return ActionPtr_t( new ActionOne() );
    }
    
    ActionPtr_t create_action_2()
    {
       // neues ActionTwo Objekt in shared_ptr verkapseln und zurückgeben
       return ActionPtr_t( new ActionTwo() );
    }
    
    // Action Factory zum Erzeugen konkrete Action Objekte
    class ActionFactory
    {
       // Typdefinition für Funktionspointer
       typedef ActionPtr_t (*func)(void);
       typedef std::map<unsigned int, func> ActionCreatorMap_t;
    
       // Map mit Zuordnung ActionID -> Erzeugerfunktion
       ActionCreatorMap    CreatorMap_; 
    
    public:
       // Implementation als Singleton
       static const ActionFactory& instance()
       {
          // das einzige Factory Objekt
          static ActionFactory theFactory;
          return theFactory;
       }
    
       ActionPtr_t create( unsigned int ActionID ) const
       {
          ActionCreatorMap::const_iterator it = CreatorMap_.find( ActionID );
          if( CreatorMap_.end() != it )
          {
             // Eintrag für übergebene Funktion gefunden, Funktion erzeugt Action
             return it->second();
          }
          // kein Treffer für übergebene ID, NullAction zurückgeben
          return ActionPtr_t( new NullAction() ); 
       }
    
    private:
       ActionFactory()
       {
          // Erzeugerfunktionen unter ihrer ID in map eintragen
          CreatorMap_[ActionOne::ID] = create_action_1;
          CreatorMap_[ActionTwo::ID] = create_action_2;
       }
       // keine Kopien erlauben, CCtor und Assignment privat
       ActionFactory::ActionFactory( const ActionFactory& );
       ActionFactory& operator=( const ActionFactory& );
    };
    
    int main()
    {
       // Action 1, Action 2 und ungültige ID ausführen
       ActionFactory::instance().create( ActionOne::ID )->execute();
       ActionFactory::instance().create( ActionTwo::ID )->execute();
       ActionFactory::instance().create( 400 )->execute();
    }
    

    Code ist nicht getestet, dient lediglich zur Veranschaulichung der Idee



  • ... und ist häßlich, unübersichtlich, beschissen zu warten und umständlich. Meine bescheidene Meinung.



  • Fellhuhn schrieb:

    ... und ist häßlich, unübersichtlich, beschissen zu warten und umständlich. Meine bescheidene Meinung.

    Argumente? Oder nur subjektiv?



  • @DocShoe:
    Ich verstehe nicht, wieso du nicht gleich boost::function verwendest.
    Wozu das eigene Interface?
    Macht ja hier genau gar keinen Sinn.

    p.S.: vielleicht damit du eine Singleton-Klasse mit all ihren Problemen (z.B. mit vielen Compilern nicht threadsafe) einführen kannst, die man sonst nicht bräuchte?



  • Da muss ich allerdings gestehen, dass ich bisher noch nie was mit boost::function gemacht habe. Kann sein, dass ich da bisher noch nie drauf gestossen bin oder dass mein Compiler die lib nicht unterstützt.
    In meinen Projekten hatte ich bisher immer einen Kontext und etwas komplexere Action Objekte, die dann einen Kontext hatten, der im Konstruktor übergeben wurde und als member gehalten wurde. Bei freien Funktionen hätte man diesen Kontext dann immer mitschleppen müssen, daher habe ich mich für Interfaces entschieden.



  • Badestrand_ schrieb:

    Ahja, GCC 4.3 bzw MSVC++ 2010 können das.

    Woher hast du die Info das GCC 4.3 Lambda's kann?



  • @DocShoe:
    boost::function Objekte können auch State halten.

    Idealerweise vermeidet man das wenn es geht. Aber das hat natürlich nichts mit boost::function zu tun, sondern eher damit, dass stateless "immer" besser ist als "stateful". Gerade bei Funktoren.

    boost::function hat allerdings - wenn ich mich richtig erinnere - "value semantics", d.h. das darin verpackte Funktions-Objekt wird bei Zuweisung kopiert und nicht referenziert. Was man allerdings wieder schön mit shared_ptr umgehen kann.

    Nochwas: wenn man Templates verwenden kann, braucht man natürlich gar kein boost::function/std::function/was-auch-immer, sondern kann den Funktor einfach als beliebigen Typ übergeben:

    class Foo
    {
    public:
        template <class Functor>
        Foo(..., Functor functor)
        {
            ...
            functor(123,456);
        }
    };
    

    Spart wieder ein wenig Overhead ein.



  • David_pb schrieb:

    Badestrand_ schrieb:

    Ahja, GCC 4.3 bzw MSVC++ 2010 können das.

    Woher hast du die Info das GCC 4.3 Lambda's kann?

    Kann laut http://gcc.gnu.org/projects/cxx0x.html erst GCC 4.5


Anmelden zum Antworten