2013-04-24 26 views
5

Ich suchte nach einem Makro, das dem Mit-Konstrukt ähneln wird. Die Nutzung sollte so etwas wie:"mit" Makro in C

with (lock(&x), unlock(&x)) { 
    ... 
} 

Es könnte für einige andere Zwecke nützlich sein.

kam ich mit diesem Makro auf:

#define __with(_onenter, _onexit, v) \ 
    for (int __with_uniq##v=1; __with_uniq##v > 0;)\ 
     for (_onenter; __with_uniq##v > 0; _onexit) \ 
      while (__with_uniq##v-- > 0) 

#define _with(x, y, z) __with(x, y, z) 
#define with(_onenter, _onexit) _with(_onenter, _onexit, __COUNTER__) 

Es verfügt über 3 verschachtelte Schleifen, weil es sollte:

  1. initialisieren Schleifenzähler (C99 nur, natürlich)
  2. Möglicherweise variable _onenter initialisieren (wie with (int fd=open(..), close(fd)))
  3. Erlaube break innerhalb des Codeblocks. (continue ist auch erlaubt. Und das Makro assert() es angepasst werden kann)

ich es für die XV6 OS auf den Code verwendet, und es scheint ziemlich nützlich.

Meine Frage ist - was sind die schlimmsten Probleme mit einem solchen Makro? Ich meine, neben der bloßen Verwendung eines C-Makros (insbesondere eines, das ein neues Kontrollfluss-Konstrukt implementiert).

Bisher haben diese Nachteile/Probleme gefunden:

  1. Keine Unterstützung für return oder goto (aber es kann einige goto s im Kernel-Code speichern)
  2. Keine Unterstützung für Fehler (wie fd < 0). Ich denke, das ist reparierbar.
  3. gnu89/c99 und darüber nur (Schleifenzähler. Der einzigartige variable Trick ist nicht notwendig)
  4. Etwas weniger effizient als einfach sperren-entsperren. Ich halte es für unbedeutend.

Gibt es noch andere Probleme? Gibt es eine bessere Möglichkeit, ein ähnliches Konstrukt in C zu implementieren?

Antwort

6

Das Makro macht mir Angst. Ich würde die traditional approach using gotos bevorzugen.

Dieser Ansatz ist primitiv, aber die meisten C-Programmierer sind mit dem Muster vertraut, und wenn sie es nicht sind, können sie es verstehen, indem sie den lokalen Code lesen. Es gibt kein verstecktes Verhalten. Folglich ist es ziemlich zuverlässig.

Ihr Makro ist clever, aber es wäre für jeden neu und es kommt mit versteckten Fallstricken. Neue Kontributoren müssten Regeln wie "Do not return oder goto aus einem mit Block" und "break wird aus dem mit Block ausbrechen, nicht aus der umliegenden Schleife". Ich fürchte, Fehler wären üblich.

Der Saldo würde sich verschieben, wenn Sie dem Compiler Warnungen für den Missbrauch dieses Konstrukts hinzufügen könnten. With clang, das scheint eine Option zu sein. In diesem Fall würden Missbrauch erkannt und Ihr Code für andere Compiler portierbar bleiben. Wenn Sie sich auf GCC und Clang beschränken möchten, können Sie das Attribut cleanup verwenden.Das würde Ihr Beispiel wie folgt aussehen:

lock_t x = NULL __attribute__((cleanup(unlock))); 
lock(&x); 

Und unlock wird mit einem Zeiger auf die Variable aufgerufen werden, wenn sie den Gültigkeitsbereich verlässt. Dies ist integriert mit anderen Sprachfunktionen wie return und goto und sogar mit Ausnahmen in gemischten C/C++ - Projekten.

+0

Oh. Antwort endlich ... Danke, ich wusste nicht über 'Aufräumen' - klingt sinnvoll. Hat das Makro mehr spezifische Probleme, abgesehen von seiner Schärfe? – Elazar

+1

Die fehlende Unterstützung für "return" scheint ein Deal-Breaker zu sein, es sei denn, Sie arbeiten mit einem strengen Codierungsstandard gegen "return" -Anweisungen mitten in Funktionen. Eine "Rückkehr" in einem "mit" -Block würde unscheinbar aussehen, würde aber zur Laufzeit verheerend wirken. – pdw

+0

Ich verstehe, und stimme zu, dass "Rückkehr" ein großes Problem ist (so ist "Aufräumen" viel besser). Aber braucht 'goto' keinen gleichermaßen strengen Kodierungsstandard? Was sind die Vorteile von goto in dieser Hinsicht? – Elazar