Hallöle,
ich suche gerade nach einer Möglichkeit eine Funktion vor main()
implizit aufzurufen. In C++ kann man eine Variable mit einem
Funktionsaufruf initialisieren. In C geht das wohl nicht!? (man
korrigiere mich, wenn das doch geht).
Lösung (in C++):
Hallo Torsten,
Welches Betriebssystem?
Welcher Prozessor?
Welches C?
Wozu das Ganze?
Mir hat man beigebracht, dass main() der Programmeinstiegspunkt sei. Hat
sich das mittlerweile geändert?
Gruß Knut
Tom schrieb:> Wie portabel muss es sein?
Hi Tom,
nicht portabel würde ich selbst irgend wie hingehackt bekommen. Ich
könnte ja auch von main() aus solche Test-Funktionen aufrufen. Ich
möchte die aber eigentlich gerne später wieder entfernen und dabei gerne
maximal die betroffene Datei anpacken.
mfg Torsten
Hallo Knut,
Knut schrieb:> Welches Betriebssystem?
Kein Betriebssystem.
> Welcher Prozessor?ARM Cortex-M3
> Welches C?
C99
> Wozu das Ganze?
Siehe Betreff.
> Mir hat man beigebracht, dass main() der Programmeinstiegspunkt sei. Hat> sich das mittlerweile geändert?
War schon immer nur die halbe Wahrheit! ;-)
mfg Torsten
So ganz verstehe ich es nicht. Für meine Unit Tests habe ich nie um
main() rum gearbeitet. Was ist so schlimm daran main nur als Einsprung
zu sehen und dein eigentliches Programm main2 oder änlich zu nennen?
Das einzige Manko sind initialisierte Variablen, die vor dem Einsprung
in main initialisiert werden, und nach dem Unit Test ggf Müll sind...
Was ist das eigentliche Ziel?
Schiko schrieb:> Was spricht dagegen eine c++ Datei (für Testzwecke) mitzulinken?
Ich muss im Großen und Ganzen C verwenden, weil die API, die ich
verwenden muss Bugs enthält, die einen C++ compiler ausschließen. Aber
Deine Idee ist voll gut: solange ich keinen API-Header einbinde, sollte
zumindest so etwas funktionieren:
1
void test_crc();
2
void test_blablabla();
3
4
static bool run_tests()
5
{
6
test_crc();
7
test_blablabla();
8
}
9
10
static bool call_init = run_tests();
dann wäre das Ganze nicht mehr so ganz lokal, aber es wäre portable. Mal
gucken, ob der startup code das ganze mitmacht.
Marc schrieb:> So ganz verstehe ich es nicht. Für meine Unit Tests habe ich nie um> main() rum gearbeitet. Was ist so schlimm daran main nur als Einsprung> zu sehen und dein eigentliches Programm main2 oder änlich zu nennen?
Ich möchte eine Zeit lang einfache unit tests mit ins Projekt
integrieren, ohne dafür eigene Projekte in meinen build zu integrieren.
Wäre ich in der Wahl der Mittel frei, würde ich das Projekt so
aufsetzen, dass ich unit tests in separaten executables ausführen kann.
Das Codebeispiel von oben, musst Du Dir am Ende der Datei vorstellen,
die auch die zu testende Funktion (calc_e2e_crc) enthält. Wir mein
Ansinnen so klarer?
mfg Torsten
Knut schrieb:> Und was hat das jetzt mit PC-Programmierung zu tun?
Tut mir leid lieber Knut, aber gibt hier halt kein C99 board. Ich hatte
aber den Verdacht, dass hier viele C99-Experten unterwegs sind.
mfg Torsten
Torsten R. schrieb:> In C++ kann man eine Variable mit einem> Funktionsaufruf initialisieren. In C geht das wohl nicht!?
Was hält Dich davon ab einfach eine Funktion zu schreiben die alle Tests
aufruft und am Ende das Ergebnis irgendwo speichert? Die rufst Du dann
von der main() aus auf. Ich sehe irgendwie Dein Problem nicht. Manchen
Syntaxzucker von C++ gibts halt in C nicht und den muss man dann halt
explizit hinschreiben, aber in Deinem Fall macht das ja so gut wie
keinen Mehraufwand.
Hallo Bernd,
Bernd K. schrieb:> Was hält Dich davon ab einfach eine Funktion zu schreiben die alle Tests> aufruft und am Ende das Ergebnis irgendwo speichert? Die rufst Du dann> von der main() aus auf. Ich sehe irgendwie Dein Problem nicht. Manchen> Syntaxzucker von C++ gibts halt in C nicht...
danke, die Information, dass so etwas in C nicht geht reicht mir ja
schon. Aber ohne zu fragen, hätte ich diese Erkenntnis nicht so leicht
erhalten können.
Der "charm" der C++ Lösung ist, dass die nötigen Änderungen sehr lokal
sind.
mfg Torsten
Hallo Torsten,
ich hatte vor einiger Zeit eine ähnliche Frage gestellt und darauf auch
kaum sinnvolle Antworten bekommen.
Manchen hier fällt es schwer sich Szenarien vorzustellen die neu für sie
sind.
Mir ging es im Prinzip auch darum, meinen Code in einer Art Testumgebung
einzubetten. Diese Umgebung musste vor main() initialisiert werden.
Außerdem war eine Anforderung, dass der eigentliche Code nicht verändert
werden soll. Deswegen kam eine main2(), die in main() aufgerufen wird,
nicht in Frage.
Per makefile konnte zwischen "richtigem" Build (ohne pre-main-Funktion)
und Testbuild (mit pre-main-Funktion) umgeschalten werden.
Die Antwort, kurz und knapp:
Portabel geht nicht weil der Startup-Code, der die main() aufruft, libc-
und OS-abhängig ist.
In meinem konkreten Fall (ATmega mit avr-libc) konnte ich die
init-Sektionen benutzen, die Jörg Wunsch glücklicherweise in der
avr-libc vorgesehen hat.
Per linker-attribute konnte ich meine pre-main-Funktion in eine
init-Sektion meiner Wahl legen.
Hallo,
mal ein Denkansatz, aber da man eh per Präprozessor Teile des Testcodes
ausblendet, warum nicht in der main das gleiche tun (aber Vorsicht, bin
kein Profi-C'ler, jedenfalls noch nicht):
void main(void) {
#ifndef NDEBUG
initUnitTests();
#endif
// main code folgend
}
3 Zeilen mehr in der Main, aber ansonsten wie gehabt und es wird
definitiv als erstes initialisiert.
Cheers,
Joerg
Hallo Joerg,
DerLang schrieb:> 3 Zeilen mehr in der Main, aber ansonsten wie gehabt und es wird> definitiv als erstes initialisiert.
ja, das wäre dann die zu wählende Lösung. Allerdings bleibt es ja nicht
bei den 3 Zeilen. Für jedes Modul, dass so einen Test enthält, braucht
es einen globalen Namen und Prototypen für die Aufzurufenden Funktion.
Es müssen also immer mindestens zwei Dateien angefasst werden.
Kind regards,
Torsten
Torsten R. schrieb:> Knut schrieb:>> Und was hat das jetzt mit PC-Programmierung zu tun?>> Tut mir leid lieber Knut, aber gibt hier halt kein C99 board. Ich hatte> aber den Verdacht, dass hier viele C99-Experten unterwegs sind.
Na jetzt wird mir einiges klar! Ausserdem wird ja die Elektronik auch
vom PC aus programmiert.
Eine Frage hätte ich noch:
Torsten R. schrieb:> nicht portabel würde ich selbst irgend wie hingehackt bekommen. Ich> könnte ja auch von main() aus solche Test-Funktionen aufrufen. Ich> möchte die aber eigentlich gerne später wieder entfernen und dabei gerne> maximal die betroffene Datei anpacken.
Da Du das Ganze sowieso in ein #define einpackst, ist es dann nicht
egal, wo Dein Testcode steht - also ob in ein einer oder mehreren
Dateien?
PS: Aber schicker ist es allemal, wenn man vor der main() noch eine
andere Funktion aufruft.
Grüße Knut
Knut schrieb:> ist es dann nicht> egal, wo Dein Testcode steht - also ob in ein einer oder mehreren> Dateien?
Naja, so lokal wie möglich wäre schon schön...
mfg Torsten
Jetzt hab ich es verstanden.
Die GCC Variante von oben beschrieben ist ganz gut. Etwas fummelig mit
den Prioritäten der Calls. Damit hast du aber zumindest über gcc einen
großen Teil der Plattformen erschlagen.
Mein Vorschlag (ohne Einsatz von __attribute__((constructor)) )
Die Testcase Funktionen schreib ich meist in die C-Datei mit rein (für
Zugriff auf modul-lokale Variablen). Die Declaration schreib ich nicht
ins Header File (da drück ich ma beide Augen zu).
Beispiel:
1
#if UNIT_TEST
2
externvoidtestcase_crc()
3
{...}
4
#endif
Mittels Skript/grep suche ich mir alle "testcase_" fälle raus und
generiere daraus eine testrun Datei:
1
// grep "testcase_" ....
2
externvoidtestcase_crc();
3
externvoidtestcase_xyz();
4
externvoidtestcase_abc();
5
6
externvoidrunAllTests(){
7
testcase_crc();
8
testcase_xyz();
9
testcase_abc();
10
}
Vorteil: Man vergisst keinen testcase und kann die Reihenfolge im
Testrun trotzdem noch manuel anpassen (wobei ein Unittest normalerweise
so geschrieben sein sollte, dass er natürlich andere Unittests nicht
beeinflusst... ergo die Reihenfolge egal sein sollte).
Ergänzung: die Anzahl der passed/failed Testcases zähle ich
normalerweise über die Vergleichsfunktionen mit. Aber das ist abhängig
davon, welche Unittest Lib genutzt wird. Deshalb liefern die testcase_
Funktionen keinen Rückgabewert.