dlsym warning castolásnál

Fórumok

Írtam egy pluginbetöltő függvényt (a programnyelvemhez amúgy. Ez mellékes csak nem bírom megállni a dicsekvést). A függvény REMEKÜL MŰKÖDIK, ezt kihangsúlyozom, szóval nincs semmi hiba!

A bajom az amit bele is írtam a kódba megjegyzésként, hogy ha a kód úgy néz ki amint az a kikommentezett sorban vagyon, tehát ha közvetlenül castolom a pointert (ahogyan azt szerintem minden jótét és normális lélek ösztönösen csinálja, mintegy „csuklóból” mert az a logikus) akkor a g++ warningol mint az állat. (De a lefordított kód JÓ AKKOR IS!). A gcc viszont nem is warningol.

A warning üzenet ez:

loadplugin.cpp:72:49: error: ISO C++ forbids casting between pointer-to-function and pointer-to-object [-Werror]
nameptr=(upuplugin *)dlsym(Library,pluginname);
 

 

Maga a programrészlet (mindent kihagytam ami nem releváns. Hogy miféle union az az union upuu az se releváns):

#define usac   uint8_t

typedef void upuplugin(union upuu *th);

char *DLERRORstring=NULL; usac DLERRORflag=0;

void * loadplugin(void *Library, char *pluginname) {
DLERRORstring=NULL; DLERRORflag=0;
upuplugin *nameptr; nameptr=NULL;
if(Library   ==NULL)   {DLERRORflag=1; return nameptr;}
if(pluginname==NULL)   {DLERRORflag=2; return nameptr;}
if(pluginname[0]==0)   {DLERRORflag=3; return nameptr;}
dlerror(); // clear errorcode
// Ez lenne a logikus hívás, de valami ISO szabály miatt ekkor warningot dob a g++: (a gcc viszont NEM!):
//nameptr=(upuplugin *)dlsym(Library,pluginname);
*(void **)(&nameptr)=dlsym(Library,pluginname); // Ha így hívom meg a fenti helyett, nem dob warningot
DLERRORstring=dlerror();
if(DLERRORstring!=NULL) {DLERRORflag=4;nameptr=NULL;}
dlerror(); // clear errorcode
return (void *)nameptr;
}

 

Szóval nem értem, mi a rákért kell warningolnia egyrészt (ismétlem a kód JÓ és remekül működik!); másrészt ha ez a warningolás mégis szükséges valami komoly ok miatt, miért csak a g++ zaklat vele, a gcc miért nem.

Hozzászólások

Dehát explicit választ kaptál a warning message-ből: az ISO C++ tiltja a függvénypointerek és objektumpointerek közti típuskonverziót, mert a két pointerméret nem feltétlenül egyezik (azaz nem feltétlenül fog működni), tehát magyarán a kódod nem szabványos C++, de ettől még a fordító megeszi és lefordítja.

A void * azért csendesíti el a warningot, mert az egy típustalan pointer, tehát bármit jelölhet.
A gcc pedig azért nem warningol, mert a C szabványban nincs ilyen tiltás, mert ott egy ilyen művelet definiálatlan viselkedést eredményez.

Attól tartok, nem értetted meg a problémámat. Én mindezt értem amit írtál, ez eléggé nyilvánvaló. A gondom az, hogy a dlsym pontosan arra való, hogy egy olyan pointert adjon vissza, amit aztán muszáj is castolnunk, hiszen eleve void* a visszatérési értéke, így castolás nélkül nem sok mindenre jó. Tehát már maga a függvény is olyan, hogy szinte elvárja és kiköveteli a castolást. De akkor minek warningolni?

Amennyiben azonban ez mégis annyira nagyon fontos hogy warningoljon a fordító ilyen esetben, hát elvárnám hogy a két fordító működése ebben az esetben egyforma legyen. De nem az.

Szóval, én ebben az esetben a g++ részéről a warningolást felesleges zaklatásnak érzem, ráadásul - bevallom - arra se jöttem rá, hogyan lehetne erről lebeszélnem őt (úgy, hogy az összes többi esetben ne változzék a viselkedése); másrészt viszont nem tetszik hogy a két fordító viselkedése különbözik. Különösen amiatt nem tetszik mert amennyire tudom, a Linux alatt végülis ugyanaz a program végzi mind a C++ mind a C programok fordítását is. Mondjuk erre nem esküdnék meg, de nálam egyetlen csomag telepítése után rendelkezésre áll mindkettő, akkor pedig eléggé sanszos a dolog.

Szóval, valamiképp inkonzisztensnek érzem a dolgot. Különben is, 64 bites rendszerről van szó, ahol azért eléggé alapvető hogy a pointerméretek azonosak. Én legalábbis nem mondtam neki sehol olyasmit hogy ne úgy legyen.

> A gondom az, hogy a dlsym pontosan arra való, hogy egy olyan pointert adjon vissza, amit aztán muszáj is castolnunk, hiszen eleve void* a visszatérési értéke, így castolás nélkül nem sok mindenre jó. Tehát már maga a függvény is olyan, hogy szinte elvárja és kiköveteli a castolást. De akkor minek warningolni?

Mert fordító nem lehet tekintettel arra, hogy a dlsym visszatérési értékének castolása egy (POSIX-) specifikus eset, mert maga a C++ nyelv szabványa sem lehet tekintettel arra, hogy a POSIX rendszereken egyformák a kód és adattér címek, valamint, hogy adott esetekben ilyen-vagy-olyan castingra van szükség, mert a C++ nem POSIX dependens; pl. egy Harvard archtektúrás mikrokontrolleren is működnie kell, ahol a kód és adatterület teljesen szeparálva van és a címzésük sem feltétlenül egyforma bitszélességen van ábrázolva.

> Amennyiben azonban ez mégis annyira nagyon fontos hogy warningoljon a fordító ilyen esetben, hát elvárnám hogy a két fordító működése ebben az esetben egyforma legyen. De nem az.

Miért kéne, hogy egyforma a működése legyen egy C++ és egy C fordítónak?

> Szóval, én ebben az esetben a g++ részéről a warningolást felesleges zaklatásnak érzem, ráadásul - bevallom - arra se jöttem rá, hogyan lehetne erről lebeszélnem őt (úgy, hogy az összes többi esetben ne változzék a viselkedése); másrészt viszont nem tetszik hogy a két fordító viselkedése különbözik. Különösen amiatt nem tetszik mert amennyire tudom, a Linux alatt végülis ugyanaz a program végzi mind a C++ mind a C programok fordítását is. Mondjuk erre nem esküdnék meg, de nálam egyetlen csomag telepítése után rendelkezésre áll mindkettő, akkor pedig eléggé sanszos a dolog.

Hiába ugyanaz a fordító, más a nyelv, mert az egyik C a másik C++ és ez határozza meg, hogy a fordító hogy fog viselkedni. Amúgy ez a viselkedés jelenleg is vita tárgyát képzi, mint esetleges "defektusa" a C++ nyelvnek.

> Szóval, valamiképp inkonzisztensnek érzem a dolgot. Különben is, 64 bites rendszerről van szó, ahol azért eléggé alapvető hogy a pointerméretek azonosak. Én legalábbis nem mondtam neki sehol olyasmit hogy ne úgy legyen.

Itt a környezet és a fordítóprogram irreleváns, mert ezt a nyelv szabványa követeli meg; a nyelvé, aminek bárhol működnie kell; ld. első bekezdés.

> A void * azért csendesíti el a warningot, mert az egy típustalan pointer, tehát bármit jelölhet.

Nem egészen, a 'void *' generikus adat-pointer, de ANSISO szerint nem kell kompatibilis legyen a kód-pointerekkel. Az a typecast, amit az OP is írt, elnyomja a warningot, de hibás működést okoz(na), ha a kódpointer és az adatpointer különböző méretű lenne. Igen, de akkor nem is lenne dlsym a rendszerben!

A dlsym POSIX-os szabvány, és a POSIX-ban előírás az uniform címtartomány. Ettől függetlenül máshol is lehet implementálni a dlopen/dlsym/dlclose függvényeket (Windows-ra és BS2000-re próbáltam), de csak akkor, ha ott is uniform címtartomány van.

Rendben, igazad van, akkor kérlek csak ennyit vegyél figyelmbe a hozzászólásomból:

Nem egészen, a 'void *' generikus adat-pointer, de ANSISO szerint nem kell kompatibilis legyen a kód-pointerekkel. Az a typecast, amit az OP is írt, elnyomja a warningot, de hibás működést okoz(na), ha a kódpointer és az adatpointer különböző méretű lenne.

> Nem egészen, a 'void *' generikus adat-pointer, de ANSISO szerint nem kell kompatibilis legyen a kód-pointerekkel.

Rendben, elnézést a pontatlan megfogalmazásért.

> Az a typecast, amit az OP is írt, elnyomja a warningot, de hibás működést okoz(na), ha a kódpointer és az adatpointer különböző méretű lenne.

De én nem is azt írtam, hogy nála különböző méretű lenne, vagy, hogy nála nem működne, hanem azt, hogy különböző méretű lehet, ami megint globálisan értendő, függetlenül az ő környezetétől, lévén a kérdés az volt, hogy miért warningol, nem az, hogy ez most nála jó-e, vagy sem...

Ezt folyton visszajön a SO-n is, én így szoktam megfixálni:

funptr= (upuplugin *)(intptr_t)dlsym(Library,pluginname);

Lelkes fiatal kollégák erre mindig rámondják, "de az ANSISO szabvány szerint a kódpointert nem szabad ám így sem!"... Valahogy az elméjüket védő mentális pajzson nem jut át, hogy ahol van `dlsym` (ami többnyire, de nem kizárólag, POSIX), ott uniform címtartomány is van... (Különben lenne dlsym_function meg dlsym_data.)

Én nem mondtam, hogy nem szabad és ne csinálja, vagy, hogy a POSIX-rendszereken nem egyformák a pointerek és nem fog működni (a "a két pointerméret nem feltétlenül egyezik (azaz nem feltétlenül fog működni)" kitétel a globális nyelvi megközelítésre vonatkozott, nem a POSIX-környezetekre), csak azt írtam le, hogy miért warningol, mert ez volt a kérdés... :P

Szerkesztve: 2020. 02. 05., sze – 11:05

Cast-olás: C++-os ajánlás a C-s cast nem használata. Erre figyelmeztet a fordító ha megadod neki a -Wold-style-cast paramétert. Cast-olás C++-osan, warn nélkül (miután tudjuk hogy nem NULL):

using func_type = int(int);
func_type * func = reinterpret_cast<func_type>(sym);

Vagy kicsit másképpen:

int (*func)(int); // Függvény ptr definició
func = reinterpret_cast<decltype(func)>(sym);

@hibaellenőrzés:

Azt mondják az okosok, hogy el tudnak képzelni olyan architektúrát, ahol a nullás címre töltődik be a shared object, és pont nincs semmilyen data-header sem, hanem a legelején van a keresett függvény, vagyis a nullás címen.

Off: BS2000-ben 24-bites módban a program default a nullás címen kezdődött, de ha máshová raktuk, akkor meg a memory-pool-ok szerettek odakerülni, vagyis egy NULL-pointer-assignment nem feltétlenül állította meg a programot. Ezért hozzálinkeltünk a főprogramhoz egy kis Assembly részt (PAULA néven), ami lefoglalt egy memory-pool cím=0, size=64KB paraméterrel. Ezt még a programfutás előtt indítottuk debugger-utasítással:


/LOAD-PROGRAM (MAIN,VAGYJO.VAGYNEM.PHA),TEST=AID
/%SET %PC INTO %1G
/%SET %@(C=PAULA) INTO %PC; RESUME
/%SET %1G INTO %PC
/RESUME