Arduino Forum

International => Deutsch => Topic started by: combie on Feb 07, 2019, 04:53 pm

Title: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Feb 07, 2019, 04:53 pm
Schönen guten Tag.

Endlich habe ich auch mal wieder ein Problem, dessen Lösung/Ursache sich mir entzieht.


Erstmal, wozu es dienen soll:
Es ist die Xte Variante meiner Multitasking Makros.
Dieses mal ganz im OOP Gewand.


Seit Monaten läuft es stabil.
In mehren Anwendungen.

Für das nächste Projekt musste die Erzeugung von Tasks dynamisch erfolgen. Auf Grund des hohen Speicher/RAM Bedarfs einzelner Aufgaben.

Und da klemmt es jetzt.


Meine Task Klasse hat 3 virtuelle Methoden:
1. begin()
2. loop()
3. destructor

Mit Constructor, Destructor, loop() ist alles ok.
Bisher immer.

Das Kind, welches Husten hat, ist begin()


Grundsätzlich ist der Aufbau jetzt:
Jede konstruierte Task, trägt sich mit Scheduler::add(Task*) in die Liste ein.
> Scheduler::instance().begin();
Ruft die begin() Methoden aller von Task abgeleiteten Objekte  auf.
Perfekt!

Scheduler::begin(); setzt auch eine interne Variable beginDone.
Klappt perfekt.

Scheduler::add(Task*) prüft ob beginDone schon gesetzt ist, und wenn ja ruft es begin() der jeweiligen Task auf.
Und das geht eben schief.
Es wird dummer Weise die Methode von Task aufgerufen, und nicht die Methode begin() der abgeleiteten Klasse.
In dem konkreten Beispiel äußert es sich dann so, dass pinMode() nicht aufgerufen wird.



Code: [Select]
void setup() // funktioniert
{
  new BlinkTask(13,500);
  Scheduler::instance().begin();
}


Code: [Select]
void setup() // versagt
{
  Scheduler::instance().begin();
  new BlinkTask(13,500);
}


Es ist also so, dass alle Tasks, welche vor dem Scheduler::begin() Lauf erzeugt werden, perfekt funktionieren. Nur die später Erzeugten, da versagt der Task::begin() Aufruf.
Task::loop() Aufrufe haben kein Problem.


Es wäre schön, wenn mir einer sagt, was ich da falsch mache.

Auch eine alternative Variante, wie man solche Tasks dynamisch verketten kann, wäre genehm.
Wobei Singleton, List und ListNode auch schon mehrfach im Einsatz sind und andernorts wie erwartet  arbeiten.




EDIT:
Die korrigierte/aktuelle Lib findet sich in diesem Beitrag (https://forum.arduino.cc/index.php?topic=596279.msg4051105#msg4051105)
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Tommy56 on Feb 07, 2019, 05:04 pm
Hm, so wie ich das jetzt verstehe, ruft Scheduler::instance().begin(); die vorhandenen begin()-Methoden auf. Etwas anderes hat er ja nicht.
Ich denke, wenn Du später neue Tasks hinzufügst, müsste add diese Aufgabe mit erledigen, denn add erhält ja eine gültige Instance und kennt deren begin()-Methode.

Gruß Tommy
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Feb 07, 2019, 05:08 pm
Ja, so ist mein Vorhaben!
Genau das war mein Plan, an dem ich versage.

Auszug:
Code: [Select]
    void add(Task *task)
    {
      list.add(task);
      if(beginDone) task->begin(); // hier wird das falsche begin() aufgerufen
    }

Das ist dem Scheduler seine add() Methode.

Das addieren zur Liste klappt, der begin() Aufruf versagt.



Code: [Select]
  void begin()
  {
     Task *temp = (Task *)list.getFirst();
     if(beginDone) return;
     while(temp)
     {
       temp->begin();
       temp = (Task *)list.getNext(temp);
     }
     beginDone = true;
  }

Das ist dem Scheduler seine begin() Methode.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Tommy56 on Feb 07, 2019, 05:21 pm
Wenn Du einen neuen Task hinzufügst, kannst Du doch davon ausgehen, dass dessen begin() noch nicht aufgerufen worden ist und das if(beginDone) weg lassen.

Wenn das nicht garantiert ist, dann sollte beginDone eine Instanzvariable von task sein, die von dessen begin() auf true gesetzt wird. (als "begin() ist schon erledigt")
Also dann etwa so:
Code: [Select]

    void add(Task *task)
    {
      list.add(task);
      if(!task->isBeginDone()) task->begin();
    }



Alles aber nur ins Unreine gedacht, nicht getestet.

Gruß Tommy
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Whandall on Feb 07, 2019, 05:22 pm
Vielleicht ist Task zu niedrig in der Virtualisierungskette,
hast du mal einen cast auf Runable versucht?
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Feb 07, 2019, 05:31 pm
Quote
hast du mal einen cast auf Runable versucht?
Ich habe Runable erst  später da raus destilliert.
Als der Fehler schon persistierte.

Und ja, gerade mal schnell getestet:
Code: [Select]
   void add(Task *task)
    {
      list.add(task);
      if(beginDone) ((Runable*)task)->begin();
    }
 

Gleiches Versagen.
Es wird nicht BlinkTask::begin() aufgerufen, sondern Task::begin()
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Whandall on Feb 07, 2019, 05:34 pm
Was ist überhaupt die abgeleitete Klasse?
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Feb 07, 2019, 05:40 pm
Quote
Wenn das nicht garantiert ist, dann sollte beginDone eine Instanzvariable von task sein, die von dessen begin() auf true gesetzt wird. (als "begin() ist schon erledigt")
beginDone wird korrekt ausgewertet und begin wird auch aufgerufen, nur das falsche.

Ich habe die beginDone  Abfrage auch schon in den Konstruktor von Task verfrachtet.
Und darin dann Task::begin() aufgerufen.
Ebenso das gleiche Ergebnis/versagen.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Feb 07, 2019, 05:42 pm
Was ist überhaupt die abgeleitete Klasse?
Die findet sich in den Beispielen.
Da nicht zur Lib gehörig, sondern zur Applikation

Hier in vollständig:
Code: [Select]

#pragma once

#include <CooperativeTask.h>


class BlinkTask: public Task
{
  protected:
    const byte pin;
    const unsigned long interval;
 
  public:
    BlinkTask(const byte pin,const unsigned long interval):
       pin(pin),
       interval(interval)
    {
     
    }
   
    virtual void begin()
    {
      Serial.print("BlinkTask begin, pin: "); Serial.println(pin);
      pinMode(pin,OUTPUT);
    }
   
    virtual void loop()
    {
      TaskBlock
      {
        digitalWrite(pin,HIGH);
        taskPause(interval);
        digitalWrite(pin,LOW);
        taskPause(interval);
      }
    }
   

    virtual ~BlinkTask()
    {
      digitalWrite(pin,LOW);
      pinMode(pin,INPUT);
    }

};
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Whandall on Feb 07, 2019, 05:54 pm
Ich denke dein Problem ist, dass du im Konstruktor von Task das add machst.
Zu dem Zeitpunkt gibt es den Blinker noch nicht.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Feb 07, 2019, 06:11 pm
Durchaus möglich!
Hört sich plausibel an.
Die Konstruktorkette hat Task() schon abgearbeitet und BlinkTask() noch nicht, darum ist die virtuelle Methodentabelle noch nicht auf dem letzten Stand.

Aber ich habe mir schon das Hirn zermartert: Wie anders machen?

Code: [Select]
(new BlinkTask(13,500))->begin();
Wäre eine Möglichkeit, die funktioniert.
Aber schön, ist das nicht.
Ich würde das lieber einer Automatik überlassen.

Wenn es nicht anders geht, dann allerdings so!
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Feb 07, 2019, 07:14 pm
So:
Wenn es nicht anders geht:

Hauptdatei:
Code: [Select]


#include <CooperativeTask.h>
#include "BlinkTask.h"
#include "MasterTask.h"


 

BlinkTask Blinker[]{// {pin,interval}
                       {12,100},
                       {11,333},
                       {10,777},
                   };     

MasterTask master {13,500,10000};

void setup()
{
  Scheduler::instance().begin();
}

void loop()
{
  Scheduler::instance().loop();
}



MasterTask, welche regelmäßig eine Slave Task erzeugt und wieder löscht:
Code: [Select]
#pragma once

#include <CooperativeTask.h>


class MasterTask: public Task
{
  protected:
    const byte pin;
    const unsigned long blinkInterval;
    const unsigned long slaveInterval;
    Task * slave;
 
  public:
   MasterTask(const byte pin,const unsigned long blinkInterval,const unsigned long slaveInterval):
       pin(pin),
       blinkInterval(blinkInterval),
       slaveInterval(slaveInterval),
       slave(nullptr)
    {
     
    }
   
   
    virtual void loop()
    {
      TaskBlock
      {
        slave = new BlinkTask(pin,blinkInterval);
        slave->begin();
        taskPause(slaveInterval);
       
        delete slave;
        taskPause(slaveInterval);
      }
    }
   

    virtual ~MasterTask()
    {
      delete slave;
    }

};



Die Lib im jetzigen Zustand, im Anhang

Naja, so geht es.
Aber wirklich glücklich macht mich das noch nicht.
Eine Automatik steht weiterhin ganz oben auf der Wunschliste.

Und damit wären wir beim nächsten Punkt:
Ist sonst noch was verbesserungswürdig?
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Tommy56 on Feb 07, 2019, 10:01 pm
Nur für mich zum Mitdenken: Was spricht gegen meinen Vorschlag den Aufruf des task->begin() in der add()-Methode zu erledigen?

Gruß Tommy
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Feb 07, 2019, 10:24 pm
Quote
Was spricht gegen meinen Vorschlag den Aufruf des task->begin() in der add()-Methode zu erledigen?
Dagegen spricht: Dass es genau das ist, was ich getan habe!
Siehe #2, da siehst du die add Methode.

Und dass es nicht funktioniert.
In dem Augenblick scheint dieses zu gelten:
Quote
Die Konstruktorkette hat Task() schon abgearbeitet und BlinkTask() noch nicht, darum ist die virtuelle Methodentabelle noch nicht auf dem letzten Stand.
Die Konstruktoren einer Vererbungskette werden in der Vererbungsreihenfolge aufgerufen.
Wie du siehst, befindet sich der add() Aufruf in der Mitte, dann ist BlinkTask noch nicht fertig konstruiert.
Darum ist die virtuelle Methodentabelle noch nicht vollständig.







Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Feb 08, 2019, 03:09 pm
So...

Für die Testrunde, obs noch so läuft, wie gedacht, habe ich mal einen kleinen Benchmark gebastelt.

Ergebnisse, nur mit dieser (weiter unten gezeigten) Zähltask, ohne Beiwerk.
Alle Ergebnisse leicht schwankend.
Hier so ca die Mittelwerte
Es wird gezählt, wie oft der Scheduler seine Task Liste abklappert.
Wie oft jede Task (hier nur die eine) an die Reihe kommt.

Code: [Select]

Arduino UNO:       124100 per s
Arduino Mega:      102540 per s
Arduino DUE:       280100 per s

Blue Pill (STM32): 614000 per s


ESP8266 (80MHz):   102000 per s
ESP8266 (160MHz):  200000 per s

ESP32S :           471000 per s


Die UNO Nachbauten von Inhaos mit MD-328D Prozessor waren nicht testbar, da die Core Dateien nicht alle nötigen Features zur Verfügung stellen.

Ja, ich weiß, nicht sonderlich Aussagekräftig.
Aber dennoch ist zu sehen, dass es mit einigen µC Typen läuft.
Mich verblüfft der ESP8266 etwas, da hätte ich mehr erwartet.

Abschätzung:
Wenn bei 1 Task 100000 Durchläufe pro Sekunde zu schaffen sind, dann sind bei 4 Tasks immer noch 25000 Durchläufe pro Sekunde drin.

Ohne getestet zu haben, erwarte ich, dass ein 8MHz AVR Arduino die halbe Leistung eines UNO zeigt.
Und dass ein Mega2560 genau so schnell, wie ein UNO ist.


TestProgramm.ino:
Code: [Select]
/**
 * Ein Scheduler loop counter
 *
 *
*/

#include <Streaming.h>
#include <CooperativeTask.h>
#include "SchedulerLoopCountTask.h"

Scheduler &scheduler  = Scheduler::instance();



void setup()
{
 Serial.begin(9600);
 Serial.println("Start");
 
 scheduler.begin();
}

void loop()
{
 scheduler.loop();
}




SchedulerLoopCountTask.h
Code: [Select]

#pragma once

#include <CooperativeTask.h>
#include <Streaming.h>


class SchedulerLoopCountTask: public Task
{
  protected:
    const unsigned long interval;
    unsigned long count;
  
  public:
    SchedulerLoopCountTask(const unsigned long interval):
       interval(interval),
       count(0){}
    
    virtual void loop()
    {
      count++;
      TaskBlock
      {
        taskPause(interval);
        Serial << F("Scheduler loops: ") << count << F(" per ") << interval << F("ms") << endl;
        count = 0;
      }
    }

} schedulerLoopCountTask(1000);




Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Doc_Arduino on Feb 08, 2019, 03:28 pm
Hallo,

Arduino Mega2560

Code: [Select]

Start
Scheduler loops: 79540 per 1000ms
Scheduler loops: 79477 per 1000ms
Scheduler loops: 79395 per 1000ms
Scheduler loops: 79477 per 1000ms
Scheduler loops: 79396 per 1000ms
Scheduler loops: 79477 per 1000ms
Scheduler loops: 79396 per 1000ms
Scheduler loops: 79477 per 1000ms
Scheduler loops: 79477 per 1000ms
Scheduler loops: 79395 per 1000ms
Scheduler loops: 79478 per 1000ms
Scheduler loops: 79395 per 1000ms
Scheduler loops: 79477 per 1000ms
...

Quote
In file included from C:\Users\Worker\Documents\Arduino\WorkSpace_ATmega2560\Forums_Sketche\combieSchedulerLoopTest\combieSchedulerLoopTest.ino:6:0:

C:\Users\Worker\Documents\Arduino\libraries\Streaming\src/Streaming.h:102:52: warning: unused parameter 'arg' [-Wunused-parameter]

 inline Print &operator <<(Print &obj, _EndLineCode arg)

                                                    ^

Der Sketch verwendet 3582 Bytes (1%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
Globale Variablen verwenden 266 Bytes (3%) des dynamischen Speichers, 7926 Bytes für lokale Variablen verbleiben.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Feb 08, 2019, 04:48 pm
Danke für den Test!

Bei mir, mit einem Mega:
Code: [Select]

Start
Scheduler loops: 102540 per 1000ms
Scheduler loops: 102558 per 1000ms


OK...
Es ergibt sich also ein Unterschied.
124 Tausend zu 102 Tausend
Wo der her kommt ist mir dann erstmal ein Rätsel...
(Oder vielleicht auch nicht!)

Der Flash Adressbus des UNO ist 12Bit breit und der des Mega 18(?)Bit.
Das führt dazu, das alle Befehle, welche den PC (ProgrammCounter) manipulieren z.B. Call und Ret 50% mehr Daten schaufeln müssen, und damit länger brauchen.
Zusätzlich nutzt der GCC beim Mega eine Trampoline Section. Allerdings erst für Programmbereiche über 128kByte. Das dürfte hier nicht in Betracht kommen.

Mein Testcode im Anhang.
Die Unterschiede zwischen deinem Mega und meinem Mega können darin begründet liegen, dass ich die Lib noch mal, nach den Reparaturmaßnahmen, ordentlich gefegt habe.
Allen Testcode entsorgt.
Da wo es schnellere Verfahren gab, die schnelleren gewählt.
Merker Variablen und ihre Abhandlungen, reduziert

Auch verwende ich vermutlich eine leicht andere Boarddefinition/Kompiler
Mein Boardverwalter sagt: 1.6.209
Bei dir eher 1.6.23, oder so.




Quote
inline Print &operator <<(Print &obj, _EndLineCode arg)
Ja, das ist eine kleine Schlampigkeit des Streaming Programmierers.

Ersetze in der Lib
> inline Print &operator <<(Print &obj, _EndLineCode arg)
durch
> inline Print &operator <<(Print &obj, _EndLineCode)
Dann ist die Meldung weg.





Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Doc_Arduino on Feb 09, 2019, 12:18 pm
Hallo,

Danke für den Hinweis mit der Streaming Lib.

"Board Version" 1.6.23
IDE 1.8.8
Mega2560

So, neuer Test. Ca. 7% mehr Durchsatz.  :)


Code: [Select]
Start
Scheduler loops: 85069 per 1000ms
Scheduler loops: 85002 per 1000ms
Scheduler loops: 84916 per 1000ms
Scheduler loops: 85002 per 1000ms
Scheduler loops: 84915 per 1000ms
Scheduler loops: 85002 per 1000ms
Scheduler loops: 84916 per 1000ms
Scheduler loops: 85002 per 1000ms
Scheduler loops: 85002 per 1000ms
Scheduler loops: 84915 per 1000ms
Scheduler loops: 85003 per 1000ms
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Feb 09, 2019, 02:13 pm
Immerhin!

85000 Durchläufe pro Sekunde ist doch schon etwas, womit man leben kann.

IDE 1.8.8 mit 1.6.23 sagt bei mir auch ca 85000 Durchläufe *korrigiert*



Am Rande:
Quote
"Board Version" 1.6.23
Wenn du unter Datei-Voreinstellungen
Diese http://downloads.arduino.cc/packages/package_avr_7.3.0_index.json als zusätzliche URL beim Boardverwalter einträgst...

Dann ist das Update (im Boardverwalter) auf 1.6.209 möglich.
Damit sollte dann auch C++14 vollständig und C++17 fast komplett zur Verfügung stehen
Selbst Teile von C++2y sind nutzbar
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Doc_Arduino on Feb 09, 2019, 04:57 pm
Hallo,

Code: [Select]
Start
Scheduler loops: 102643 per 1000ms
Scheduler loops: 102558 per 1000ms
Scheduler loops: 102453 per 1000ms
Scheduler loops: 102558 per 1000ms
Scheduler loops: 102453 per 1000ms


Irre. :)  Danke für den Tipp mit dem neuen avr-gcc.
Denn diese Methode von hier   http://blog.zakkemble.net/avr-gcc-builds/ (http://blog.zakkemble.net/avr-gcc-builds/)  funktioniert nicht.  Egal ob 8.2 oder 7.3
Das Paket funktioniert nur unter Atmel Studio.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Jul 04, 2019, 11:52 pm
Irre. :)  Danke für den Tipp mit dem neuen avr-gcc.
Denn diese Methode von hier   http://blog.zakkemble.net/avr-gcc-builds/ (http://blog.zakkemble.net/avr-gcc-builds/)  funktioniert nicht.  Egal ob 8.2 oder 7.3
Das Paket funktioniert nur unter Atmel Studio.
Mittlerweile habe ich die Version 9.1 von genau der Seite bei mir am laufen.
Falls du (oder auch wer anders) interessiert bist, könnte ich mal eine Installationsanleitung basteln.
Denn die Anleitung auf der Seite funktioniert (bei mir) wirklich nicht.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Doc_Arduino on Jul 05, 2019, 08:10 am
Hallo,

die Anleitung auf der Seite für die Arduino IDE hatte ich auch schon probiert. Im Thread steht dann was das es nur mit der 7er funktionieren soll. Aber auch das wollte bei mir nicht laufen. Irgendwann hast du dann den Tipp mit dem Update in Arduino IDE gegeben. Das läuft ohne Probleme.

Mein Atmel Studio 7 läuft schon eine Weile mit gcc 9.1 von der Seite. Ich muss nur immer eine alte Dateiversion der avr-size.exe im ersten bin Ordner mitschleppen. Sonst zeigt er nach Kompilierung immer falsche Codegrößen an bzw. gar keine.

Falls es dir keine Umstände macht würde ich deine Anleitung ausprobieren, dann wären bei mir beide gleichgezogen. Wenn getestet kannste das bei ihm ins Forum schreiben.   :)
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Jul 05, 2019, 11:50 am
Ja, dann mal eine Kurzanleitung.

Auf der Seite gibt es drei Pakete.
x86, x64 und für Linux

x86, x64  laufen auf meinem Win 10
x64 ist deutlich schneller, sonst keine erkennbaren Unterschiede.

Ich gehe mal davon aus, dass du immer noch das Arduino AVR Paket 1.6.209 installiert hast.


Bei mir fahre ich die Arduino portable Version.
Du auch?
Egal.

Das 9.1 Paket kannst du in irgendeinen beliebigen Ordner stopfen.
Ich habe E:\Programme\arduino\portable\avr-gcc\avr-gcc-9.1.0-x64-mingw
gewählt/erzeugt.
Einziger Rat: Keine Leerzeichen im Path

In dem 1.6.209 Installationsordner findet sich eine platforms.txt Datei
Bei mir: E:\Programme\arduino\portable\packages\arduino\hardware\avr\1.6.209\platforms.txt
Daneben, eine platforms.local.txt anlegen

Mit folgendem Inhalt:
Code: [Select]

compiler.path={runtime.ide.path}/portable/avr-gcc/avr-gcc-9.1.0-x64-mingw/bin/

compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++2a  -fno-exceptions -fpermissive -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto
compiler.cpp.extra_flags=-fno-sized-deallocation  -fconcepts



Wenn du einen ganz anderen Ordner wählst, kannst du auch den absoluten Pfad angeben.
Hier eine ältere Variante von mir
Code: [Select]
#compiler.path=E:\Programme\arduino\portable\packages\arduino\tools\avr-gcc\avr-gcc-9.1.0-x64-mingw/bin/

So fluppt es bei mir.

-------
Am Rande:
https://www.gnu.org/software/gcc/projects/cxx-status.html (https://www.gnu.org/software/gcc/projects/cxx-status.html)


Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Doc_Arduino on Jul 05, 2019, 09:14 pm
Hallo,

läuft  :smiley-mr-green:

Meine IDE ist installiert.  Habs entpackt unter   (32Bit)
Code: [Select]

C:\Users\xyz\Documents\Arduino\avr-gcc-9.1.0-x86-mingw


Mein Pfad lautet  
Code: [Select]

C:\Users\xyz\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.209


Der Eintrag entsprechend
Code: [Select]
compiler.path=C:\Users\xyz\Documents\Arduino\avr-gcc-9.1.0-x86-mingw/bin/

compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++2a  -fno-exceptions -fpermissive -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto
compiler.cpp.extra_flags=-fno-sized-deallocation  -fconcepts

Besten Dank kann ich da nur sagen. Damit kann man jede aktuelle Version verwenden.  :)
Klappt das auch ohne das "1.6.209 Paket" ?   Wobei das nicht wirklich stört.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Jul 05, 2019, 09:55 pm
Quote
Klappt das auch ohne das "1.6.209 Paket" ?
Nicht getestet...

Vielleicht stiften wir ja einen stillen Mitleser an?
?

Laut dem Kommentar müsste das funktionieren:
Quote
Code: [Select]
# Default "compiler.path" is correct, change only if you want to override the initial value
compiler.path={runtime.tools.avr-gcc.path}/bin/
Das kann man schon fast als Empfehlung auffassen.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Doc_Arduino on Jul 06, 2019, 08:20 am
Hallo,

wenn ich mal mehr Zeit haben sollte ... könnte ich das ausprobieren, glaube aber selbst nicht dran.

Habe den SchedulerLoopCount mit der 32 und 64Bit Version vom avr-gcc-9.1 laufen lassen. Bekomme wie erwartet gleiche Werte.
Die 100.000er Marke wurde jedoch unterschritten. Vielleicht sind irgendwo noch verschiedene Einstellungen vorhanden.
Oder das IDE Paket 1.6.209 mit seinem gcc-7.3er war/ist optimiert. Naja was solls.   ;) 

Code: [Select]

// 32Bit
Scheduler loops: 87368 per 1000ms
Scheduler loops: 87278 per 1000ms
Scheduler loops: 87369 per 1000ms

// 64Bit
Scheduler loops: 87278 per 1000ms
Scheduler loops: 87369 per 1000ms
Scheduler loops: 87368 per 1000ms


Welche Unterschiede hast du in den xx Bit Versionen?
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 13, 2019, 01:48 am
Hallo combie,

ich habe mal Dein:
QuadroDynamicDefekt mal ausprobiert.
Dabei habe ich nicht - wie vorgesehen - die Library benutzt, sondern die Dateien in die lokale Dir kopiert.
Dabei ist mir aufgefallen, dass ich in Task.h  #include "Listnode.h"  #include "ListNode.h" umbenennen musste.
Das Ergebnis ist ok:

Start
BlinkTask begin, pin: 10
BlinkTask begin, pin: 11
BlinkTask begin, pin: 12
Init 13
BlinkTask begin, pin: 13

Oder habe ich etwas falsch verstanden?




Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Jul 13, 2019, 02:15 am
Quote
Dabei ist mir aufgefallen, dass ich in Task.h  #include "Listnode.h"  #include "ListNode.h" umbenennen musste.
Oh...
Windows meldet mir solche Fehler leider nicht.

Wird in der nächsten Version geändert!

Danke.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 13, 2019, 02:27 am
So, die "QuadroBlinkDynamicDefekt" funktioniert. Es lassen sich jederzeit - auch später im Programm - dynamisch neue Tasks starten.
Anbei als Zip. (keine Library - alles in Sketch-Folder).

Ich benutze kooperatives Multitasking schon seit vielen Jahren - lange vor dem Erscheinen von Arduino.
Hier meine "Werke":
https://github.com/MacLeod-D/Arduino-Multitasking-CoopOS
https://github.com/MacLeod-D/CoopOS-Kurs-Deutsch
http:/HelmutWeber.de
Daher darf ich bei aller Bescheidenheit wohl sagen, über einige Erfahrung zu verfügen - ohne zu beanspruchen, die Weisheit mit Löffeln gefuttert zu haben.

Es juckt mich, einige Bemerkungen loszuwerden, aber die könnten als Kritik verstanden werden.
Das möchte ich vermeiden!

Deshalb positiv:
Combie hat die beste C++ Version erstellt, die ich bisher gesehen habe - deutlich besser, als meine eigenen Versuche dazu.

Glückwunsch - weiter so! ;)

Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 13, 2019, 10:42 am
Sorry, firstD, firstE müsssen Global statt local ;)
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Jul 13, 2019, 02:28 pm
Quote
Combie hat die beste C++ Version erstellt, die ich bisher gesehen habe - deutlich besser, als meine eigenen Versuche dazu.

Glückwunsch - weiter so! ;)
Danke für die Blumen und den Zuspruch!

Allerdings, ist es doch auch ein hervorragender Dünger für meinen Hochmut und Arroganz. :o


Kritik, Bemerkungen?
Men los!

Ich fange mal an:
Beim überfliegen deiner Seiten, ist mir ein "sind" aufgefallen, welches, so vermute ich, eigentlich gerne ein "Sinn" werden möchte.

---

Eigentlich höre ich mir gerne alles an....
Etwas Probleme habe ich da mit allzu absoluten Aussagen, und (ausuferndem) Featurerismus.

Offensichtlich hast du dich schon "etwas" mit dem Thema Multitasking beschäftigt.
Das Denken in Nebenläufigkeiten scheint mir, bei dir, gut verankert zu sein.
Und der Einbau deiner "TaskHandles", in meinen Code, hat ja auch gut geklappt.

Wie auch immer...
Ich betrachte diese Multitasking Variante eher als Fingerübung in Sachen C++, als ein bezugsfertiges System.
Da ist also noch deutlich Entwicklungspotential.

Also:
Bitte raus damit.
Sage, was es zu sagen gibt.


Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Doc_Arduino on Jul 14, 2019, 05:21 pm
Hallo,

ich habe es heute probiert ohne das Board Paket 1.6.209.
Alle Zusatzpfade in den Preference gelöscht und IDE deinstalliert.
Auch den Ordner  C:\xyz\Worker\AppData\Local\Arduino15  habe ich anschließend gelöscht.
Nackte aktuelle IDE installiert.
Wenn man die platform.local.txt dann in den Pfad
C:\Program Files (x86)\Arduino\hardware\arduino\avr\ ablegt verwendet die IDE den dort angegebenen gcc 9.1.
Ein Problem weniger.  :)



Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 14, 2019, 11:34 pm
Ok, Du hast es gewollt ;)

1) Es gibt keinen TaskStatus. (Task.h: enum state {READY, DELAYED, ..} class Task { state State; ... }

Das führt dazu, dass auch pausierende Tasks aufgerufen werden, die - da pausierend - sofort mit taskSwitch:
Code: [Select]

#define taskSwitch()                               \
    do {                                                     \
        lable = &&LC_CONCAT(taskLable, __LINE__);    \
        return;                                                                    \
       LC_CONCAT(taskLable, __LINE__):;                    \
    } while(0)


#define taskPause(Task_interval)                                  \
    timeStamp = millis();                                                 \
    while((millis() - timeStamp) < (Task_interval)) taskSwitch()



zurückzukehren. Welch eine Verschwendung. Bei länger wartenden Tasks kann sich das millionenfach wiederholen, so dass 20 wartende Task einen Task, der jetzt dran ist, erheblich verzögern können.
Ergebnis: 200000 TaskSwitches /s aber dabei nur echte 2 TaskSwitches für READY Tasks.
Ein Task, der pausiert, darf nicht aufgerufen werden! Punkt.

Bei RTOS ( die, die mit Listen arbeiten) wird ein pausierender Task auf eine andere Liste verschoben.

2) Die Verwaltung mit der Liste ist fragwürdig. Es wird nur zugefügt. Das geht besser (und schneller) mit einem
Array[MAXTASKS];
Damit ist auch eindeutig, ob ein neuer Task Platz hat - da statisch reserviert. Mit "new" muss in jedem Fall (vorab) kontrolliert werden, ob der Speicherplatz dafür noch reicht.

3) Und wann und wie lösche ich einen Task? Jederzeit? Oder nur bei einem bestimmten Zustand seiner selbst?

4) Es fehlen Zustandüberwachungen wie: (serial output):
Starte Task nur, wenn Resource frei ist.
Oder: Bleibe blockiert, bis ein anderer Task (oder Interrupt) dich wieder aktiviert.

5) Es fehlen Taskprioritäten, um dafür zu sorgen, dass der wichtigere (READY-) Task zuerst aufgerufen wird.

6) Die Tasks (A,B,C) werden "rückwärts" initialisiert und auch (bei gleichen Delays) abgearbeitet.
Das kann bei Tasks, deren Wirkung - gerade beim Start - aufeinander aufbaut, zu Problemen führen:

Sch-Begin: BlinkTask begin, pin: 5 Name: C                    <<<
Sch-Begin: BlinkTask begin, pin: 12 Name: B                  <<<
Sch-Begin: BlinkTask begin, pin: 2 Name: A                    <<<
construct task: Add
Constr 14
BlinkTask begin, pin: 14 Name: F                                    
construct task: Add
Constr 14
BlinkTask begin, pin: 14 Name: Hallo1
Loop Task: Hallo1 Pin:14                                                <<< Reihenfolge rückwärts
Loop Task: F Pin:14                                                        <<< ...
Loop Task: C Pin:5
Loop Task: B Pin:12
Loop Task: A Pin:2

Folge von: An Liste (add(Node)) wird vorne angefügt - nicht hinten!


Mit anderen Worten: als "Fingerübung" sehr schön - in der Praxis so (noch!) nicht zu gebrauchen.
Also: Tasks haben Zustände (state), die vom Scheduler berücksichtigt werden:
Code: [Select]

For i < TaskAnzahl

    if (DELAYED)
      Test, ob von DELAYED auf READY

    if (READY)
      call Task

End For  

Eine solche Schleife übergeht z.Bsp. auch BLOCKED Tasks ohne Zeitverlust.

Warum taskPause in millis() statt micros(), wenn 200000 TaskSwitches ausgeführt werden (alle 5 us) ?

Jeder Task muss von class Task ableitet werden. Warum ? Es reicht, wenn class Task die Funktionspointer für
       begin, loop
enthält. Wobei loop eigentlich reicht.
Dann können mit TaskInit("name", BeginPt, LoopPt); Funktionen vom Scheduler aufgerufen werden - wesentlich übersichtlicher. Alle Tasks sind von class Task - egal, was sie letztendlich tun!

Tasks können als einfache Funktionen (mit taskBegin, taskEnd)  deklariert sein - wesentlich übersichtlicher und einfacher, als für jeden neuen Task eine neue Klasse zu definieren/abzuleiten.
Sinn von OOP ist es schließlich, Dinge zu abstrahieren und die Benutzung des Gesamtproduktes zu vereinfachen, statt sie unnötig zu komplizieren.

Da aber TaskLoop sowieso vom Scheduler aufgerufen wird, ist Task.h eigentlich auch überflüssig:
Es reicht struct (von mir aus class) Task:
Code: [Select]

class task {
    char     *name;
    char     ID;
    state    State;
    void     *Param;
    void     (*FunctionLoop)();  // Von InitTask besetzt
...
}


Damit kann - wenn gewünscht - auch eine Parameter-Struktur an einen aufgerufenen Task weitergereicht werden. Die auch von mehreren Tasks benutzt werden kann.

Bitte nicht böse sein. Ich finde Deinen Ansatz nach wie vor Klasse! Aber etwas lösen von den "Fingerübungen" und mehr die Praxis im Hinterkopf haben könnte Sinn machen.

Hier eine simple Aufgabe:
BlinkTask1 : Loop: schalte LED 1000 us HIGH, dann 1000 µs LOW
BlinkTask2 : Wenn Blink2 auf LOW geschaltet hat - und nur dann:
               Loop: schalte LED 100 us High, dann 100 us LOW.

Lasse 5 weitere Tasks laufen und kontrolliere das Ergebnis mit einem LA:
Wie genau werden die Zeiten eingehalten? Wie lange braucht Task2, um auf die LOW Flanke von
Task1 zu reagieren?

Eine gute Intertask-Kommunikation ist sehr wesentlich für ein solches System. Reine Delays sind die Ausnahme!

Ich hoffe sehr, ich habe eher Deinen Ehrgeiz angestachelt, als Dich entmutigt.
Bleibe am Ball - Du bist auf einem guten Weg - der noch weit ist. Durchhalten ;)

Mit besten Wünschen und Grüßen
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 14, 2019, 11:41 pm
Wenn Du eine entsprechende Version machst:

Ich teste sie mit Vergnügen!
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Jul 15, 2019, 02:32 am
Wenn Du eine entsprechende Version machst:
Das kannst du knicken!


Mit diesen Anforderungen, habe ich irgendwie gerechnet.

Irgend eine Art von abgesetzten Taskkontrollblöcken, werde ich nicht einführen.
Prioritäten, no
Tasks in einem Array fester Größe verwalten, no.

Ob eine Task abgelaufen ist, wen interessiert es...
Und wenn doch, dann kann sie ja ihren Status bereit stellen.
Oder sich jederzeit aus dem Speicher entfernen.

Was interessieren mich µs? ,
ms ist in meiner kleinen Arduino Welt meistens völlig ausreichend.
Zudem: Der Umbau, oder das hinzufügen, wäre trivial.

Du möchtest bei jedem Taskwechsel:
1. Prioritäten abhandeln
2. Taskzustand abhandeln
Das kostet RAM, Flash und Zeit.
Diese verplemperten Ressourcen könnte man effektiver für eine weitere Task verwenden.



Wer Spaß an der Featureritis hat , darf den Kram gerne einbauen.
Alternativ könnte der Wünscher dein System verwenden, oder FreeRTOS
Eine ganze Reihe von Alternativen, es gibt.


----------------------


Was anderes, ist die Sache mit den Mutex/Semaphore.
Ein solches Konzept ist manchmal nötig.
Allerdings auch bei jedem anderen System, welches Nebenläufigkeiten nutzt/implementiert.
Egal, ob da nun Multitasking auf dem Etikett steht, oder nicht.

Das kann/darf gerne jeder selber entwickeln.
Nutzbare Implementierungen gibt es auch schon wie Sand am Meer.
Aus dem Pool bediene ich mich sowieso schon.

Die Mutex/Semaphore Idee muss also nicht Teil "meines" Systems sein.
Und ja, es wäre ein sinnvolles/nützliches Feature.


----------------------------------------


Quote
2) Die Verwaltung mit der Liste ist fragwürdig. Es wird nur zugefügt.
Es wird auch entfernt.


Quote
Damit ist auch eindeutig, ob ein neuer Task Platz hat - da statisch reserviert. Mit "new" muss in jedem Fall (vorab) kontrolliert werden, ob der Speicherplatz dafür noch reicht.
Instanzen von Task können unterschiedlich groß sein.


Quote
Damit kann - wenn gewünscht - auch eine Parameter-Struktur an einen aufgerufenen Task weitergereicht werden. Die auch von mehreren Tasks benutzt werden kann.
Das geht bei mir per Konstruktor, Template oder Setter Injektion.
Einen vierten Weg halte ich für flüssiger als Wasser. Für Überflüssig.


Quote
Tasks können als einfache Funktionen (mit taskBegin, taskEnd)  deklariert sein - wesentlich übersichtlicher.
War es nicht der OOP Ansatz, welche die Begeisterung bei dir ausgelöst hat?



Quote
Ein Task, der pausiert, darf nicht aufgerufen werden! Punkt.
Wie sagte ich zu absoluten Aussagen?
> Etwas Probleme habe ich da mit allzu absoluten Aussagen ....


Übrigens, wenn ich das richtig gesehen habe verwendest du switch/case für dein Konzept.
Das wird mit zunehmender Anzahl Fälle schnell sehr ineffektiv.
Es zerfällt in speicherintensive lookup tables oder zeitintensive Vergleichs/Entscheidungs Kaskaden.
Auch verbietet es die Verwendung von switch/case in Tasks.

Quote
Welch eine Verschwendung.
Nur wenn man das switch/case dafür verwendet.
Mit dieser simplen Anwendung von computed goto  ist man die Verschwendung los.



----


Ich hoffe, dass du mir jetzt nicht allzu böse bist.

Schade, dass meine Implementierung nicht deinen Ansprüchen genüge tut.
Aber sie wäre nicht so herzerfrischend naiv, wenn sie nicht so wäre, wie sie ist.


----


Wo es Potential gibt:
Die Wire und SPI Implementierungen blockieren, solange eine Transaktion läuft.
Da bleibt richtig Zeit liegen....

Serial ist da etwas komfortabler, es sagt immerhin, wie viele Zeichen/Byte im Buffer frei sind. So kann man Wartezeit abgeben.








Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Jul 15, 2019, 11:48 am
Den Punkt 6 habe ich eben erst gesehen.....

Quote
6) Die Tasks (A,B,C) werden "rückwärts" initialisiert und auch (bei gleichen Delays) abgearbeitet.
Das kann bei Tasks, deren Wirkung - gerade beim Start - aufeinander aufbaut, zu Problemen führen:

Sch-Begin: BlinkTask begin, pin: 5 Name: C                    <<<
Sch-Begin: BlinkTask begin, pin: 12 Name: B                  <<<
Sch-Begin: BlinkTask begin, pin: 2 Name: A                    <<<
construct task: Add
Constr 14
BlinkTask begin, pin: 14 Name: F                                   
construct task: Add
Constr 14
BlinkTask begin, pin: 14 Name: Hallo1
Loop Task: Hallo1 Pin:14                                                <<< Reihenfolge rückwärts
Loop Task: F Pin:14                                                        <<< ...
Loop Task: C Pin:5
Loop Task: B Pin:12
Loop Task: A Pin:2

Folge von: An Liste (add(Node)) wird vorne angefügt - nicht hinten!
Deine Argumentation zieht nur, wenn man heimliche, von der Reihenfolge abhängige, Abhängigkeiten einbaut.
Aber wer das tut, bringt sich selber in des Teufels Küche.
Sowieso.

Aus meiner Sicht, eine irrationale Geschichte.


Ob an den Anfang, oder ans Ende der Liste eingefügt wird, ist prinzipiell völlig egal.
Die Funktionsfähigkeit eines Multitasking Systems darf nicht von der Abarbeitungsreihenfolge der Tasks abhängig sein.
Es müssen alle Tasks an die Reihe kommen, diese Garantie reicht.


Das Einfügen an den Anfang ist einfacher und deutlich schneller, bei einer einfach verketteten Liste.
Hinten einhängen und von Vorn durchlaufen, erfordert eine doppelt verkettete Liste.
Und damit quasi den doppelten Verarbeitungsaufwand bei Veränderungen und den doppelten Speicher Aufwand.

Das nur, um eine potentielle Schlampigkeit/Irrtum des Anwendungsprogrammierers auszubügeln?


Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 15, 2019, 02:24 pm
"Allerdings, ist es doch auch ein hervorragender Dünger für meinen Hochmut und Arroganz. :o "

Es war zu befürchten, dass Du so reagierst. ;)

"Damit ist auch eindeutig, ob ein neuer Task Platz hat - da statisch reserviert. Mit "new" muss in jedem Fall (vorab) kontrolliert werden, ob der Speicherplatz dafür noch reicht."

Re. "Instanzen von Task können unterschiedlich groß sein."

Die von TCBs nicht !

"Du möchtest bei jedem Taskwechsel:
1. Prioritäten abhandeln
2. Taskzustand abhandeln
Das kostet RAM, Flash und Zeit.
Diese verplemperten Ressourcen könnte man effektiver für eine weitere Task verwenden. "

Oder für sinnlose TaskSwitches ?

Erweitere mal dein Macro taskPause

Code: [Select]

#define taskPause(Task_interval)                                      \
    timeStamp = millis();                                                      \
    while((millis() - timeStamp) < (Task_interval)) {             \
        Serial.println("Ich verschwende Zeit");                      \
        taskSwitch();                                                             \
    }


und lass es laufen und wundere Dich.

Um überhaupt noch etwas zu sehen nimm
Serial.print("."); \

Loop Task: A Pin:2
...7086
...Loop Task: E Pin:14
.Loop Task: D Pin:13
.................................................

(Hier hat jemand die unerträglich vielen Punkte gelöscht, die in taskPause geschriebben wurden,
Also selber ausprobieren!)



Ich bleibe dabei: diese taskPause() - ein zentraler Punkt -  ist wirklich reine Zeitverschwendung.


"Was interessieren mich µs? ,
ms ist in meiner kleinen Arduino Welt meistens völlig ausreichend."

Die Zahl der erreichten TaskSwitches/s scheinen Dich doch sehr zu interessieren, denn von denen berichtest
Du gerne ;)
Ein Standard Sensor: HC_SR04 Ultraschallsensor ... mit ms ?


Alle preemptiven Versionen sind langsamer und brauchen viel mehr Speicher. Genau da liegen ja die Vorteile des
kooperativen Multitasking.
Und selbst in der noch viel kleineren AtTiny Welt sind us oft interessant.

Auch wenn Du es anscheinend nicht merkst: Du benutzt selbst Duff's Device. (TaskBlock, taskSwitch, ...) mit
entsprechenden Auswirkungen:
Z. Bsp.: keine localen Variablen im Task möglich - nur static.

Im loop von BlinkTask:
Code: [Select]

virtual void loop()
 {
 static int mycnt;
   TaskBlock
   {
     mycnt++;
     Serial.print("Loop Task: "); Serial.print(name); Serial.print(" Pin:"); Serial.print(pin); Serial.print(" ");
     Serial.println(mycnt);

Mit static geht, ohne nicht!

Code: [Select]

oder probiere:
virtual void loop()
 {
   TaskBlock
   {
     int i=0;
     do {
       i++;
       taskSwitch();
     } while (i<5);

     // wird NIE ausgeführt !!!        
     >>>>> Serial.print("Loop Task: "); Serial.print(name); Serial.print(" Pin:"); Serial.println(pin);


Ich habe mal taskSwitch/taskPause erweitert:

Code: [Select]

// Task Kontroll Worte, diese werden Taskwechsel einleiten
#define taskSwitch()                                                          \
  do {                                                                                  \
        SwitchCount++; \
        lable = &&LC_CONCAT(taskLable, __LINE__);        \
        return;                                      \
        LC_CONCAT(taskLable, __LINE__):;                        \
  } while(0)
  
#define taskPause(Task_interval)                                      \
  timeStamp = millis();                                                        \
  while((millis() - timeStamp) < (Task_interval)) {               \
      PauseCount++;                                                           \
      digitalWrite(5, HIGH);                                                  \
      taskSwitch();                                                               \
      digitalWrite(5, LOW);                                                  \
  }


Ein spezieller BlinkTask2 gibt alle 2 Sekunden aus:

Code: [Select]

TaskBlock
    {
      Serial.print("Loop Task: "); Serial.print(name); Serial.print(" Pin:"); Serial.println(pin);
      Serial.print("Micros/Pause/Switch:  ");
      Serial.print(micros()); Serial.print(" / "); Serial.print(PauseCount);
      Serial.print(" / "); Serial.println(SwitchCount);
      SwitchCount=0; PauseCount=0;


Output:
Loop Task: Hallo1 Pin:14
Micros/Pause/Switch:  535208045 / 440083 / 440083
Loop Task: F Pin:14
Loop Task: C Pin:5
Loop Task: B Pin:12
Loop Task: A Pin:2
Loop Task: E Pin:14
Loop Task: D Pin:13
31450
Loop Task: E Pin:14
Loop Task: D Pin:13
31203
Loop Task: Hallo1 Pin:14                              (TaskSwitch)
Micros/Pause/Switch:  537208039 / 438550 / 438550
                                                   (TaskPause)
Loop Task: Hallo1 kommt sehr sauber alle 2 Sekunden.

Aber:

taskPause() und in Folge taskSwitch() werden je 220000 mal pro Sekunde aufgerufen !

Das ist das, was ich mit Verschwendung meinte!

Auch mit dem LA kann man sehen:
Das System ist mehr als 50% der Zeit mit
              taskPause() / taskSwitch()
beschäftigt !

"Die Zeit kann man besser für weitere Tasks nutzen" ;)


Meine Implementierung erfüllt diese Forderungen und ist mindestens so schnell, wie Deine ;)
Bei vielen Tasks mit delay mit Sicherheit schneller. Ich stelle mich gerne einem Vergleich.
Es geht also! Wo bleibt Dein Ehrgeiz?

Wie gehst Du im Job mit einem Vorgesetzten um, der Forderungen wie ich stellt?

Schade - Dein grundsätzlicher Ansatz hätte viel Potential gehabt. Aber so bleibt es wohl doch nur Hilfsmittel für Hochmut und Arroganz  - und sicher wärst Du stinksauer, wenn ich sagen würde, was ich davon halte.
So jedenfalls ist Dein Produkt nicht brauchbar.

Auf den Ansatz kannst Du stolz sein - auf das sture Verfolgen der Irrwege und Verkaufen von Anfängerfehlern als Innovation eher nicht.

Schade, denn ich hätte Dir wirklich das Potential zugetraut, ein beachtliches Produkt zu schaffen! Aber wenn Du so schnell aufgibst mit "will ich nicht, weil ich es nicht kann", dann wird die Säule des Hochmuts wohl ins Wanken geraten.

Schluck einfach mal trocken runter und schaue - ohne beleidigt zu sein - meine Ratschläge noch einmal in Ruhe an. Meine Beispielaufgabe ist doch wirklich nicht schwer. (Die beiden HIGH/LOW Tasks)
Programmieren ist kein Selbstzweck - es sollte auch als Übung etwas brauchbares leisten.

Und solltest Du je einmal professioneller Programmierer werden wollen - gewöhne Dich daran, mit Anforderungen umzugehen ;)
Ich habe Dir ein paar Jahrzehnte Praxis voraus. Das heißt nicht zwangsläufig, dass ich ein besserer Programmierer bin. Aber ich sehe sehr schnell, wo es bei einer Lösung klemmt. Im Zweifel mit einem LA - ohne das Programm auch nur zu sehen.
Was hindert Dich, in class Task ein flag zu setzen: InDelay und timestamp dort zu deklarieren und in
class Scheduler::loop:
Code: [Select]

while(temp)
{
     temp->loop();  // InDelay neu setzen
     temp = (Task *)list.getNext(temp);

     // Dieser Test sorgt dafür, dass ein Task, der pausiert, gar nicht erst
     // Aufgerufen wird. Eine enorme Zeitersparnis!  

     if (temp->InDelay) {
         if ((millis() - timeStamp) < (Task_interval)) {    // Dieser Task pausiert noch
             temp = (Task *)list.getNext(temp);              //  Der nächste bitte ...
         }
     }
     else temp->InDelay = false;
}

// Nicht vollständig, aber ich denke die Richtung ist erkennbar.

Damit würden nur Tasks aufgerufen, die nicht pausieren - READY Tasks also viel schneller drankommen !

(Nachtrag)
Schaue dir mal das Disassembly von switch/case und computed goto an. Keiner zu finden?
Doch: Switch/case arbeitet genauso mit den vom Compiler  __LINE__ generierten Labels.
Ein Fehler darin führt zu Fehlverhalten (der case wird nicht gefunden) - aber nicht zum Absturz.
Und dieses Fehlverhalten kann entdeckt und abgefangen werden. (Mache ich auch nicht, aber es geht)
Computed goto verreckt sofort gnadenlos ! Still ruht der See.

Sturheit ist manchmal eine Tugend - und manchmal einfach nur dumm.
Die Kunst ist, die beiden Fälle zu erkennen ;)

Schaffst Du es, mir trotz meiner klaren Worte nicht böse zu sein?



Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 15, 2019, 02:57 pm
Die Reihenfolge der Tasks  kann am Anfang (ja, nur da!) wichtig sein. Z. Bsp. zur Initialisierung des Systems mit einem Task "SysInit", der weitere Tasks, die geblockt starten, freigibt.
Oder auch:
So etwas benötigt man z. Bsp., wenn ein WDT kontrolliert einen Systemfehler erkennt und SysInit später als Task Aufrufen möchte - der wiederum das System hochfährt.
Es ist auch wichtig, wenn sich 2 Tasks Signale senden und so gegenseitig synchronisieren, aber nur, wenn sie in passender Synchronisation starten:
Task_1 sendet Ultraschallpuls und startet Task2.
Task2 misst die Zeit des Echos.
Wer keine Taskkommunikation benutzt und die Tasks "anonym" frei und losgelöst von dieser Welt und ohne eine eigentliche Funktion "vor sich hinblinken läßt", dem kann die Start-Reihenfolge natürlich egal sein :)
Mit der Meinung: "Die Startreihenfolge ist unwichtig" wirst du wohl nicht viele überzeugen.
Da tropft es nämlich aus der Kaffeemaschine, bevor der Startknopf gedrückt wurde.

Ich denke, wenn Du ausser LED-Geblinke auch mal richtige Beispiele programmierst, wird dir so mancher Kronleuchter aufgehen.
Übrigens: Mit absoluten Aussagen habe ich nicht so Probleme, wie Du. Aber mit absolutem Unsinn schon.
Denn Programieren bedeuted Logik - und das Programm MACHT absolute Aussagen.
Also: mal ran an die Kaffemaschine:
Ist Einschaltknopf gedrückt?
Ja, ist genug Wasser da? Nein, blinke uns stoppe.
loop: Ja, heize Wasser auf
Wasser heiß genug? Nein, gehe zu loop
...
Jeder Task ist von dem anderen abhängig.
Wasserbehälter wird unvorhergesehen entnommen. Also alles stoppen und blinken.

Aber ich habe den Eindruck, Du bist schon so arrogant, dass Du lernresistent geworden bist !
Schade.


Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 15, 2019, 03:15 pm
Denn Hochmut kommt vor dem Fall - immer  :smiley-eek-blue:

Wer nicht in der Lage ist- auch nicht mit einem Serial.print - festzustellen, welche Taskvariation nun gerade blinkt
Wer im Delay die millis() bis zum Abwinken pollt und damit den Prozessor massiv belastet
Wer Tasks baut, deren Status (READY, BLOCKED, DELAYED) nicht erkennbar ist
Wer Sicherheitsabfragen (new: hat es geklappt?) für überflüssig hält
Wer wirklich glaubt, die Reihenfolge beim Start von Tasks sei egal
Wer sich an das Publikum mit einem Problem wendet, weil die REIHENFOLGE bei der Initialisierung nicht stimmte

der sollte sich sagen lassen: Das Üben mit Tools macht Sinn. Die Demonstration seiner Fertigkeit im Umgang damit auch. Aber es nützt nichts, wenn es nicht irgendwann auch einmal verwertbar eingesetzt wird.
Auch wenn Du bei jedem Jahrmarkt einen Nagel immer mit drei Schlägen versenkst - ein guter Zimmermann bist Du deshalb noch nicht.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 15, 2019, 03:23 pm
Ich habe Dein defektes Programm (QuadroBlinkDynamicDefekt) repariert.
Dabei hätte ich es belassen sollen.
Der Rest war wohl Perlen ...
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Tommy56 on Jul 15, 2019, 03:23 pm
@heweb: Das was Du hier abziehst, hat aber auch nichts mehr mit sachlicher Kritik zu tun. Du hast Dich in eine Hasspredigt rein gesteigert.

Komm wieder runter von Deinem hohen Ross.

Gruß Tommy
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 15, 2019, 03:37 pm
"Das Einfügen an den Anfang ist einfacher und deutlich schneller, bei einer einfach verketteten Liste.
Hinten einhängen und von Vorn durchlaufen, erfordert eine doppelt verkettete Liste.
Und damit quasi den doppelten Verarbeitungsaufwand bei Veränderungen und den doppelten Speicher Aufwand."

Tut mir leid, auch das ist Unsinn!

Deine Version - zufügen am Anfang:

Code: [Select]

void add(Node *client)
  {
    Node *temp = first;
    first = client;
    client->next = static_cast<ClientClass *>(temp);
  }


Ein Pointer auf den letzen Eintrag reicht völlig aus.

Meine Version - zufügen am Ende:

Code: [Select]

void add(Node *client)
{
  if (end) end->next = static_cast<ClientClass *>(client);
  else first=client;
  end = client;
}


Diese kleine Änderung - ohne jede sonstige Modifikation im Programm - sorgt für Abhilfe.
Einen großen Mehraufwand an Zeit und Code sehe ich nicht.

Kannst Du wenigsten hier mal zugeben, Unrecht gehabt zu haben ? ;)
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 15, 2019, 03:49 pm
@Tommy56
Hasspredigt ist wohl doch übertrieben.
Wenn jemand übt, dann helfe ich gerne. Aber mit Hochmut und Arroganz habe ich so meine Probleme - wenn sie auf sehr wackeligen Füssen stehen.
Ja, ich gebe zu, so schlau wie mit 18 bin ich nie wieder geworden ;) Und das ist das geschätzte Alter von Combie.
Aber wenn er anfängt, vor lauter Selbstüberschätzung gute und in der Praxis bewährte Produkte herabzusetzen
und seine Methoden als die Weisheit dieser Welt hinstellt - und zwar sehr absolut - und die Begründung dogmatisch wird, statt logisch, dann wird es problematisch.
Ich habe nichts dagegen, wenn er mir bessere Methoden zeigt - es gibt immer einen, der besser ist.
Aber es muss dann auch Hand und Fuß haben. Ich bin da für Beispiele, Tests, Messugen usw.

Ich denke - mein letztes Beispiel (Liste) zeigt sehr genau, was ich meine.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Tommy56 on Jul 15, 2019, 03:51 pm
Wenn Du schon weiter hier rumtrollen willst, dann zeige wenigstens, dass Du die Minimalforderungen an eine Diskussion in einem Forum erfüllen kannst: Kennzeichne Zitate ordentlich als solche und setze Deine Codefragmente in Codetags.
Deinen anmaßenden Ton und die Beleidigungen kannst Du auch weglassen.

Gruß Tommy

Edit: Du hast Probleme mit Arroganz? Dann schaue mal in den Spiegel.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Jul 15, 2019, 03:53 pm
Quote
Du bist schon so arrogant, dass Du lernresistent geworden bist !
Da hast du natürlich völlig Wahr!
Lernresistenz und Arroganz, sind meine herausragendsten Eigenschaften.


Ich wünsch dir viel Spaß mit deinen tropfenden Kaffeemaschinen und auch (egal wie verketteten) Listen.


Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 15, 2019, 04:08 pm
@Tommy
Dein Eintreten für Combie ist nett gemeint.
Wenn mehr Erfahrung und gute Ratschläge arrogant sind - dann bin ich es wohl.
Wenn das Beispiel für das einfache Anfügen an eine Liste "herumtrollen" ist und als arrogant empfunden wird - dann sollte ich gehen.
Ich werde diesen Thread nicht mehr belästigen - ich denke, das ist in Eurem Sinne.
Tut mir leid, dass ich Euch gestört habe. Macht weiter so.

Und da es sinngemäß von Combie kommt ist es sicher ok:

Ich wünsche Euch noch viel Spaß beim Experimentieren mit "QuadroBlinkDynamicDefekt" und den Listen, bei denen
das Anfügen an das Ende so problemtisch ist.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Jul 15, 2019, 04:25 pm
Quote
ich denke, das ist in Eurem Sinne.
Och...

Warum?

Eigentlich geht das so:
1. Man beäugt das Gegenüber, was es so tut, wie es sich so verhält.
2. Man macht den Hahn auf dem Mist. Hierarchie Gerangel.
3. Und wenn die Fronten geklärt sind, dann dominiert man den anderen.
4. Gelingt 2 und 3 nicht, kann man sich ja immer noch auf das fachliche konzentrieren.

Mir ist es im Grunde lieber, die Schritte 1 bis 3 zu überspringen.
Aber, wie meine Oma schon sagte: Watt mutt, dat mutt!
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Tommy56 on Jul 15, 2019, 04:54 pm
@Tommy
Dein Eintreten für Combie ist nett gemeint.
Ich bin nicht für Combie, sondern gegen Deine Beleidigungen eingetreten.

Wenn mehr Erfahrung und gute Ratschläge arrogant sind - dann bin ich es wohl.
Die Art und Weise, wie Du Deine "Erfahrungen und Ratschläge", gepaart mit persönlichen Beleidigungen, hier bringst, macht die Arroganz aus.
Gegen eine sachliche Fachdiskussion hat keiner etwas.

Gruß Tommy
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Doc_Arduino on Jul 15, 2019, 05:19 pm
Hallo,

ich denke eher die geballte Menge an Informationen die nur so einprasselt macht das Problem. Stück für Stück wäre besser gewesen. Man kann das kaum erfassen - ich jedenfalls. Und man sollte schon quote und code tags verwenden um das leserlich zu machen. Das kannst du heweb auch nachträglich noch bitte erledigen.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 15, 2019, 09:31 pm
@Tommy56
Dein fachlicher Beitrag beschränkte sich auf "Hasspredigt" und "herumtrollen".

Kann es sein, dass Du trotz der Einführung von Combie ( Dein Gespräch an anderer Stelle mit Combie)  noch nicht so vertraut mit dem kooperativen Multitasking bist ?

Ich habe immer wieder betont, dass ich Combies Ansatz sehr gut finde !


Meine Bespiele zeigten - berechtigte - fachliche Kritik.
Wie Combie damit umgegangen ist, hat mir nicht gefallen. Ob Du es glaubst oder nicht:
Dabei geht es nicht ums Recht haben, sondern die Verzweiflung über die Sturheit, mit der der gute Ansatz
erkennbar in die falsche Richtung geprügelt wird. Dabei hätte Combies Versuch durchaus das Potential für ein
tolles Produkt. Und bereits wenige Zeilen Änderung würden es qualitativ enorm aufwerten.

Obwohl ich C++ recht gut beherrsche ist Combie da sicher besser. Ich kann das problemlos so sehen. Es gibt immer einen, der besser ist.
Aber wenn jemand - der es selbst eigentlich ablehnt - mit dogmatischen Äußerungen, die nach boolscher Algebra schlicht "unwahr" sind, nur so um sich wirft, dann hört jede Diskussion auf.
Das Beispiel mit der Liste (Combie. hinten anfügen macht keinen Sinn, weil...s.o.) ist nicht das einzige, aber besonders treffend.

In jeder Grundvorlesung Informatik wird auf die Bedeutung von states und state-change hingewiesen.
Ein üblicher Weg ist, die Klassen und ihr Zusammenwirken in UML zu erstellen, um sich dann den Code generieren zu lassen.
Wenn ich sage: Tasks ohne states und damit ohne den Zustandwechsel  der states - ist Unsinn und unbrauchbar - dann ist das nicht auf meinem Mist gewachsen, sondern lange Bestandteil der Informatik. In JEDEM Buch dazu nachzulesen.
Und es ist KEINE Beleidigung, sondern Grundwissen!
Ein Multitasking System ohne einen TaskControlBlock mit Informationen wie "state"- egal, ob preemptive oder cooperative - kann nicht wirklich brauchbar funktionieren. Nicht mit Arduino, nicht mit Linux oder was auch immer!

Wenn Combie sich darüber hinwegsetzen möchte - seine Sache.
Aber ich werde nicht dabei mitmachen, wenn das als neues Dogma und Innovation verkündet wird.
Es führt den Leser, der hier lernen möchte, in die falsche Richtung.

Und DAS zu beurteilen - entschuldige, Combie - bin ich weit besser in der Lage.

Vergessen wir nicht den Anfang: Combie hatte ein Problem mit seinem Programm und er hatte große Schwierigkeiten, es zu lösen ...


BTW, Tommy:   Combies Art habe ich als stur und trotzig empfunden, aber ich gehe jede Wette ein, er wird sich dann doch das eine oder andere fachliche Argument näher ansehen. Früher oder später.

Deine Art IST beleidigend ! Deshalb mein Rückzug.
Ich bin auch nur hier, um der berechtigten BITTE von DOC_Arduino nachzukommen, den Code besser herauszuheben.
DU erfüllst meine Mindestanforderungen für den Umgang im Forum nicht.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Tommy56 on Jul 15, 2019, 09:40 pm
Kurz zusammen gefasst: Jeder der nicht Deiner Meinung ist, wird abqualifiziert.
Klarer hättest Du Dich nicht darstellen können.

Gruß Tommy
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 15, 2019, 10:16 pm
@Tommy,
Wer sich aus 2+2 das Ergebnis 5 bastelt: Ja
Beim Programmieren geht es nicht um Glaubensgrundsätze sondern Ergebnisse.
Und ja, ich denke, ich habe mich dementsprechend dargestellt und überlasse es dem Leser, sich seine
Gedanken dazu zu machen.
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Tommy56 on Jul 15, 2019, 10:18 pm
Kein weiterer Kommentar nötig.

Gruß Tommy
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Jul 15, 2019, 10:32 pm
 :o
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Tommy56 on Jul 15, 2019, 10:34 pm
Er hat sich doch damit selbst disqualifiziert.

Gruß Tommy
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: postmaster-ino on Jul 15, 2019, 10:42 pm
... wobei ich sagen muß, daß sowohl die Ausführungen auf Seiner Seite, wie 'der Kram', den combie hier zeigt, Beides für mich sehr interessant ist.
Meiner Meinung nach zeigt BEides auch in 'die gleiche Richtung' - ggf. fehlt mir hier aber die Übersicht.
Eigentlich schade, daß Ihr Euch 'nicht riechen könnt' - soll aber kein Beinbruch sein - werde versuchen, mir von Beiden 'das Beste' abzuzwacken und zum C++/OOP-Gott mutieren ... oder so ;)

MfG
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: combie on Jul 15, 2019, 11:17 pm
Quote
werde versuchen, mir von Beiden 'das Beste' abzuzwacken und zum C++/OOP-Gott mutieren ... oder so ;)
So will ich dich agieren sehen!
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: Tommy56 on Jul 15, 2019, 11:23 pm
Ich auch. ;)

Gruß Tommy
Title: Re: Unbeabsichtigter Aufruf einer virtuellen Methode.
Post by: heweb on Jul 16, 2019, 01:34 am
Richtige Reihenfolge beim Start:                           erledigt
Zufügen an Liste (auch in 3 Zeilen):                      erledigt
Dynamisches Zufügen von Tasks:                         erledigt         
Umstellung auf micros():                                        erledigt
Neues taskPause:                                                  erledigt
Code: [Select]

#define taskPause(Task_interval)  ThisTask->InDelay = 1; ThisTask->timeStamp = micros(); return;

Umstellung Scheduler auf neues
taskPause (dadurch viel schneller)                        erledigt
Einführung von state in Task.h                               erledigt

Ging schneller als die Diskussion.
Viel Glück auf dem alten Weg ;)
Und Tschüss                                                          erledigt