Funktionszeiger



  • Hallo,
    ich stehe im Moment vor folgendem (wahrscheinlich sehr atypischen) Problem:

    Ich möchte gerne mit Funktionszeigern arbeiten, von denen ich nicht weiß, die die Argumentliste aussieht. Ist das in C++ überhaupt möglich?

    Der ungefähre Ablauf:

    1. Member-Variable auf eine Funktion mit unbekannter Argumentliste
    void (*funktionszeiger)(<Was kommt hier hin?>)
    
    1. Nacheinander Zuweisungen einer beliebigen konkreten Funktion.
    void add(int i, int j);
    void output(std::string s);
    
    funktionszeiger = &add;
    (*funktionszeiger)(1,2);
    [...]
    funktionszeiger = &output;
    (*funktionszeiger)("Test");
    

    Hierbei sollen diese Zuweisungen und die Aufrufe möglich sein.
    Ich hätte kein Problem damit, wenn ich die Aufrufe "(*funktionszeiger)(1,2)" in einen try-catch-Block packen muss, um Abstürze zu verhindern.

    Ich hoffe Ihr könnt mir helfen und versteht mein Problem.

    Gruß JoKl



  • JoKl schrieb:

    Ich möchte gerne mit Funktionszeigern arbeiten, von denen ich nicht weiß, die die Argumentliste aussieht. Ist das in C++ überhaupt möglich?

    Welchen Sinn hätte es solche Konstrukte zu verwenden?
    Ich gehe hier von ein Fehler im Design aus...

    Und Speicherzugriffsfehler etc. lösen keine Exception im C++ Sinne aus, Abstürze sind vorprogrammiert.

    cu André



  • Nein, das geht so nicht. Ein Funktionszeiger hat immer eine feste Signatur, d.h. er benoetigt eine feste Anzahl Parameter von einem bestimmten Typ.
    boost::function bietet etwas mehr Funktionalitaet (im Zusammenhang mit boost::bind), aber was du genau vor hast geht so nicht (ausser vllt ueber eine menge dreckiger casts ;D).

    Gruss,
    DeSoVoDaMu



  • asc schrieb:

    Welchen Sinn hätte es solche Konstrukte zu verwenden?
    Ich gehe hier von ein Fehler im Design aus...

    OK, der Sinn ist der folgende:
    Ich verwalte in einem Vector eine Liste von Funktionszeiger von Funktionen unterschiedlichen Typs. Jedem Funktionszeiger ist über einen Integer eine ID zugeordnet. Nun interpretiere ich eine Sequenz von Integern und nachfolgender Parameterliste und möchte gerne in Abhängigkeit von der ID die entsprechende Funktion mit den angegebenen Parametern aufzurufen.



  • JoKl schrieb:

    ...
    ...Jedem Funktionszeiger ist über einen Integer eine ID zugeordnet. Nun interpretiere ich eine Sequenz von Integern und nachfolgender Parameterliste und möchte gerne in Abhängigkeit von der ID die entsprechende Funktion mit den angegebenen Parametern aufzurufen.

    asc schrieb:

    ...Ich gehe hier von ein Fehler im Design aus...

    :p 😃
    Der Einsatz von "Typ-IDs" ("Wenn a dann mach dies - wenn b, dann mach das - ...") sollte einen immer stutzig machen ud nach anderen Lösungen suchen lassen.

    Wenn es unbedingt sein muss, kannst Du Letztlich natürlich immer void (*f)(void*) nehmen und je nach Funktion ein unschiedliche structs übergeben, die die jeweiligen Parameter bündeln ...

    Aber: "Typsicherheit ist Dein Freund !" - und zwar ein sehr mächtiger. Es wäre unklug, sich von ihr nicht helfen zu lassen.

    Gruß,

    Simon2.



  • JoKl schrieb:

    OK, der Sinn ist der folgende:
    Ich verwalte in einem Vector eine Liste von Funktionszeiger von Funktionen unterschiedlichen Typs. Jedem Funktionszeiger ist über einen Integer eine ID zugeordnet. Nun interpretiere ich eine Sequenz von Integern und nachfolgender Parameterliste und möchte gerne in Abhängigkeit von der ID die entsprechende Funktion mit den angegebenen Parametern aufzurufen.

    Zu welchem Zweck machst du das? Eventuell kann man dir Alternativen vorschlagen, wenn du uns das Kernproblem erklaerst, das du damit loesen willst.



  • [quote="JoKl Jedem Funktionszeiger ist über einen Integer eine ID zugeordnet.[/quote]

    dann codier doch da gleich die übergabe- und den rückgabeparameter mit rein.



  • Der Zweck ist der folgende:
    Es soll ein Zeileninterpreter geschrieben werden, der aus einer Textdatei ein Skript abarbeitet. Die Befehle in der Datei sehen so aus:

    Befehl1 12 abc 3.4
    Befehl2 acs
    Befehl3 100

    Ich möchte jetzt eine Klasse schreiben, der ich Zeiger auf Funktionen mit beliebiger Signatur übergeben möchte und zusammen mit dem Bezeichner in einem Vektor speichern. Der Vector sieht dann ungefähr so aus:
    (("Befehl1", ZeigerAufFunktionA), ("Befehl2", ZeigerAufFunktionB), ...)

    ZeigerAufFunktionA zeigt dann auf eine Funktion, die als Parameter einen int, einen String und einen double Wert erwartet.

    Jetzt möchte ich im Fall, dass der Bezeichner "Befehl1" ist, die Funktion "ZeigerAufFunktionA" mit Parametern 12, abc, 3.4 aufrufen.

    Ich bin über jeden anderen Ansatz dankbar



  • C++ und funktionspointer ist generell was, was sich beisst.

    Funktionspointer sind C ... und wenn du nich gerade eine C Schnittstelle bedienen musst (Callbacks ets), oder von der Performance zu ganz schmutzigen hacks gezwungen wirst, solltest Du auch keine funktionspointer verwenden.
    Der "Ersatz" fuer funktionspointer auf OOP Ebene sind virtuelle methoden. (ne vtable ist halt sowas wiene funktoinspointerliste).
    Also wenn du c++ willst, solltest du OOP korrekt mit Polymorphie arbeiten.

    Ciao ...



  • Was beist sich da? Nix. Wenn sich da was groß beißen würde, gäbe es keine dazu passende Funktionen im Standard ...



  • Ich würds in etwa wie folgt machen:

    1. Schreib eine Basisklasse für alle ausführbaren Befehle
    struct Executable {
      virtual void Execute() = 0;
      virtual std::istream& GetParams(std::istream&) = 0;
    };
    
    std::istream& operator>>(std::istream& is, Executable& ex) 
    {
      return ex.GetParams(is);
    }
    
    1. Schreib für jeden Befehl eine Abgeleitete Klasse, die die nötigen Parameter selbst aus einem String oder einem Stream holen kann.
    class Befehl1 : public Executable {
      int i_;
      std::string str_;
      float flt_;
    public:
      void Execute() {/*...*/}
      std::istream& GetParams(std::istream& is) 
      {
        is >> i_ >> str_ >> flt; return is;
      }   
    };
    
    1. Schreib zu den Klassen jeweils eine simple Erzeugerfunktion
    Executable* makeBefehl1()
    { return new Befehl1; }
    
    1. Schmeiß die Erzeugerfunktionen in eine map
    typedef Executable* (ExMaker*)();
    std::map<std::string, ExMaker> makerMap; 
    
    makerMap["Befehl1"] = &makeBefehl1;
    
    1. Für den Zeileninterpreter geht dann folgendes:
    int main()
    {
      std::ifstream scriptfile("script.txt");
      std::string befehlsname;
      while(scriptfile >> befehlsname) {
        ExMaker maker = makerMap[befehlsname];
        std::auto_ptr<Executable> pex(maker());
        scriptfile >> *pex; //oder pex->GetParams(scriptfile);
        pex->Execute();
      }
    }
    

    Der Code sucht sich die richtige Makerfunktion raus, die dir ein Befehlsobjekt des richtigen Typs erzeugt. Das Objekt besorgt sich dann die nachfolgenden Parameter aus dem Stream (es weiß ja selbst genau wie viele es braucht) und wird danach ausgeführt.

    Das alles ist nur als Skizze zu sehen, natürlich würde man die makerMap nicht als globlaes Objekt anlegen, Fehler werden auch nicht abgefangen usw.



  • Hey,
    danke für die ausführliche Antwort. Ich glaub, dass mir das weiterhelfen wird.

    Gruß
    jokl


Anmelden zum Antworten