problem mit funktionszeiger



  • hallo zusammen,

    ich arbeite gerade an meinem ersten größeren c++-projekt und könnte hier etwas hilfe gebrauchen. und zwar möchte ich einer funktion eine andere memberfunktion als parameter übergeben.
    hier erstmal der code:

    float quad2d::gauss2d(float (*func)(float x, float y))
    {
    	nrfunc=func;
    	return gauss(f1);
    }
    
    float quad2d::f1(float x)
    {
    	xsav=x;
    	return gauss(&quad2d::f2);
    }
    
    float quad2d::f2(float y)
    {
    	return (*nrfunc)(xsav,y);
    }
    
    class quad2d : public quad {
    public:
    	...
    private:
    	...
    	float xsav;
    	float (*nrfunc)(float,float);
    	float f1(float), f2(float);
    };
    
    class quad {
    public:
    	float gauss (float (*)(float));
    protected:
    	...
    };
    

    das problem liegt beim aufrauf der gauss()-funktion. ich hab nu auf verschiedene arten versucht die funktion zu übergeben, zwei hab ich hier mal angegeben. ich bekomme dabei immer fehlermeldungen in der art:

    error C2664: 'quad::gauss': Konvertierung des Parameters 1 von 'float (__thiscall quad2d::* )(float)' in 'float (__cdecl *)(float)' nicht möglich
    1> Es gibt keinen Kontext, in dem diese Konvertierung möglich ist

    ich hab doch nun eigentlich jeweils einen funktionszeiger mir float-parameter und float-rückgabe. der einzige unterschied ist cdecl und thiscall, von denen ich nicht wirklich verstanden habe was sie bedeuten.
    wenn jmd hinweise zur aufklärung dieses falles hat, und dabei auch gleich etwas zu meinem verständnis zu objektorientierter programmierung beitragen könnte, wäre ich dafür sehr dankbar. 🙂

    gruß christian



  • Hi,

    ohne mir das genau angesehen zu haben: Weißt Du, dass Memberfunktionspointer etwas anderes sind als "reine" Funktionspointer (nicht kompatibel) ?

    Gruß,

    Simon2.



  • ja sowas ähnliches hab ich befürchtet. gibt es denn keine möglichkeit den zeiger zu konvertieren oder muss ich dann die aufrufende (gauss) funktion ändern?



  • Hi,

    Du kannst eine Wrapperfunktion drumrumstricken, aber insgesamt scheint mir, Du könntest virtual functions besser brauchen...
    Wer übergibt eigentlich Deinem quad-Objekt den Aufrufparameter ? Kannst Du mir mal den Aufruf zeigen ?

    Nur mal so ins Grobe geschrieben:

    class quad {
    public:
        float gauss (float);
    protected:
        virtual float f1(float) { // irgenwas quad-maessiges tun 
        }
        virtual float f2(float) { // irgenwas quad-maessiges tun 
        }
    };
    
    float quad::gauss (float f) {
       // do something
       f1(f);
       // do something
       return f2(f);
    }
    
    class quad2d : public quad {
    protected:
        // überladen die quad::f1 und quad::
        float f1(float) { // irgenwas quad2d-maessiges tun 
        }
        float f2(float) { // irgenwas quad2d-maessiges tun 
        }
    };
    
    int main() {
       quad q;
       q.gauss(1.2); // macht quadmaeßiges
    
       quad2d q2;
       q2.gauss(2.3); // macht quad2dmaeßiges
       return 0;
    }
    

    Gruß,

    Simon2.



  • hmm also wenn die Memberfunktionszeiger nur innerhalb deiner Klasse genutzt werden ... und die Memberfunktion in dieser Klasse ist ... also du bsw. eine Basisklasse hast mit einem std::vector voll Memberfunktion, die in einer bestimmten Situation aufgerufen werden sollen, und du davon dann eine Klasse ableitest und dem vector neue Funktionen der abgeleiteten Klasse hinzufügst ... geht das ... mitm this-Zeiger ... sonnst musst du einfach immer einen Zeiger auf ein Objekt der entsprechenden Klasse mitgeben ...



  • danke, dann werd ich mir die virtual-functions nochmal genauer angucken.
    ich weiß nicht ganz was du mit den aufrufparametern meinst. das objekt soll ne numerische integration durchführen. die grenzen übergeb ich über den konstruktor und die zu integrierende funktion eben über den zeiger. Z.B. so:

    float funktion (float x){ return x; }
    
    int main ()
    {
    	float a=0, b=1, result;
    	int n=10;
    	quad integrand(a,b,n);
    
    	result=integrand.gauss(funktion);
    
    	cout << result;
    
    	return 0;
    }
    

    das original für die funktion hab ich in c-code vorliegen und da klappt es auch, hab ich schon probiert. ich wollts nun nur nochmal zu übungszwecken und der vollständigkeit halber in c++ übersetzen.



  • ich kapier nicht ganz was du willst. was du in deinem beispielcode jetzt gezeigt hast sind normale Funktionszeiger. => typedef float (*fnGauss)(const float);



  • Ah !!

    OK, jetzt weiß ich, worum es Dir geht !
    Du gibst von außen eine (Art Callback-)Funktion mit, die von quad genutzt werden soll.
    Braucht quad2d nun 2 Funktionen von außen und Du hofftest, mit dem Erben von quad auf Wiederverwendung ? Das scheint mir nicht besonders glücklich, weil offensichtlich für quad2d auch nicht gauss(), sondern gauss2d() aufgerufen werden sollen.

    Brauchen quad(2d) überhaupt einen eigenen Status ? Mir scheint das ganze Gebiet eher wenig für einen OO-Umsetzung geeignet und denke, dass Deine bisherige Version gar nicht so schlecht ist. Aber Du könntest mit Funktionsoverloading arbeiten (wenn Du ein C++-Feature dabeihaben willst):

    float gauss(float, float, int, float (*func)(float x)) { // 1-D-Implementierung }
    float gauss(float, float, int, float (*func)(float x, float y))  { // 2-D-Implementierung }
    
    float funktion(float x){ return x; }
    float funktion2d(float x, float y ){ return x*y; }
    
    int main ()
    {
    	float a=0, b=1, result;
    	int n=10;
    
    	result=gauss(a,b,n, funktion); // ruft 1-D-Variante auf
    	result=gauss(a,b,n, funktion2d); // ruft 2-D-Variante auf
    
    	cout << result;
    
    	return 0;
    }
    

    Ein wenig "Eye-Candy" kannst Du sonst noch über operator-Overloading einbauen:

    struct gauss {
       float operator()(float, float, int, float (*func)(float x)) { // 1-D-Implementierung }
       float operator()(float, float, int, float (*func)(float x, float y))  { // 2-D-Implementierung }
    };
    
    float funktion(float x){ cout << "funktion\n"; return x; }
    float funktion2d(float x, float y ){ cout << "funktion2d\n"; return x+y; }
    
    int main (int argc, char **argv)
    {
        float a=0, b=1, result;
        int n=10;
        gauss g;
    
        result=g(a,b,n, funktion); // ruft 1-D-Variante auf
        cout << result << "\n";
        result=g(a,b,n, funktion2d); // ruft 2-D-Variante auf
    
        cout << result << "\n";
        return 0;
    }
    

    ... aber wirklich weiter bringt es auch nicht (sieht aber ganz lustig aus und ist "... echt C++ig, Mann !" :D).

    Gruß,

    Simon2.



  • boost::function
    boost::bind



  • joa mein ziel war halt das ganze möglichst c++ig zu machen 😃 . da muss natürlich auch vererbung mit rein, egal obs sinn macht oder nicht. aber man kann sichs wohl auch selber schwer machen 🙄
    ich habs nun einfach erstmal so gelöst, dass ich die rekursiven funktionsaufrufe mit den problematischen memberzeigern (auf f1 und f2) durch ne for-schleife ersetzt habe. klappt genauso gut, auch wenns nicht ganz so toll aussieht.
    trotzdem danke für die hilfe, hat mich wieder etwas tiefer in die OOP (bzw deren sinnvolle nutzung 😉 ) gebracht.

    gruß christian



  • djang0 schrieb:

    ...hat mich wieder etwas tiefer in die OOP ...gebracht.

    gruß christian

    Naja, wohl eher nicht. 😉
    In C++ kann man eben nicht nur OO programmieren, sondern hat auch einiges in der Hand zum generischen Programmieren - und das ist bisweilen wichtiger/übersichtlicher/besser geeignet für das jeweilige Problem.
    Wenn Du OOP ausprobieren willst, solltest Du Dir auch eine Aufgabe suchen, die diesen Einsatz anbietet.

    Gruß,

    Simon2.


Anmelden zum Antworten