Forum: PC-Programmierung Fortschrittsanzeige: Anzahl der Aufrufe einer Funktion zur Compile Zeit zählen


von Paulchen Panther (Gast)


Lesenswert?

Hallo,

ich habe öfters Programme (Konsolenanwendung, C, Visual Studio), die 
viele Sekunden bis einige Minuten dauern. Zum Beispiel Anwendung um 
einen µC zu flashen.
Dabei möchte ich dem Benutzer alle paar Sekunden eine Rückmeldung geben 
um anzuzeigen, dass es einen Fortschritt gibt und dass das Programm 
nicht einfach abgestürzt ist. Ich persönlich mag möglichst detaillierte 
Ausgaben. Die Programme werden auch von "Laien" verwendet, die eine 
möglichst einfache Ausgabe wünschen.

Meine Idee ist, einfach eine Prozentangabe anzuzeigen. Sie soll den 
Fortschritt für den kompletten Programmablauf anzeigen. Nach jeder 
Funktion, die etwas länger dauert (z.B. 1 Sekunde) wird sie 
aktualisiert.
Mein Problem ist eine möglichst geschickte Implementierung, die ich 
immer wieder verwenden kann.

Beispiel Flashen: Es gibt die Funktionen Connect(), Erase(), Write() und 
Verify().
Da es 4 Stück sind, würde die Prozentangabe nach jeder Funktion um 25% 
steigen. Eine Lösung wäre natürlich eine Funktion ShowProgress(), die 
mit jedem Aufruf eine statische Variable um 25 erhöht und sie anzeigt. 
Allerdings sind die 25% von der Anzahl der Funktionen abhängig und ich 
müsste sie jedes Mal ändern, wenn ich eine neue Funktion hinzufüge. Um 
bei dem Beispiel zu bleiben, wenn die Funktion Reset() hinzukommt, 
dürfte ShowProgress() die Variable nur um 20 erhöhen.

Gibt es ein Möglichkeit zur Compilezeit die Anzahl der Aufrufe zählen zu 
lassen? Eine Art define COUNTOF(ShowProgress) wie
1
__LINE__
 oder
1
__FILE__
? Oder sonst eine Möglichkeit?

ShowProgress würde so aussehen:
1
ShowProgress(void)
2
{
3
   static unsigned int Progress = 0;
4
   unsigned int Percentage;
5
   
6
   Progress++;
7
   Percentage = (unsigned int) ( (100 * Progress) / COUNTOF(ShowProgress));
8
   printf("Fortschritt: %u\%  ", Percentage);
9
}

Gruß
PP

P.S.: Ich weiß, dass die Funktionen unterschiedlich lang dauern und sich 
dadurch die Prozentzahl nicht linear zu Zeit ist. Das ist aber ok.

P.P.S. Ich weiß auch, dass in diesem Beispiel die Ausgabe so aussieht:
Fortschritt: 25%  Fortschritt 50%  Fortschritt: 75%  Fortschritt 100%
Das Problem werden ich mit \b lösen.

: Verschoben durch User
von Karl H. (kbuchegg)


Lesenswert?

> Gibt es ein Möglichkeit zur Compilezeit die Anzahl der Aufrufe zählen zu
lassen?

Nein. Ist in deinem Fall auch sinnlos.
Derartige Prozentangaben werden an den Daten festgemacht. D.h. der 
Prozentbalken wandert mit dem Anteil der bereits übertragenen Daten.
Für Connect u dgl. fügt man dann einfach einen gewissen Anteil an 
"falschen Daten" in die Anzeige ein, oder macht zusätzlich zum Balken 
noch eine Statusanzeige.

Das Problem: Im Vorfeld feststellen wieviele Daten zu bearbeiten sind. 
Das ist einfach, wenn die Zahl im Programm schon vorhanden ist, aber 
wenn Windows beim Umkopieren von Files erst mal eine Nachdenkpause 
einlegt um die Größe der zu kopierenden Daten zu bestimmen, krieg ich 
regelmässig einen Auszucker.

von Karl H. (kbuchegg)


Lesenswert?

Auf Konsolen wurde zb gerne ein Balken gebildet aus # benutzt.

mit jeden 5% (oder so) kommt ein # dazu.
Ein Log könnte dann zb so aussehen
1
Connecting to Server xyyz
2
Done!
3
Transfering Data (83986 Bytes)
4
  0  10  20  30  40  50  60  70  80  80  100
5
  +---+---+---+---+---+---+---+---+---+---+
6
  |##############

Man sieht: Der Verbindungsaufbau hat geklappt und es wurden bereits ca. 
35% der Daten übertragen.

Der Aufbau ist einfach:
Das Balkenmodul kriegt am Anfang die Maximalzahl und wird danach zb aus 
der Übertragungsfunktion regelmässig mit der bereits übertragenen Menge 
aufgerufen. Daraus errechnet es sich die Prozentzahl und bestimmt 
wieviele # sichtbar sein müssten. Da es darüber Buch führt, wieviele # 
bereits angezeigt werden, kann es daher auch leicht bestimmen, wieviele 
# an die Anzeige angehängt werden müssen und gibt die dann einfach bei 
Bedarf aus.

von Paulchen Panther (Gast)


Lesenswert?

Hallo,

erstmal danke für die schnelle Antwort. Ich habe gerade gesehen, dass 
ich es in µC und Elektronik gepostet habe. Könntest du es nach 
PC-Programmierung verschieben?

Karl Heinz Buchegger schrieb:
> Derartige Prozentangaben werden an den Daten festgemacht. D.h. der
> Prozentbalken wandert mit dem Anteil der bereits übertragenen Daten.
In meinem Fall hat aber nicht jede Funktion Daten, z.B. Erase sendet nur 
den Erasebefehl mit ein paar Bytes, die Ausführung kann aber schon mal 8 
Sekunden gehen.

Karl Heinz Buchegger schrieb:
> Das Problem: Im Vorfeld feststellen wieviele Daten zu bearbeiten sind.
Genau! Ich vereinfache das Problem in dem ich "Menge der Daten" durch 
"Anzahl der Funktionsaufrufe" ersetze. Die sind sogar zur Compilezeit 
bekannt. Ich könnte sie natürlich von Hand zählen, aber es wäre schön, 
wenn der Präprozessor/Compiler/wer auch immer das für mich übernehmen 
könnte.

Gruß
PP

von Paulchen Panther (Gast)


Lesenswert?

Habe gerade erst das Post von 13:33 gesehen:

Karl Heinz Buchegger schrieb:
> Connecting to Server xyyz
> Done!
> Transfering Data (83986 Bytes)
>   0  10  20  30  40  50  60  70  80  80  100
>   +---+---+---+---+---+---+---+---+---+---+
>   |##############

Ich möchte nicht den Text der Funktion ausgeben, die er gerade ausführt. 
Das Programm startet und der Benutzer sieht nur die Prozentzahl, die 
eben alle paar Sekunden (kann unterschiedlich lang sein) um ein paar 
Prozentpunkte steigt. Sobald sie bei hundert ist, ist das Programm 
fertig.

Gruß
PP

von Karl H. (kbuchegg)


Lesenswert?

Paulchen Panther schrieb:
> Hallo,
>
> erstmal danke für die schnelle Antwort. Ich habe gerade gesehen, dass
> ich es in µC und Elektronik gepostet habe. Könntest du es nach
> PC-Programmierung verschieben?
>
> Karl Heinz Buchegger schrieb:
>> Derartige Prozentangaben werden an den Daten festgemacht. D.h. der
>> Prozentbalken wandert mit dem Anteil der bereits übertragenen Daten.
> In meinem Fall hat aber nicht jede Funktion Daten, z.B. Erase sendet nur
> den Erasebefehl mit ein paar Bytes, die Ausführung kann aber schon mal 8
> Sekunden gehen.

Du musst ja nicht die Anzahl der Daten nehmen. Dem Balkenmodul ist das 
ja völlig schnurz, was die Zahlen zu bedeuten haben. In dem Fall kannst 
du ja auch jede Sekunde einen dazunehmen.

Natürlich kannst du auch die Anzahl der Aufrufe nehmen, klar. Nur ist 
diese Zahl nicht sehr aussagekräftig, ausser als 'Schritt 1 von 4'. Da 
aber deine Schritte sich im Zeitbedarf wesentlich unterscheiden, wird 
das etwas zu grob sein. Benutzer tendieren nämlich auch dazu, eine 
einigermassen gleichmässig wachsende Fortschrittsanzeige zu erwarten. 
Bleibt der Balken für längere Zeit stehen, werden sie nervös.

> "Anzahl der Funktionsaufrufe" ersetze. Die sind sogar zur Compilezeit
> bekannt.

Wie das?

  for( i = 0; i < NrBytes; ++i )
    TransferByte( Bytes[i] );

wieviele Aufrufe von TransferBytes werden erfolgen?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Paulchen Panther schrieb:
> Ich vereinfache das Problem in dem ich "Menge der Daten"
> durch "Anzahl der Funktionsaufrufe" ersetze.
> Die sind sogar zur Compilezeit bekannt.

Sind sie eben nicht, spätestens wenn Funktionen aus einer Schleife 
heraus aufgerufen werden.

von Paulchen Panther (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Paulchen Panther schrieb:
>> Ich vereinfache das Problem in dem ich "Menge der Daten"
>> durch "Anzahl der Funktionsaufrufe" ersetze.
>> Die sind sogar zur Compilezeit bekannt.
>
> Sind sie eben nicht, spätestens wenn Funktionen aus einer Schleife
> heraus aufgerufen werden.

Die Programme sind so aufgebaut, dass die Anzahl der Aufrufe konstant 
und zur Compilezeit bekannt ist.

Danke für das Verschieben!

von Karl H. (kbuchegg)


Lesenswert?

Paulchen Panther schrieb:
> Rufus Τ. Firefly schrieb:
>> Paulchen Panther schrieb:
>>> Ich vereinfache das Problem in dem ich "Menge der Daten"
>>> durch "Anzahl der Funktionsaufrufe" ersetze.
>>> Die sind sogar zur Compilezeit bekannt.
>>
>> Sind sie eben nicht, spätestens wenn Funktionen aus einer Schleife
>> heraus aufgerufen werden.
>
> Die Programme sind so aufgebaut, dass die Anzahl der Aufrufe konstant
> und zur Compilezeit bekannt ist.

Na dann zähl sie.

Mir kommt echt vor, du machst hier aus einer Mücke einen Elefanten.

von Paulchen Panther (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Mir kommt echt vor, du machst hier aus einer Mücke einen Elefanten.

Zugegeben, ich könnte es auch von Hand machen ;-)
Ich dachte vielleicht hat hier jemand ne simple Lösung. Schließlich bin 
ich nicht der erste, der eine Fortschrittsanzeige programmiert.

Gruß
PP

von Karl H. (kbuchegg)


Lesenswert?

Paulchen Panther schrieb:
> Karl Heinz Buchegger schrieb:
>> Mir kommt echt vor, du machst hier aus einer Mücke einen Elefanten.
>
> Zugegeben, ich könnte es auch von Hand machen ;-)
> Ich dachte vielleicht hat hier jemand ne simple Lösung.

Wozu?
Im Allgemeinen Fall ist es nicht möglich diese Anzahl festzustellen.

> Schließlich bin
> ich nicht der erste, der eine Fortschrittsanzeige programmiert.

Weil alle anderen das anders machen als du.

von Karl H. (kbuchegg)


Lesenswert?

1
ShowProgress(void)
2
{
3
   static unsigned int Progress = 0;
4
   unsigned int Percentage;
5
   
6
   Progress++;
7
   Percentage = (unsigned int) ( (100 * Progress) / COUNTOF(ShowProgress));
8
   printf("Fortschritt: %u\%  ", Percentage);
9
}

das ist nicht allgemein genug
1
void ConfigProgress( int min, int max, int percent, char TickCharacter )
2
{
3
  ...
4
}
5
6
void UpdateProgress( int actNumber )
7
{
8
  ...
9
}

so wäre das allgemein verwendbar.
Am Anfang sag ich dem Balken die Grenzen in denen sich der Wert, den er 
in UpdateProgress bekommen wird, bewegen wird. Ich werd ihm 
wahrscheinlich auch noch mitteilen in welchen Prozentabständen ich ein 
Tickmark haben will und eventuell welches Zeichen dafür verwendet werden 
soll.
Und in weiterer Folge ruf ich dann UpdateProgress mit der jeweiligen 
Zahl auf.

von Karl H. (kbuchegg)


Lesenswert?

Edit:
Ob das dann ein Balken oder eine Prozentzahl ist, ist gehupft wie 
gesprungen. Prozentzahlen funktionieren genau gleich, nur dass ich beim 
Config Call dann eben noch die Beschriftung mit dazunehme.

von Paulchen Panther (Gast)


Lesenswert?

Danke für die schnellen Antworten.

Ich werde die Aufrufe dann von Hand zählen.

Gruß
PP

von Hans M. (hansilein)


Lesenswert?

Du kannst natürlich eine Klasse ProcessingStep schreiben, davon dann 
abgeleitet deine einzelnen Schritte.
Die baust du in deine Liste, iterierst darüber und aktualisierst in der 
gleichen schleife die Anzeige.

so etwa:
for(int i =0;i<steps.length;i++){
  steps[i].run();
  Percentage = (unsigned int) ( (100 * Progress) / steps.length);
  printf("Fortschritt: %u\%  ", Percentage);
}

von Tom K. (ez81)


Lesenswert?

Ging es nicht um C?

Wenn man mit make arbeiten würde, ginge das beim Compilieren z.B. so 
(nur aus dem Kopf hingepfuscht):
1
/* balken.c */
2
#include "steps.h"
3
void notifyBalken(void)
4
{
5
    static int steps = 0;
6
    ++steps;
7
    printf("%d von %d Schritten\n", steps, NUM_OF_STEPS);
8
}
1
/* balken.h */
2
#define __STEP__ notifyBalken();
3
void notifyBalken(void);
1
/* bla.c */
2
#include "balken.h"
3
void doStuff(void)
4
{
5
    __STEP__
6
    doSomething();
7
    __STEP__
8
    doMore();   
9
}


Makefile:
1
steps.h: bla.c blubb.c
2
  echo -n '#define NUM_OF_STEPS ' > steps.h
3
  grep '__STEP__' bla.c blubb.c | wc -l >> steps.h

Mit VS, benutzerdefinierten Buildschritten und Windows wird das 
allerdings aufwendiger sein.

Grüße,
Tom

von Tom K. (ez81)


Lesenswert?

Bei genauerem Nachdenken funktioniert das natürlich nicht mit Schleifen. 
Und kommentierte Schritte werden auch fälschlich mitgezählt.

von Paulchen Panther (Gast)


Lesenswert?

Tom K. schrieb:
> Bei genauerem Nachdenken funktioniert das natürlich nicht mit Schleifen.
> Und kommentierte Schritte werden auch fälschlich mitgezählt

Danke für das Beispielprogramm. Es scheint wohl keine immer 
funktionierende, einfache Lösung zu geben außer: Von Hand zählen und die 
Anzahl in einem Define einzutragen.

Vielen Dank für eure Beiträge!

von Chris (Gast)


Lesenswert?

Mach das doch wie FTP,... je xxx Datenmengen ein Hash, keine Prozente.
Wenn du magst, ev. jede Sekunde noch einen Punkt während der Erase oder 
auch
ein E anstelle des #. Die Benutzer merken sich wie lange das dauert und
wissen auch ob ein langes Programm oder ein kurzes gefläsht wird.

von Hans M. (hansilein)


Lesenswert?

Wenns sein muss kann man meine Lösung auch auf c umfrickeln, 
wahrscheinlich am besten mit einem array von Funktionszeigern.

von Frank M. (aktenasche)


Lesenswert?

C# + backgroundworkerthread ;)

da haste sogar ne gui die noch reagiert wenn irgendwas passiert ohne 
groß rumzugedönsen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.