Jaki jest sens makra PROTOTYPE, które rozszerza się jedynie do swoich argumentów?

82

Mam plik nagłówkowy, który zawiera

#define PROTOTYPE(s) s

Jaki to ma sens? Wygląda na to, że po prostu zastąpiłby wejście sobą.

Istnieje mnóstwo innych dyrektyw wokół niego, ale jedynym, który wydaje się mieć żadnego wpływu po prostu sprawdzić, czy jest zdefiniowana: #ifndef PROTOTYPE. Znalazłem kilka miejsc w plikach nagłówkowych HDF4 że to zrobić: #define PROTOTYPE. Więc nic z tego nie wyjaśnia mojego pytania. Wciąż wydaje się całkiem bezużyteczny.

Oto jak to jest używane:

CS_RETCODE clientmsg_callback PROTOTYPE((
CS_CONTEXT * context,
CS_CONNECTION *connection,
CS_CLIENTMSG *clientmsg));

Jest to część projektu korzystającego z Sybase Open Client. clientmsg_callback jest później używany tutaj:

ct_callback(context, NULL, CS_SET, CS_CLIENTMSG_CB,
                  (CS_VOID *)clientmsg_callback);

Wychodzę z przykładowego programu stąd:

http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc35570.1570/html/clcprgde/clcprgde10.htm

clientmsg_callback jest implementowany później. Myślę, że próbka została pierwotnie napisana z myślą o C, a nie o C ++. Może to ma z tym coś wspólnego?

Charlie Elverson
źródło
6
Czy są w pobliżu dyrektywy #if/ #ifdef/ #ifndef/, w #elsektórych zamiast tego może mieć inną definicję? Może to mieć znaczenie, gdy jest używane w innych makrach, zwłaszcza w pobliżu #lub ##. Może to być tylko styl komentowania. Za mało kontekstu, aby naprawdę odpowiedzieć.
aschepler
Ogólnie rzecz biorąc: ponieważ ktoś może mieć powód, by chcieć się zmienić PROTOTYPE. Jeśli widzisz w kodzie dziwne definicje, które wydają się bezużyteczne, pomyśl o potencjalnej elastyczności, jeśli ktoś chciałby coś wygodnie zmienić.
Apollys obsługuje Monikę

Odpowiedzi:

130

W dawnych czasach naprawdę, bardzo wczesnego C, nie było czegoś takiego jak prototyp. Listy argumentów funkcji pojawiają się po nawiasach funkcji, na przykład :

square(x)
int x;
{
int y = x * x;
return y;
}

Obecnie argumenty są oczywiście umieszczone w nawiasach:

square(int x)
{
int y = x * x;
return y;
}

Zwróć uwagę na „brakujący” typ zwrotu; Funkcje C używane do niejawnego zwracania int, i tylko wtedy, gdy potrzebny był inny typ zwracania, trzeba było powiedzieć, co to jest.

Deklaracje funkcji miały jeszcze inny zestaw reguł. Deklaracja funkcji w K&R C (stara wersja) nie miała argumentów:

int square();

A prototypy funkcji w ANSI C mają listę argumentów:

int square(int x);

Podczas przejścia ludzie używali zwariowanych makr, aby mogli kompilować w obie strony:

int square(PROTOTYPE(int x));

Z

#define PROTOTYPE(s)

która rozwinie się do pierwszej wersji.

Z

#define PROTOTYPE(s) s

rozszerzyłby się do drugiego.

Jeśli chodzi o „dodatkowe” nawiasy w kodzie w pytaniu, są one potrzebne, gdy na liście argumentów jest więcej niż jeden argument. Bez nich wywołanie makra ma więcej niż jeden argument, więc nie dopasuje makra zdefiniowanego tylko z jednym argumentem:

PROTOTYPE(int x, int y)   // error: too many arguments
PROTOTYPE((int x, int y)) // ok: only one argument (enclosed in parentheses)
Pete Becker
źródło
10
Łał. Powiew przeszłości. Jedną z moich pierwszych prac w oprogramowaniu było usunięcie tego materiału z istniejącej bazy kodu. Zbudowałem zdrowy szacunek dla zestawu uniksowych programów modyfikujących tekst w jednej linijce.
user4581301
15
Nie mam zrozumienia, jak działają te definiuje, w jaki sposób #define PROTOTYPE(s)z wejściem int x;zostaje przekształcony x? Wygląda na to, że zawija się do pustego sznurka
Ferrybig
3
@Ferrybig - przepraszam, pomyliłem rzeczy. W ten sposób definiuje się prototyp . W K&R C prototyp nie miał argumentów, aw ANSI C ma styl listy argumentów, do którego przywykliśmy.
Pete Becker
1
Tę praktykę można również zobaczyć w zlib.hnagłówku z biblioteki zlib z OF()makrem: github.com/madler/zlib/blob/master/zlib.h
Paul Belanger
@PaulBelanger Rzeczywista definicja znajduje się w zconf.h.
SS Anne
16

Makra takie jak to byłyby używane w prototypach w pliku nagłówkowym, aby umożliwić coś takiego:

int foo PROTOTYPE((int bar));

Gdyby wykryto ANSI C ( __STDC__zdefiniowane jako 1), rozwinęłoby się to do:

int foo(int bar);

Jeśli ANSI C nie zostanie wykryty, rozwinie się do:

int foo();

co było powszechne przed standaryzacją C.

Niektóre biblioteki nadal to robią; jeśli zajrzysz do środka tcpd.h(jeśli masz to dostępne), zobaczysz:

/* someone else may have defined this */
#undef  __P

/* use prototypes if we have an ANSI C compiler or are using C++ */
#if defined(__STDC__) || defined(__cplusplus)
#define __P(args)       args
#else
#define __P(args)       ()
#endif

To dobrze to wyjaśnia.

Jeśli chodzi o podwójne nawiasy, __P(arg1, arg2)dałoby błąd składniowy (przekazanie zbyt wielu argumentów do makra), podczas gdy __P((arg1, arg2))byłoby w porządku (tylko jeden ujęty w nawias).

Jest to podobne do __extension__((...))GNU C. W kompilatorach innych niż GNU wystarczy #define __extension__(unused)mieć pół-przenośny kod, ponieważ podany jest tylko jeden „argument” zawinięty w nawiasy.

SS Anne
źródło