| Autor |
Nachricht |
dundanox
Mitglied
Benutzerprofil
Anmeldungsdatum: 12.02.2011
Beiträge: 3
|
dundanox Mitglied
15:28:43 12.02.2011 Titel: |
Zeiger auf gebundene Funktion als Funktionsparameter |
Zitieren |
Hallo Leute, erst mal ein kleines Hallo an alle! Das hier ist mein erster Beitrag :-)
So, nun zum Problem:
Wie kann kann ich den Zeiger auf eine gebundene Funktion als Funktionsparameter übergeben?
Versuche ich das nämlich, so meckert Visual Studio 2010:
| Zitat: | | Ein Zeiger auf eine gebundene Funktion darf nur zum Aufrufen der Funktion verwendet werden. |
Was genau bedeutet es und wie kann ich es umgehen?
Das Ziel, welches ich damit verfolge, ist der Runge-Kutta-Methode eine Funktion aus einer Klasse zu übergeben. Diese Funktion berechnet die Ableitungen und RungeKutta damit dann den nächsten Punkt der Trajektorie.
Runge-Kutte-Methode:
| C++: | void rk4(int n, double *y, double *f, double x, double h, void (*derivs) (double,double [], double [])){
...
} | |
Klasse mit ODE-Funktion, in der die Ableitungen berechnet werden:
| C++: | class MyClass {
// parameter
void ODE(double x, double *y, double *f) {...}
}; | |
Nicht funktionierende Übergabe des Zeigers:
| C++: | MyClass a(...);
rk(n,y,f,x,h,(&a)->*ODE)
... | |
Hier ist es nun so, dass der Ausdruck "(&a)->*ODE" nicht akzeptiert wird und der obere Hinweis ausgegeben wird.
Meine Suche nach diesem Problem hat mich nicht wirklich weiter gebracht. Wenn ich es richtig verstanden habe, dann hat es etwas mit dem impliziten this-pointer zu tun. Eine Lösung konnte ich aber nicht finden. Ich hoffe meine Fragestellung ist klar formuliert. Wenn nicht, dann fragt bitte nach. |
|
|
|
 |
314159265358979
Mitglied
Benutzerprofil
Anmeldungsdatum: 09.03.2010
Beiträge: 4658
|
314159265358979 Mitglied
15:37:06 12.02.2011 Titel: |
|
Zitieren |
Das Problem ist, dass du versuchst eine Memberfunktion zu übergeben, allerdings sind Memberfunktionen verschieden von normalen, sie brauchen immer ein Objekt, mit dem sie aufgerufen werden können. Um dein Problem zu lösen gibt es jetzt folgende Möglichkeiten:
1) Du machst deine Funktion static. Statische Funktionen verhalten sich wie normale Funktionen, man kann sie direkt übergeben, nämlich mit der Syntax &Class::method.
2) Du benutzt Funktoren. Mit bind kannst du folgendes schreiben:
| C++: | 1 2 3 4 5 6 7 8 9 10 11 | class MyClass
{
void sayHello() { std::cout << "Hello World!" << std::endl;
};
int main()
{
MyClass obj;
std::function<void()> f = std::bind(&MyClass::sayHello, &obj);
f();
} | |
f ist jetzt ein Funktionsobjekt, das für dich die Methode sayHello() auf dem Objekt obj aufruft.
Generell würde ich anstatt Funktionszeigern immer Funktoren verwenden, da du so viel flexibler bist. Funktoren geistern irgendwo im tr1 rum. Da ich einen C++0x Compiler verwende, habe ich sie im Header <functional>. Boost bietet auch Funktoren sowie bind.
Übrigens haben die beiden Ausdrücke obj.*func und obj_ptr->*func keinen greifbaren Typ. Deshalb kannst du damit auch nichts anfangen. |
Zuletzt bearbeitet von 314159265358979 am 15:41:54 12.02.2011, insgesamt 4-mal bearbeitet |
|
 |
krümelkacker
Mitglied
Benutzerprofil
Anmeldungsdatum: 10.08.2010
Beiträge: 2232
|
krümelkacker Mitglied
16:40:23 12.02.2011 Titel: |
|
Zitieren |
Viele Wege führen nach Rom.
Beispiel: Generisch (Templates) + Lambdas
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | template<class Func>
void rk4(???, Func f)
{
...
??? f(???);
...
}
class konkretes_problem {
...
??? eval_f(???) const;
...
};
...
konkretes_problem kp;
rk4(???, [&kp](???){return kp.eval_f(???);} ); | |
Statt der Lambda-Funktion (welches noch nicht offizieller Bestandteil von C++ ist) kannst Du hier auch einen eigenen Funktor oder std::tr1::bind oder boost::bind benutzen.
Beispiel: "Laufzeit-Polymorphie":
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | class abstract_gradient_field
{
protected:
~abstract_gradient_field() {}
public:
virtual ??? evaluate(???) const = 0;
};
void rk4(???, abstract_gradient_field const& af)
{
...
??? af.evaluate(???);
...
}
class konkretes_problem : public abstract_gradient_field {
...
??? evaluate(???) const;
...
};
...
konkretes_problem kp;
rk4(???,kp); | |
Dies ist für Einsteiger vielleicht erstmla einfacher zu verstehen. Der generische Ansatz von oben ist aber mächtiger/flexibler und ggf performanter. |
|
|
|
 |
dundanox
Mitglied
Benutzerprofil
Anmeldungsdatum: 12.02.2011
Beiträge: 3
|
dundanox Mitglied
18:04:46 12.02.2011 Titel: |
|
Zitieren |
Vielen Dank für die schnellen Antworten. Das Problem habe ich aber noch nicht gelöst bekommen
1. Die Methode bzw. das Beispiel von 31415... (PI) mit dem std::bind hat wunderbar funktioniert. Wie mache es aber, wenn die Funktion (im Beispiel sayHello) Argumente hat. Also z.B.
| C++: | class MyClass
{
void sayHello(char *str) { std::cout << str << std::endl; }
}; | |
Eine Kurze Suche zu dieser Methode hat ergeben, dass man Platzhalter einführen muss. Wenn ich es richtig verstanden haben, dann müsste es so aussehen
| C++: | int main()
{
MyClass obj;
std::function<void()> f = std::bind(&MyClass::sayHello, &obj, _1);
f("Hallo");
} | |
Allerdings kann Visual Studio mit dem Platzhalter _1 bei mir nichts anfangen.
2. Lambda-Funktion. Bei JavaScript bin ich so etwas gewohnt. Wusste gar nicht, dass C++ das nun auch beherrscht. Der Compiler will aber nicht so richtig. Hier der (fehlerhafte) Code/Zeile:
| C++: | | rk4(4,nx,ny,t,dt,[&K](double t,double *x,double *y) {K.ODE(t,x,y);}); | |
und hier die Fehlermeldung:
| Zitat: | | error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""void __cdecl rk4<class `anonymous namespace'::<lambda0> >(int,double *,double *,double,double,class `anonymous namespace'::<lambda0>)" (??$rk4@V<lambda0>@?A0x2df2e2fb@@@@YAXHPAN0NNV<lambda0>@?A0x2df2e2fb@@@Z)" in Funktion "_main". |
Bei der Funktor-Geschichte bekomme ich den gleichen Fehler-Code (ähnlicher Text). Hier der Code:
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Funktor
{
private:
Kreisel& K;
public:
explicit Funktor(Kreisel& _K)
:K(_K)
{}
void operator()(double t, double *x, double *y)
{
K.ODE(t,x,y);
}
// in main()
rk4(4,nx,ny,t,dt,Funktor(K));
}; | | |
|
|
|
 |
314159265358979
Mitglied
Benutzerprofil
Anmeldungsdatum: 09.03.2010
Beiträge: 4658
|
314159265358979 Mitglied
18:11:52 12.02.2011 Titel: |
|
Zitieren |
Du musst dem function-Object natürlich auch sagen, dass es eine Funktion enthält, die einen char* nimmt
| C++: | int main()
{
MyClass obj;
std::function<void(char*)> f = std::bind(&MyClass::sayHello, &obj, _1);
f("Hallo");
} | | |
Zuletzt bearbeitet von 314159265358979 am 18:16:15 12.02.2011, insgesamt 1-mal bearbeitet |
|
 |
314159265358979
Mitglied
Benutzerprofil
Anmeldungsdatum: 09.03.2010
Beiträge: 4658
|
314159265358979 Mitglied
18:27:00 12.02.2011 Titel: |
|
Zitieren |
Ich habe noch einmal nachgesehen, folgendermaßen funktioniert es:
| C++: | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <functional>
#include <iostream>
struct MyClass
{
void say_something(const char* str) { std::cout << str << std::endl; }
};
int main()
{
MyClass obj;
std::function<void(const char*)> f = std::bind(&MyClass::say_something, &obj, std::placeholders::_1);
f("Hello World");
} | |
In boost reicht es, wenn man nur _1 schreibt, bei VC++ muss man std::placeholders::_1 schreiben. |
Zuletzt bearbeitet von 314159265358979 am 18:29:19 12.02.2011, insgesamt 2-mal bearbeitet |
|
 |
krümelkacker
Mitglied
Benutzerprofil
Anmeldungsdatum: 10.08.2010
Beiträge: 2232
|
krümelkacker Mitglied
19:08:48 12.02.2011 Titel: |
|
Zitieren |
Nur, dass das klar ist: std::bind, std::placeholders::_1, std::function, Lambdas gehören nur zum kommenden C++ Standard. Im aktuellen C++ Standard gibt es diese Dinger nicht. In TR1 gibt es bind und function, dort aber im Namensraum std::tr1, wenn ich mich richtig erinnere.
Die Linker-Fehlermeldung ist komisch. Ohne weiteren Kontext, kann ich damit nichts anfangen.
Ich schätze, man hat die Placeholder deswegen in einen eigenen Namensraum gepackt, damit man sich diesen per using-Direktive "reinholen" kann, also
| C++: | using namespate std::placeholders;
std::bind(dies,das,_1,_3,_2); | |
Ohne Boost, TR1, C++0x kann man immer noch die beiden Ansätze fahren, die ich vorgeschlagen hatte (wenn man das Lambda-Objekt durch einen eigenen Funktor ersetzt). |
|
|
|
 |
314159265358979
Mitglied
Benutzerprofil
Anmeldungsdatum: 09.03.2010
Beiträge: 4658
|
314159265358979 Mitglied
19:09:43 12.02.2011 Titel: |
|
Zitieren |
Natürlich ist das C++0x, aber wenn VC++ 10 das unterstützt, kann man es doch nutzen |
Zuletzt bearbeitet von 314159265358979 am 19:10:34 12.02.2011, insgesamt 1-mal bearbeitet |
|
 |
hustbaer
Mitglied
Benutzerprofil
Anmeldungsdatum: 27.10.2006
Beiträge: 16051
|
hustbaer Mitglied
01:59:36 13.02.2011 Titel: |
|
Zitieren |
Die Placeholder der Boost stecken IIRC auch in ihrem eigenen Namespace, bloss werden sie irgendwo in den Boost Headern schon per using reingeholt.
Wobei das IMO eh sehr fragwürdig ist. |
_________________ "Let there be Licht..." http://lichttools.sourceforge.net/
Sehr cooles ASCII Spiel (leider nicht von mir): ASCII-Scramble - http://www.roskakori.at/ascii/
|
|
 |
dundanox
Mitglied
Benutzerprofil
Anmeldungsdatum: 12.02.2011
Beiträge: 3
|
dundanox Mitglied
13:03:55 13.02.2011 Titel: |
|
Zitieren |
Vielen Dank für eure schnellen und hilfreichen Antworten!
Habe jetzt alle genannten "Wege nach Rom" ausprobiert und bin auch (fast) in Rom angekommen :-)
Die Alternative mit der Lambda-Funktion hat mir am besten gefallen. Vor allem weil ich deren Verwendung bei JavaScript gewohnt bin. Für die, die es genau wissen wollen, ich habe es so gemacht:
| C++: | | rk4(...,[this](double t,double *x,double *y) {ODE(t,x,y);}); | |
Wobei ich die Runge-Kutta-Methode rk4 ins cpp-file der Klasse kopiert habe und die Methode dann auch in einer Methode der Klasse aufrufen muss. Sonst kommt es bei mir zu dem letztens genannten Link-Fehler. Blicke bei dieser Link-Geschichte nämlich noch nicht so ganz durch. Schön wäre aber eine getrennte Lösung. Wie müsste ich denn die Klasse und die RK-Methode Verteilen/Verlinken damit ich sie dann auch im main() aufrufen kann? Wenn ich es so machen:
| C++: | #include "classKoerper.h" // der physikalische Körper mit der charakteristischen DGL (engl.: ODE)
#include "RungeKutta.h" // die allgemeine RK-Methode (benötigt ODE-Methode)
// später in der main()
Koerper K(...);
rk4(...,[&K](double t,double *x,double *y) {K.ODE(t,x,y);}); | |
dann funktioniert es nicht -> LINK-Fehler
Hier habe ich ansonsten noch eine kleine nette Zusammenfassung über die Lambda-Funktion mit einer Erklärung zur Syntax gefunden:
http://en.wikipedia.org/wiki/C%2B%2B0x#Lambda_functions_and_expressions |
Zuletzt bearbeitet von dundanox am 13:05:19 13.02.2011, insgesamt 1-mal bearbeitet |
|
 |
|
Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können Beiträge in dieses Forum schreiben. Sie können auf Beiträge in diesem Forum antworten. Sie können Ihre Beiträge in diesem Forum nicht bearbeiten. Sie können Ihre Beiträge in diesem Forum nicht löschen. Sie können an Umfragen in diesem Forum nicht mitmachen.
|
|
|
|
|