Funktion als Rückgabetyp



  • redrew99 schrieb:

    Eine Funktion als Rückgabewert ist schon ziemlich abgefahren... 😃
    Fällt jmd. zufällig auch ein Beispiel ein an wo so etas Sinn macht?

    Die C-Standardfunktion signal.

    anti-freak schrieb:

    gutes tut

    Widerspruch in sich.



  • Auto, tut-tut!


  • Mod

    ichbineinnoob schrieb:

    Hey,

    dass man Funktionen in C++ per Parameter übergeben kann, durfte ich vor einiger Zeit schon erstaunt feststellen. Aber kann man Funktionen auch zurückgeben?

    Ich habe es so versucht, aber mein Compiler mag das nicht...

    (double (*fkt)(double, double)) get_fkt(int);
    

    LG Noob

    Eigentlich nicht Funktionen, sondern Zeiger oder Referenzen darauf. Interessanterweise macht der Compiler von sich aus Zeiger daraus, wenn man Funktion schreibt. Aber nur bei Parametern. Beim Rückgabetyp ist es schlicht nicht erlaubt.

    typedef double fkt(double,double);
    void do(fkt f); // äquivalent zu
    void do(fkt* f); // oder ausgeschrieben
    void do(double f(double,double)); // bzw.
    void do(double (*f)(double,double)); // wer will auch
    void do(fkt& f); // das kann aber nur mit Funktionen gefüttert werden,
    void do(double (&f)(double,double)); // Zeiger muss man erst explizit dereferenzieren, also nicht wirklich sinnvoll
    
    // aber
    fkt get_fkt(int); // Fehler!
    fkt* get_fkt(int); // oder auch
    fkt& get_fkt(int); // allerdings ohne semantische Vorteile
    //ausgeschrieben
    double (*get_fkt(int))(double,double); // oder
    double (&get_fkt(int))(double,double);
    

    Danke, K&R. Trotz dieser *hust* Eleganz ist es nicht möglich, Eine Funktion zu deklarieren, die einen Zeiger auf sich selbst zurückgibt (mit dem richtigen Typ).
    ach ja, dann geht auch noch C++11:

    auto get_fkt(int) -> fkt*;
    auto get_fkt(int) -> double (*)(double,double); // usw.
    

    etwas besser lesbar, erlaubt aber auch nicht mehr als die alte Syntax.

    auto get_fkt(int a) -> decltype(&get_fkt);
    

    wäre schön, geht aber nicht, weil Namen erst am Ende aller Deklaratoren bekannt sind, im decltype ist der Funktionsname noch unbekannt.



  • anti-freak schrieb:

    ok, passt nicht ganz, aber hat jemand irgendwo nen gutes tut für solche funktionsspielchen?
    habe auch schon davon gehört, und das wäre natürlich mega, wenn ich es selbst anwenden könnte 😛
    also ein tutorial hauptsächlich darüber, wie ich ne funktion übergebe.

    Das ist ein Einzeiler:
    Funktionen können weder als Parameter an eine Funktion übergeben werden, noch zurückgeliefert werden, da es den Datentyp function oder wie auch immer das genannt werden soll, nicht gibt. Lambdafunktionen des neuesten C++ Standards mögen etwas ähnliches sein, dazu kann ich aber nix sagen, da ich zu wenig darüber weiß.

    Na gut, waren jetzt doch mehr als eine Zeile.

    Als Ersatz können Zeiger auf Funktionen definiert werden. Das geht sehr einfach:

    1. Schritt: erstelle einen Prototypen:

    <type> <functionName>( <type> <param>, ... );
    Beispiel:

    int compare( const char *s1, const char *s2);
    

    2. Schritt: füge Klammern um den Funktionsnamen ein:
    <type> (<functionName>) ( <type> <param>, ... );
    Beispiel:

    int (compare)( const char *s1, const char *s2);
    

    3. Schritt: setze den Zeigeroperator vor dem Funktionsnamen:
    <type> (*<functionName>) ( <type> <param>, ... );
    Beispiel:

    int (*compare)( const char *s1, const char *s2);
    

    4. Schritt: gibt dem Zeiger 'nen anständigen Namen:
    <type> (*<pointerName>) ( <type> <param>, ... );
    Beispiel:

    int (*comparePtr)( const char *s1, const char *s2);
    

    Zum Befüllen des Zeigers schreibst Du einfach die Adresse einer Funktion mit dem passenden Prototypen rein. Die Adresse erhälst Du, in dem Du den Funktionsnamen ohne Parameterliste schreibst.

    Beispiel:

    comparePtr = compare;
    

    Zum Derefenezieren des Funktionszeigers schreibst Du den Namen des Zeigers mit Parameterliste:

    Beispiel:

    int result = comparePtr( "Hello", "World" );
    

    Wenn Du mehr als einen Zeiger auf so eine Funktion brauchst, ist es sinnvoll sich mit typedef einen Typ zu definieren:

    typedef int (*comparePtrType)( const char *s1, const char *s2);
    ...
    comparePtrType comparePtr = compare;
    

    Das sollte es sein.

    mfg Martin



  • mgaeckler schrieb:

    Funktionen können weder als Parameter an eine Funktion übergeben werden, noch zurückgeliefert werden, da es den Datentyp function oder wie auch immer das genannt werden soll, nicht gibt.

    Da du den neuen Standard erwähnt hast, was ist mit std::function?


  • Mod

    mgaeckler schrieb:

    Funktionen können weder als Parameter an eine Funktion übergeben werden, noch zurückgeliefert werden, da es den Datentyp function oder wie auch immer das genannt werden soll, nicht gibt.

    Das ist auf subtile Weise richtig. Es gibt Funktionstypen, aber Funktionstypen sind keine Objekttypen (oder Datentypen wenn du sie so nennen willst).



  • Fällt jmd. zufällig auch ein Beispiel ein an wo so etas Sinn macht?

    Unter reinem c++ wohl eher seltener, weil es fuer c++ bessere Mittel gibt (Interfaces / virtualitaet).
    bleibt C und C kompatiblitaet mit c++ ....

    bei C wird man aber wohl eher keinen funktionspointer als rueckgabewert waehlen, sondern eher den Fehlercode, der eigentliche rueckgabewert ist dann in den paramatern als zeiger auf ... modeliert.

    Ciao ...



  • set_new_handler Ist z.B. eine Funktion aus dem C++-Standard, die einen Funktionspointer zurückgibt. Gibt afaik noch ein paar andere...



  • set_unexpected und set_terminate fallen mir da ein.



  • redrew99 schrieb:

    Eine Funktion als Rückgabewert ist schon ziemlich abgefahren... 😃
    Fällt jmd. zufällig auch ein Beispiel ein an wo so etas Sinn macht?

    Mal so ein Vorschlag:

    qsort erwartet als einen der Parameter die Adresse einer Vergleichsfunktion. Dummerweise wird weder der Vergleichsfunktion noch qsort das Sortierkriterium mitgegeben. Daher wäre folgenden Szenario denkbar:

    typedef int (*comparePtrType)( const void *e1, const void *e2 );
    
    int compareNames( const void *e1, const void *e2 );
    int compareSizes( const void *e1, const void *e2 );
    int compareAges( const void *e1, const void *e2 );
    
    comparePtrType getCompareFunc( int criteria )
    {
       switch( criteria )
       {
          case 0:
             return compareNames;
          case 1:
             return compareSizes;
          case 2:
             return compareAges;
       }
    
       return NULL;
    }
    
    void sortData( void *data, size_t numElements, size_t dataSize, int criteria )
    {
       comparePtrType compareFunc = getCompareFunc( criteria );
       if( compareFunc )
          qsort( data, numElements, dataSize, comparefunc );
    }
    

    Hab's jetzt nicht übersetzt sondern nur so hingeschrieben. Tippfehler müsst Ihr daher selbst beheben.

    mfg Martin



  • mgaeckler schrieb:

    qsort erwartet als einen der Parameter die Adresse einer Vergleichsfunktion. [...]

    /* viel C-Code... */
    

    Macht man in C++ komplett anders. Templates statt void-Pointern, und das Vergleichskriterium hängt man dann auch eher in einen Functor.



  • pumuckl schrieb:

    mgaeckler schrieb:

    qsort erwartet als einen der Parameter die Adresse einer Vergleichsfunktion. [...]

    /* viel C-Code... */
    

    Macht man in C++ komplett anders. Templates statt void-Pointern, und das Vergleichskriterium hängt man dann auch eher in einen Functor.

    Klar, nur wollte jemand ein einigermassen sinnvolles Bespiel. Mir ist halt auf die schnelle nix beseres eingefallen.

    mfg Martin



  • mgaeckler schrieb:

    Zum Derefenezieren des Funktionszeigers schreibst Du den Namen des Zeigers mit Parameterliste:

    Beispiel:

    int result = comparePtr( "Hello", "World" );
    

    Nö. Funktionszeiger funktionieren genau wie normale Zeiger auch, bloß sind sie umständlich zu deklarieren.

    typedef vector<string> Function(int, double);
    Function*  func_ptr    = some_func; // einzig hier ist der Adressoperator optional
    Function&  func_ref    = *func_ptr;
    Function&& func_refref = std::move(func_ref); // klingt unlogisch, ist es auch
    

  • Mod

    314159265358979 schrieb:

    Function&& func_refref = std::move(func_ref); // klingt unlogisch, ist es auch
    

    Auf die Schnelle kann nicht finden, dass das verboten ist, scheint aber ein Defekt zu sein. Denn dieses func_refref kann weder dazu benutzt werden, einen Zeiger auf eine Funktion zu bilden, noch die Funktion selbst aufzurufen (beides setzt ein Funktions-lvalue voraus).
    Entweder müssen die entsprechenden Abschnitte in 4.3 und 5.2.2 geändert werden, oder dieses Konstrukt wird verboten oder als lvalue-Referenz intepretiert.
    Evtl. steht aber schon irgendwo etwas dazu.

    Funktion-rvalues entstehen traditionell nur in der Form gebundener Memberfunktionen, in

    struct { void foo(); } bar;
    bar.foo();
    

    ist

    bar.foo
    

    ein Funktions-rvalue (in C++03).



  • mgaeckler schrieb:

    Klar, nur wollte jemand ein einigermassen sinnvolles Bespiel.

    Eben - in C++ ist dein Beispiel nicht sinnvoll 😉



  • btw. ich muss hier mal einen Vorposter korrigieren:

    GetProcAddress gibt einen Funktionszeiger zurück, nämlich "void (*)(void)"
    oder wie Microsoft es per typedef nennt "FARPROC".

    Lediglich dlsym weist den Defekt auf, einen void* zurückzugeben.

    Btw. bevorzugte Nutzung von Funktionszeigern sind Callbacks.
    Das trifft aber hauptsächlich im Zusammenhang mit C-Bibliotheken auf,
    da man in C++ eben eher auf Templates und Funktoren setzt.
    Ausnahme stellen hier DLL's dar, da man Templates nicht ohne größere Probleme exportieren kann.



  • anti-freak schrieb:

    ok, passt nicht ganz, aber hat jemand irgendwo nen gutes tut für solche funktionsspielchen?

    http://www.newty.de/fpt/index.html



  • DrakoXP schrieb:

    Lediglich dlsym weist den Defekt auf, einen void* zurückzugeben.

    Das ist kein Defekt, das ist absicht. Es ist laut Standard undefiniertes Verhalten, einen Funktionszeiger in einern Funktionszeiger eines anderen Typs umzucasten.


  • Mod

    DrakoXP schrieb:

    Lediglich dlsym weist den Defekt auf, einen void* zurückzugeben.

    Das ist kein Defekt per se, weil nicht jedes Symbol auf eine Funktion verweist. Sauberer wäre nat. eine zweite Funktion speziell für Funktionssymbole.

    314159265358979 schrieb:

    Es ist laut Standard undefiniertes Verhalten, einen Funktionszeiger in einern Funktionszeiger eines anderen Typs umzucasten.

    Unfug.



  • DrakoXP schrieb:

    btw. ich muss hier mal einen Vorposter korrigieren:

    GetProcAddress gibt einen Funktionszeiger zurück, nämlich "void (*)(void)"
    oder wie Microsoft es per typedef nennt "FARPROC".

    Du hast Recht! *staun*
    Wurde das in den letzten 10 Jahren mal geändert? :xmas2:
    (Aber ne, vermutlich hab ich das nicht so in Erinnerung, weil es irgendwann früher mal so war, sondern weil ich es mir einfach eingebildet habe *g*)


Anmelden zum Antworten