Verständnisprobleme mit __VA_ARGS__
-
ich möchte gerne 2 Makros umsetzen. Vereinfachtes Beispiel:
#define RETURN_IF_FAILED(func) \ { \ func; \ if (getStatuscode() != NO_ERROR) \ { \ return -1; \ } \ } #define RETURN_IF_FAILED(func, returnValue) \ { \ func; \ if (getStatuscode() != NO_ERROR) \ { \ return returnValue; \ } \ }Ich möchte also ein Makro 'RETURN_IF_FAILED' nutzen, den ich einen optionalen Rückgabewert geben kann:
RETURN_IF_FAILED(meineBeispielFunktion()); RETURN_IF_FAILED(meineBeispielFunktion(), MeinRückgabewertBeiFehler);Gruß,
SBond
-
Dieser Thread wurde von Moderator/in SeppJ aus dem Forum C (alle ISO-Standards) in das Forum C++ (alle ISO-Standards) verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
Gibt es einen tieferen Grund, nicht die bewährten Fehlerbehandlungspraktiken zu benutzen? Wenn das C++ sein soll (wonach es trotz der Forenwahl aussieht), böten sich ein paar Exceptions an. Einfacherer Code, übersichtlicherer Code, (in der Regel) keine Laufzeitkosten, und vor allem keine Makros!
-
SBond schrieb:
Ich möchte also ein Makro 'RETURN_IF_FAILED' nutzen, [...]
Nein. Möchtest Du nicht!
Du möchtest ein Problem lösen.
Offensichtlich mit C++.
-
Mit den Makros möchte ich neben einer einfachen Fehlerbehandlung auch eine Log-Funktion integrieren. Natürlich geht es auch ohne Makros, aber das würde den Quellcode weiter aufblähen. Mit Exceptions habe ich aufgrund fehlender Erfahrung bisher noch nicht gearbeitet.
Makros wie 'RETURN_IF_FAILED' kenne ich noch aus Frameworks, mit denen ich mal kurzfristig zu tun hatte. Daher wollte ich so etwas auch in meinem Code zur Verfügung stellen. Nur weiß ich nicht, wie ich optionale Parameter realisiere. Der Code im 1. Post funktioniert z.B. unter g++ problemlos, aber nicht unter vc++.

Wie könnte man das Problem in meinen 2. Post am besten angehen. Wären hier das arbeiten mit Exceptions besser?
Edit: wie ich gerade sehe, finden Makros hier nicht viele Freunde. Sind Makros wirklich so schlimm?

Edit 2: Ja exceptions sehen schon mal sehr interessant aus und werde ich morgen in meinen Quellcode integrieren. Danke für den Tipp. Allerdings ist es für eine lokale Fehlerbehandlung in den einzelnen Funktionen ggf. nicht ideal. ...bin mir da nicht sicher.
viele Grüße,
SBond
-
Es wäre besser, wenn du genau beschreibst, was du erreichen möchtest, anstatt nach Details zu deiner Lösungsidee zu fragen. "Logger" ist schon einmal ein gutes Stichwort. Da ist es zumindest vorstellbar, dass Makros eine passende Lösung sein könnten. Aber Fehlerbehandlung via globaler Fehlerflags? Das sind Techniken, die vor 20 Jahren schon veraltet waren. Wenn wir wirklich wollten, könnten wir dir sicherlich tolle Makros dafür schreiben, aber dir wäre sicherlich besser geholfen, wenn wir dir zeigen, wie du deine Fehler korrekt behandelst. Hier ist ein Artikel über Fehlerbehandlung in C++:
https://www.c-plusplus.net/forum/219864
https://www.c-plusplus.net/forum/219865
Der Artikel ist vermutlich etwas zu anspruchsvoll, falls du noch nie mit modernem Softwaredesign Kontakt hattest und C++ nicht wirklich gut beherrscht (und C++ mit C verwechselst). Wenn du uns deine konkrete Problemstellung beschreibst, dann kann man dies hier auch sicherlich am konkreten Beispiel durchexerzieren.
-
Danke für eure Geduld mit mir.

Ja ich gebe dir Recht. Tatsächlich habe ich bisher Fehlerflags verwendet, da ich es noch von anderen Programmiersprachen (AutoIt, PureBasic, C) gewohnt war. Ich hatte schon die Vermutung, dass es in OOP-Programmiersprachen bessere Lösungen gibt. Da mir zudem die Erfahrung fehlt, weiß ich oft nicht welche Lösungen für welche Probleme am besten geeignet sind. Auch ist mir bewusst, dass ich derzeitig C und C++ gewisserweise mische.Momentan setze ich für meine Masterarbeit ein Protokoll zur sicheren Zeitübertragung um. Habe mich daher für C++ entschieden, um dahingehend auch Wissen anzusammeln. Für die Entwicklung nutze ich drei externe Bibliotheken, für die ich Wrapper schreibe, um diese besser nutzen zu können.
hier mal ein Ausschnitt aus einer Klasse:
int Asn1Wrapper::setAny(ANY_t &destination, const unsigned char &source, int num) { // check parameters if (num <= 0) { setStatuscode(NTS_ERR__INVALID_PARAMETER); return RETURN_ERROR; } // delete existing content clearAny(destination); // allocate and zero-initialize memory destination.buf = (unsigned char*)calloc(1, num); // check memory if (destination.buf == NULL) { setStatuscode(NTS_ERR__NO_MEMORY); return RETURN_ERROR; } // save new values memcpy(destination.buf, &source, num); destination.size = num; // return success setStatuscode(NTS_ERR__NO_ERROR); return RETURN_NO_ERROR; }Vorweg: calloc() benutze ich hier bewusst, da die Lib in C geschrieben ist und gelegentlich einen realloc() anwendet. Ich denke das Mischen von new und realloc ist keine gute Idee. Was man hier auch gut sieht, ist dass ich mit Fehlerflags nicht sparsam bin. Der Grundgedanke ist erstmal, dass ich später jeden Fehler loggen kann. Nun war meine Überlegung, wie ich das ganze Verkürzen kann, da ich ja vor jedem return einen Statuscode setze. Meine Lösung war erstmal Makros. Ich habe mich da unter anderem an einem Framework und OpenSSL orientiert, da ich einfach nichts besseres gefunden habe und wissen wollte, wie größere Softwareprojekte mit Fehlerbehandlung umgehen. Nach dem einsetzen der Makros ändert sich mein Code wie folgt:
int Asn1Wrapper::setAny(ANY_t &destination, const unsigned char &source, int num) { // check parameters if (num <= 0) { RETURN_ERROR(NTS_ERR__INVALID_PARAMETER); } // delete existing content RETURN_IF_FAILED(clearAny(destination)); // allocate and zero-initialize memory destination.buf = (unsigned char*)calloc(1, num); // check memory RETURN_IF_POINTER_NULL(destination.buf, NTS_ERR__NO_MEMORY); // save new values memcpy(destination.buf, &source, num); destination.size = num; // return success RETURN_SUCCESS; }Hinter dem Makro RETURN_IF_FAILED verbirgt sich folgendes:
#define RETURN_IF_FAILED(func) \ { \ int statusCode = 0; \ registerFunc(#func, "in"); \ func; \ statusCode = getStatuscode(); \ registerFunc(#func, "out", statusCode); \ if (statusCode != NTS_ERR__NO_ERROR) \ { \ return -1; \ } \ }Kurze Erläuterung zur gedachten Funktionsweise:
Meine Überlegung war, ob ich alle meine Funktionen mit diesen Makros ausstatte. Das Makro RETURN_IF_FAILED könnte ich nämlich leicht zum Loggen der Funktionen erweitern (so wie bereits im Code). Die Funktion 'registerFunc()' ist momentan noch leer. Diese registriert mit Hilfe des Makros RETURN_IF_FAILED den Aufruf und das Verlassen einer Funktion. Werden mehrere Funktionen kaskadiert aufgerufen, so lassen sich die Funktionsaufrufe verfolgen. In etwa so:call function_1 call function_2 call function_3 exit function_3, exitcode exit function_2, exitcode call function_4 call function_3 exit function_3, exitcode exit function_4, exitcode exit function_1, exitcode...gut das würde ich dann als Baumansicht mit einem Log-Auswertungsprogramm darstellen. Beim Makro RETURN_IF_FAILED gibt es allerdings ein Problem, wenn ich einen benutzerdefinierten Rückgabewert anstatt '-1' senden möchte. Dafür müsste mein Makro sinngemäß so aussehen: RETURN_IF_FAILED(func, <optionalReturnValue>). ...gut auf die Verfolgung der Funktionsaufrufe kann ich eigentlich auch verzichten, aber die Fehler möchte ich schon gerne loggen.
...Möglicherweise ist mein Lösungsansatz nicht so prall. Ich bin mir auch nicht sicher, wie eine saubere Alternative aussehen könnte. Exception sind schon interessant, aber wahrscheinlich nicht für einfache Fehler wie ein ungültiger Parameter sinnvoll. ....ich hätte nicht gedacht, dass mir eine Fehlerbehandlung so viel Kopfzerbrechen bereiten kann

Was kann ich tun?
@SeppJ: nochmals danke für die Links. Hatte ich gestern bereits gelesen, wobei insbesondere der zweite Link mich noch zu sehr fordert.
viele Grüße,
SBond.
-
Überleg Dir ein Objekt, das im Konstruktor call schreibt und im Destruktor exit. Denke, die Basis ist besser.
Die kannste dann übrigens auch noch mit RETURN-Makros ansteuern. Aber sie kann überhaupt immer einschreiten, wenn Du aus Versehen oder wegebn throw in aufgerufenen Funktionen ohne RETURN-Makro aus Deiner Funktion rausgehst.
-
Hallo Leute. Erstmal danke für eure Tipps.

Ich denke der richtige Weg ist in meinem Fall die C++ error exception. So nimmt die eingangs gestellte Frage ein ganz anderes Ende. Abschließend hätte ich noch eine kleine Frage, die sich mir noch nicht beantwortet hat: Wenn ich in ein einem try{}-Block z.B. eine Funktion aufrufe die einen throw wirft, wie kann ich im catch{}-Block herausfinden, ob ich eventuell noch dynamischen Speicher freigeben muss, die in irgendeiner kaskadierten Subfunktion mal alloziert wurde?
-
SBond schrieb:
Wenn ich in ein einem try{}-Block z.B. eine Funktion aufrufe die einen throw wirft, wie kann ich im catch{}-Block herausfinden, ob ich eventuell noch dynamischen Speicher freigeben muss, die in irgendeiner kaskadierten Subfunktion mal alloziert wurde?
Gar nicht.
Versuch es, wie Volkard vorschlägt. Stichwort ist: RAII.