Hallo,
ich würde meinen Code gern etwas event-driven haben, daher will ich
meinen Objekten gerne Methoden übergeben, dass sie sich zurück melden
können, wenn etwas passiert. Leider habe ich schon seit bestimmt 10
Jahren kein c++ mehr gemacht und benötige daher etwas hilfe. Kann mir
jemand sagen, wie folgender JavaScript-Code in c++ aussehen würde?
1
doSomething(obj){
2
consta=5;
3
obj.doAlsoSomething(()=>{// pass a function as parameter to another function
4
console.log(a);// prints 5
5
});
6
}
Soweit bin ich schon. Ich habe etwas gelsen dass man mit std:function
den Context mit übergeben kann, aber gibt es auch andere alternativen?
Ich will es auf dem Arduino laufen lassen und nicht unnötig platz mit
std verschwenden.
1
classDebouncedButton
2
{
3
private:
4
void(*onToggle)(int);// called, when the debounced state of the button changes
>obj.doAlsoSomething(()=>{// pass a function as parameter to another
4
>function
5
>console.log(a);// prints 5
6
>});
7
>}
8
>
1
template<typenameT>
2
voiddoSomething(T&obj){
3
constinta=5;
4
obj.doAlsoSomething([a](){
5
std::cout<<a<<std::endl;
6
});
7
}
> Soweit bin ich schon. Ich habe etwas gelsen dass man mit std:function> den Context mit übergeben kann, aber gibt es auch andere alternativen?
std::function<> ist im wesentlichen eine Abstraktion für etwas, dass
aufrufbar ist. Zusätzliche Parameter (Kontext) kannst Du mit std::bind()
an einen bestehendes aufrufbares Ding packen.
> Ich will es auf dem Arduino laufen lassen und nicht unnötig platz mit> std verschwenden.
Die Anforderungen sind zu schwamming und die Möglichkeiten, die C++
bieten zu groß, um da jetzt irgend eine Empfehlung draus zu machen.
mfg Torsten
Closures in einer Sprache, die Garbage Collection mit überladenen
Zuweisungsoperatoren simuliert? Hört sich so an, als hätte die C++
Fraktion eine neue Methode gefunden, sich selbst in den Fuss zu
schiessen.
Kannst du uns berichten, wie die Sache ausgegangen ist?
Für das Callback würde ich eine std::function nehmen. Als Callback
kannst du dieser dann entweder eine bestehende Funktion mit std::bind
oder ein lokales Lambda übergeben.
Ich meine für beidest musst du den Compiler mindestens auf C++11
stellen.
Jan schrieb:> Ich will es auf dem Arduino laufen lassen und nicht unnötig platz mit> std verschwenden.
Du initialisierst die Buttons in einer Schleife. Ich kann mir nicht
vorstellen, dass das so bleiben wird. Denn du wirst ja nicht
PerformState::BTN_COUNT Knöpfe haben, die alle das gleiche machen, oder?
Packe die Sache andersrum an: Schreibe erst mal die Initialisierungen
hintereinander hin, und erst wenn sich ein Muster ergibt, mache eine
Schleife daraus. Ich bin mir ziemlich sicher, dass sich kein
vernünftiges ergeben wird.
Insb. in einer Firmware wird es immer der Fall sein, dass absolute
Generizität nichts bringt, weil im konkreten Anwendungsfall die
angeschlossene Hardware fix ist. Du schreibst ja nicht eine Software,
die mal mit drei Buttons und ein andermal mit fünf Buttons funktionieren
muss. Du kannst also ruhig alle PerformState::BTN_COUNT Button-Treiber
(DebouncedButton-Instanzen) explizit hinschreiben. Sobald du das gemacht
hast, kannst du jeder einzelnen Instanz auch eine eigene Klasse geben.
Dann ergibt sich wieder ein Muster, das man diesmal mit Templates
erledigen kann. Und schon brauchst du kein std::function<> mehr:
1
template<classCB,intPIN>
2
classDebouncedButton
3
{
4
private:
5
CBonToggle;// called, when the debounced state of the button changes
Oje, jetzat weiss ich nicht mehr weiter, weil mir das Arduino-Framework
mit seinem bescheuerten setup()+loop()-Konzept in die Suppe spuckt: Man
muss die Button-Instanzen globale Variablen machen, und ob das in deinem
Fall möglich ist, kann ich durch meine Kristallkugel leider nicht sehen.
Aber vielleicht hab' ich dir ein paar Denkanstöße gegeben.
Das "this->" ist in C++ meist überflüssig.
Allerdings sind weder "new" noch "std::function" besonders gut für
Mikrocontroller geeignet, weil dynamischer Speicher hier meist zu viel
Overhead hat. Dazu kommt, dass der AVR-GCC und damit auch AVR-Arduino
keine C++-Standard-Bibliothek mitliefert, also auch kein std::function.
Daher ist die Lösung von tictactoe wohl die bessere. Ansonsten könnte
man auch ein klassisches Observer-Pattern mit virtuellen Funktionen
machen.
Allerdings:
tictactoe schrieb:> DebouncedButton(CB&& cb) : onToggle(std::forward<CB>(cb))
Das geht so leider nicht; dazu müsste "CB" ein template-Parameter des
Konstruktors sein:
Schamane schrieb:> Niklas G. schrieb:>> [&]>> Was bedeutet denn das?
Das ist die capture clause zum Lambda. [&] gibt an, dass der default,
mit dem der Codeblock des Lambdas auf andere lokale Variablen zugreift,
per Reference ist.
Im Beispiel oben, ist es überflüssig, da nur auf den Parameter X und die
globale Variable std::cout zugegriffen wird.
Wenn lambdas als Callback eingesetzt werden, will man in der Regel auch
nicht per Reference auf lokale Variablen zugreifen, weil die Lebenszeit
des Lambdas in dem Fall in der Regel länger ist, als die Funktion, in
dem das Lambda erzeug wurde.
> Mein C++ Wissensstand ist auf dem Stand von irgendwo Ende der 90er.
In den 20 Jahren ist sehr viel passiert.
Der Aufwand, die Lebenszeit der referenzierten Daten herauszufinden,
übersteigt den Nutzen.
Wir nehmen C++, weil es für jedes Problem eine passende Library gibt.
Nur leider hat jede Library eine andere Strategie zur Freigabe des
Speichers. Leider funktionieren Unit Tests nicht. In den einfachen Tests
haben dangling pointers keine Auswirkungen. Wir müssen die internen
Details von einem Dutzend verschiedener Container-Libraries beherrschen.
Mag ja sein, dass es wunderbar funktioniert, wenn wir uns auf die
modernen Konzepte beschränken. Aber wenn wir die Unmassen an bestehenden
Libraries und Frameworks nicht benutzen - warum nehmen wir dann C++?