Utilizarea și returnarea ieșirii în macro C (Programare, C, Valoare De Returnare, Preprocesor C)

Michael Mior a intrebat.

Încerc să instrumentez un cod pentru a prinde și imprima mesaje de eroare. În prezent folosesc o macro ceva de genul acesta:

#define my_function(x) 
  switch(function(x)) { 
    case ERROR: 
      fprintf(stderr, "Error!
"); 
      break; 
  }

În mod normal, nu capturez niciodată ieșirea funcției și acest lucru funcționează bine. Dar am găsit câteva cazuri în care am nevoie și de valoarea de returnare a function(). Am încercat ceva de genul următor, dar acest lucru produce o eroare de sintaxă.

#define my_function(x) 
  do { 
    int __err = function(x); 
    switch(__err) { 
      case ERROR: 
        fprintf(stderr, "Error!
"); 
        break; 
    } 
    __err; 
  } while(0)

Aș putea declara o variabilă globală pentru a păstra valoarea de retur a funcției, dar asta arată urât, iar programul meu este multithreaded, așa că este posibil să cauzeze probleme. Sper că există o soluție mai bună.

Comentarii

  • De ce nu o faci o funcție inline? –  > Por mtvec.
  • „Ieșirea” unei funcții nu este același lucru cu „valoarea de returnare” a unei funcții. Vrei valoarea de retur. –  > Por William Pursell.
  • În postarea dvs. lipsesc mai multe marcaje de continuare a liniei. Este posibil să fie o eroare de sintaxă. –  > Por William Pursell.
5 răspunsuri
Jens Gustedt

Acesta este un cod relativ complicat, nu există un motiv prea mare pentru a-l avea într-o macro. Faceți-o inline (C99) sau static (C89) sau ambele, dacă doriți cu adevărat să îl plasați într-un fișier header. Cu orice compilator rezonabil, acest lucru ar trebui apoi să rezulte în aceeași eficiență ca și un macro.

Comentarii

  • Din păcate, o funcție inline nu poate face unele dintre trucurile pe care le poate face un macro, cum ar fi concatenarea identificatorilor (FOO ## _size dintr-un cod generat pe care îl folosesc, de exemplu). În rest, da, vă rugăm să folosiți funcții inline. –  > Por George Hilliard.
  • Un alt lucru util pe care funcția inline nu îl poate face este extinderea __FILE__ și __LINE__ macro pentru a genera un mesaj de eroare acolo unde s-a produs efectiv eroarea. În plus, personal, consider că răspunsurile care le spun oamenilor să nu facă lucrurile pentru care cer o modalitate nu sunt utile, deoarece nu răspund cu adevărat la întrebare. Dacă răspundeți la o întrebare, răspundeți la întrebare. Orice comentariu cu privire la întrebare ar trebui să vină doar după ce se răspunde la întrebare sau să fie doar un comentariu. –  > Por xiay.
  • @logicor, cred că nu înțelegi ce vrei să spui. Întrebarea se referă la returnarea unei valori din astfel de construcții. Aceasta este o caracteristică pe care nu o poți avea doar cu macro-uri în C standard. Dar nimic nu te împiedică să înfășori o macro în jurul o astfel de funcție pentru a vă ocupa și de __LINE__ etc.  > Por Jens Gustedt.
  • @JensGustedt Atunci când înfășurați o macro în jurul funcției, nu vă întoarceți la problema inițială, în care nu puteți returna valoarea din expresia macro? Singurul mod în care văd că funcționează cu funcția inline este trecerea __LINE__ ca argument la funcția inline, ceea ce duce la un cod oarecum urât, IMHO. –  > Por xiay.
  • @logicor, a face acest lucru direct în apel ar fi într-adevăr urât, da. De aceea, aș înfășura un astfel de apel într-un macro care adaugă __LINE__, __FILE__ sau __func__. Acest lucru ar fi complet portabil, sigur din punct de vedere al tipului și eficient. Și vă rog pe toți cei care votați acest lucru, reveniți la întrebarea la care se răspunde aici. Nu există nimic despre aceste macro-uri speciale în întrebare. –  > Por Jens Gustedt.
qrdl

GCC are o caracteristică numită expresii de declarație

Deci, dacă se definește un macro ca

#define FOO(A) ({int retval; retval = do_something(A); retval;})

atunci veți putea să o utilizați ca

foo = FOO(bar);

Comentarii

  • Asta este practic ceea ce am încercat să fac cu cele de mai sus. Cred că @Jens a avut dreptate că o funcție inline se potrivește mai bine nevoilor mele. –  > Por Michael Mior.
  • Acesta ar trebui să fie răspunsul corect. Uneori nu pot folosi funcții inline statice (cum ar fi atunci când am nevoie de „goto”), dar definirea funcției ca macro și folosirea expresiilor de declarație îmi rezolvă problema. –  > Por Leo Lu.
simp76

Un răspuns foarte târziu. Dar totuși. Sunt de acord că funcțiile inline sunt mai bune, dar MACRO-urile oferă o distracție de tipărire destul de plăcută pe care nu o poți obține cu funcțiile inline. Sunt de acord cu @qrdl că puteți utiliza într-adevăr expresii de declarație dacă ați restructurat puțin declarațiile dvs. Iată cum ar funcționa cu un macro –

#define my_function(x, y) ({ 
  int __err = 0; 
  do { 
    __err = function(x, y); 
    switch(__err) { 
      case ERROR: 
        fprintf(stderr, "Error!
"); 
        break; 
    } 
  } while(0); 
  __err; 
})

Comentarii

  • ({ ... }) funcționează în orice compilator ? este o extensie GNU C gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html –  > Por JuanPablo.
  • do-while nu este inutil aici? –  > Por Traummaennlein.
  • Aș da un vot în plus dacă nu ar fi inclus do-while, deoarece este inutil aici. –  > Por Gabriel Staples.
hopia

Îmi pare rău, aceasta este o editare…

  1. Cred că ai nevoie doar de acoladele curly braces. Nu este nevoie de cuvintele cheie do..while
  2. Asigură-te că backslash-urile sunt ultimele caractere de pe fiecare linie (fără spațiu după).
  3. Dacă aveți nevoie să obțineți valoarea err din macro, puteți adăuga un parametru

Astfel:

 #define my_function(x, out) 
      { 
        int __err = function(x); 
        switch(__err) { 
          case ERROR: 
            fprintf(stderr, "Error!
"); 
            break; 
        } 
        __err; 
        (*(out)) = _err; 
      }

Pentru a păstra paradigma C pass-by-reference C, ar trebui să apelați my_function în acest fel:

int output_err;

my_function(num, &output_err);

În acest fel, mai târziu, dacă decideți să faceți din funcția mea_funcție o funcție reală, nu trebuie să schimbați referințele de apelare.

Btw, „Statement Expressions” din qrdl este, de asemenea, o modalitate bună de a face acest lucru.

Jokerul

nu este nevoie să declarați o variabilă dacă funcția dvs. returnează ceva, atunci puteți obține direct acea valoare. De exemplu:

#define FOO(A) do_something(A)

Aici do_something returnează un număr întreg. Apoi, o puteți utiliza cu ușurință ca:

int a = FOO(a);

Comentarii

  • Acest lucru nu funcționează în situația mea, deoarece macroul trebuie să utilizeze și rezultatul din do_something și singura modalitate de a adopta abordarea pe care o sugerați este să apelați do_something de două ori. –  > Por Michael Mior.