(kieg: asszem az utolsó sor tévedés volt, mivel eleve az sem világos, hogy mit is kellene jelentsen Win16-ban az INT_PTR: Oké egy olyan intet, amiben elfér egy pointer. De milyen pointer: code/data, near/far? Akkor legyen modellfüggő?)
akkor legjobb eredményt a WPARAM típussal kapjuk (tudom, az UINT_PTR is jó lenne, ha az a 'lenne' ott nem lenne, vagyis ha a Win16 ismerne UINT_PTR típust)
=Most például azt mondja nekem a gcc, hogy "cast from pointer to integer of different size" arra, hogy void *p; (int)p... Persze igaza van, de hogyan lehetne megmondani neki, hogy 'tudom, szándékosan csináltam' (Később: azt lett belőle, hogy (int)(intptr_t)p)
Hogy miért fontos ez? Hát, mondjuk valamilyen lelki okból kézileg akarunk előállítani egy olyan paraméter-listát, amit mondjuk egy vsprintf-nek akarunk odaadni, akkor a paramétereket egy intptr_t elemekből álló tömbben lenne érdemes elhelyezni, hogy így emuláljunk egy stack-et.
(Mondjuk egy kicsit el kell ágazni, amikor 64-bites értéket akarunk rátenni erre az emulált stack-re: 32-bit esetén két helyet használunk, 64-bit esetén egyet.)
PS: Valaki kipróbálná nekem Win64 alatt a tesztprogramot?
Kiegészítettem a tesztprogramot még egy vizsgálattal, nevezetesen, hogy hogyan helyezkednek el a vermen a függvényparaméterek (legalábbis a 'variadic' paraméterek, vagyis amiket a stdarg.h-val érünk el, mert ugye egyébként a fordító azt csinál, amit nem szégyell, akár regiszterben is átadhatja a paramétereket). Ahol tudtam tesztelni, ott pont az jött ki, amit józan ésszel is vártunk: a stacken a paraméterek (üzemmódtól függően) 32 vagy 64 bites szavakban tárolódnak, valahogy így:
Ha például egy függvény címével akarunk bűvészkedni, könnyen kaphatunk olyan üzenetet a fordítótól, hogy: warning: ISO C forbids conversion of function pointer to object pointer type
Ilyenkor nincs mese, két lépésben kell cast-olni: volt: void *p = (void *)printf; lett: void *p = (void *)(intptr_t)printf;
A Windows-zal kapcsolatos kínjaimat összefoglalva: 1. Ha nincs szükség arra, hogy egy pointert integerbe csomagoljunk, akkor minden rendben van* 2. Ha van rá szükség, és kifejezetten windows-ra fejlesztünk, akkor az LPARAM a barátunk, amely Win16-tól Win64-ig mindenütt létezik. 3. Egyébként pedig, ha multiplatform fejlesztünk, akkor az intptr_t a barátunk. +1. Ha az adott platformon nincs <inttypes.h>, vagy van, de hiányos, akkor a szövegszerkesztő célirányos használatával orvosoljuk a problémát...
* Például, azért hogy egy pointer növeljünk, nem kell integgeré konvertálnunk: rossz: void *p=&x, *q = (void *)((int)p+42); jó: void *p=&x, *q = (void *)((char *)p+42);
Kicsit jobban körüljárva, ha adott, mondjuk, egy time_t most, mit is tehetünk vele?
1. Binárisan akarjuk file-ba írni/ hálózaton átküldeni. Írjuk ki 8 byte-on (a byte-ordert egyeztessük a file-olvasójával/távoli partnerrel), erre való az (int64_t)most.
2. Számikusan akarjuk kezelni. Használjuk a printf "%lld" szekvenciáját, ami 'long long' típust vár: (long long)most
3. Karakteresen, human-readable formában akarjuk kezelni. A szokásos módszerrel gmtime/locatime + strftime.
Az előző terminológiával a small modell L32 (vagy precízebben: SIP16-L32), a large modell LP32 (SI16-LP32), a win32 LL64 (S16-ILP32-LL64), a unix64 LP64 (S16-I32-LP64), a win64 LLP64 (S16-IL32-LLP64).
Illetve, ha jól látok ilyen korán reggel, erre találták fel az intptr_t és [color]uintptr_t[/color] típusokat. (Sőt, most jut eszembe, hogy a ssize_t és a [color]size_t[/color] is jó, csak az 'intptr_t' nek kifejezőbb a neve.
Do not cast pointers to int, long, ULONG, or DWORD. If you must cast a pointer to test some bits, set or clear bits, or otherwise manipulate its contents, use the UINT_PTR or INT_PTR type.
A kompatibilitás kérdése fel sem ködlik a kis agyában, vagyis az, hogy DOS-ban, UNIX-ban, WIN16-ban, WIN32-ben (mármint az eredeti win32-ben, amit még nem egészítettek ki backport útján ezzel a típussal), BS2000-ben nincs olyan, hogy INT_PTR...
Persze, ha nem akarunk igazunk biztos tudatában fejjel menni a falnak Bill Gatesnek, inkább kompromisszumot akarunk kötni az ördöggel, saját kis 'localdefs.h' állományunkba felvehetünk valami ilyesmit:
#if !defined(_WIN64) && !defined(INT_PTR_DEFINED) #define INT_PTR_DEFINED #if !defined(LLP64) typedef signed long INT_PTR; typedef unsigned long UINT_PTR; #else typedef long long INT_PTR; typedef unsigned long long UINT_PTR; #endif #endif
Mondjuk itt van a EnumFonts, aminek ilyen a paraméterezése:
int EnumFonts( HDC hdc, // handle to DC LPCTSTR lpFaceName, // font typeface name FONTENUMPROC lpFontFunc, // callback function LPARAM lParam // application-supplied data );
Ez minden megtalált font-nál meghívja az EnumFontsProc típusú callback függvényt, amelynek átadja a 'lParam'-ot is. Namostan a derék programozó esetleg annyi információt szeretne átadni, ami nem fér el egy LPARAM-ban sem, hanem inkább egy pointert adna át, amihez persze az kell, hogy sizeof (LPARAM) >= sizeof (void *) legyen. <Kiegészítés: Még jobb példa a WM_CREATE, melynek lParam-ja egy CREATESTRUCT-ra mutató pointer.>
Win16-ban és Win32-ben az LPARAM unsigned long volt, Win64-ben viszont a LLP64 miatt unsigned long long lett belőle.
<Kiegészítés: És hogy mi ezzel a baj? Ha kellően ügyesek vagyunk, tudunk úgy barkácsolni, hogy amit alkotunk, az kellőképpen hordozható legyen (Win16, Win32, Win64), de ha olyan programot szeretnénk írni, amelyik más platformon is kellene menjen, azaz nem használhatunk benne 'LPARAM' meg 'DWORD' típusokat, akkor fellép az a gond, amit az előző hozzászólásban írtam.>
Persze ebből az is következik, hogy aki régebbi fordtókkal is fordtható programot akar írni (vagyis nem akar 'long long'-ot használni), az egyszerűen nem talál olyan integer típust, amibe beleférne egy pointer... illetve, meg kellene nézni, hogy egy ilyen platformon mekkora a ptrdiff_t, size_t és a va_list.