2013-04-24 12 views
5

Szukałem makra, które będzie przypominało z-konstrukt. Wykorzystanie powinno być coś takiego:"z" makro w C

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

może to być przydatne dla innych celów.

wymyśliłem tego makro:

#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__) 

Ma 3 zagnieżdżonej pętli, ponieważ powinien on:

  1. inicjalizacji licznika pętli (tylko C99, oczywiście)
  2. Ewentualnie zainicjowania zmiennej _onenter (taki jak with (int fd=open(..), close(fd)))
  3. Zezwól break wewnątrz bloku kodu. (continue zbyt wolno. A makro mogą być dostosowane do assert() go)

użyłem go w kodzie dla XV6 OS i wydaje się całkiem użyteczne.

Moje pytanie brzmi - jakie są najgorsze problemy z takim makro? Mam tu na myśli, oprócz samego użycia makra C (szczególnie takiego, który implementuje nowy konstruktor sterujący przepływem).

Dotychczas odkryli tych wad/problemów:

  1. Brak wsparcia dla return lub goto (ale może to zaoszczędzić trochę goto S w kodzie jądra)
  2. brak wsparcia dla błędów (takich jak fd < 0). Myślę, że ten jest naprawialny.
  3. gnu89/c99 i więcej tylko (licznik pętli, unikalna zmienna sztuczka nie jest konieczna)
  4. Nieco mniej wydajna niż prosta blokada-odblokowanie. Uważam, że to nieistotne.

Czy są jakieś inne problemy? Czy istnieje lepszy sposób na wdrożenie podobnej konstrukcji w C?

Odpowiedz

6

To makro przeraża mnie. Wolałbym traditional approach using gotos.

Takie podejście jest prymitywne, ale większość programistów C zna schemat, a jeśli nie, to może to zrozumieć, czytając lokalny kod. Nie ma ukrytego zachowania. W konsekwencji jest całkiem niezawodny.

Twoje makro jest sprytne, ale dla większości jest nowością i zawiera ukryte efekty. Nowi współpracownicy musieliby pomyśleć, że reguły takie jak "nie return lub goto poza blokiem" i "break wyrwie się z bloku z, a nie poza otaczającą pętlę". Obawiam się, że błędy będą powszechne.

Saldo przesunąłoby się, gdyby można dodać ostrzeżenia o niewłaściwym użyciu tego konstruktora do kompilatora. With clang, która wydaje się być opcją. W takim przypadku zostaną wykryte nadużycia, a Twój kod pozostanie przenośny dla innych kompilatorów.

Jeśli chcesz ograniczyć się do GCC i Clang, możesz użyć atrybutu cleanup.To sprawi, że Twój przykład wyglądać tak:

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

I unlock zostanie wywołana ze wskaźnikiem do zmiennej, gdy wychodzi z zakresu. Jest on zintegrowany z innymi funkcjami językowymi, takimi jak return i goto, a nawet z wyjątkami w mieszanych projektach C/C++.

+0

Oh. Odpowiedź w końcu ... Dziękuję, nie wiedziałem o "oczyszczeniu" - dźwięki przydatne. Czy makroprojekt ma bardziej specyficzne problemy, oprócz swojej niedorzeczności? – Elazar

+1

Brak wsparcia dla 'return' wydaje się być łamaczem transakcji, chyba że pracujesz z ścisłym standardem kodowania przeciwko instrukcjom' return' w środku funkcji. 'Return' w bloku' with' będzie wyglądać niepozornie, ale może siać spustoszenie w czasie wykonywania. – pdw

+0

Rozumiem i zgadzam się, że 'return' jest dużym problemem (więc' cleanup' jest znacznie lepszy). Ale czy "goto" nie wymaga równie surowego standardu kodowania? Jakie są zalety goto w tym zakresie? – Elazar