Article View: pl.comp.lang.c
Article #267815Re: Ciemna strona C++
From: Marcin ‘Qrczak’ Kowalczyk
Date: Sun, 25 Nov 2007 14:18
Date: Sun, 25 Nov 2007 14:18
174 lines
11110 bytes
11110 bytes
Dnia 25-11-2007, N o godzinie 10:46 +0100, Seweryn Habdank-Wojewódzki
pisze:
> Teraz program w C++ odpalam i on elegancko ładuje zarówno libstdc++, jak
> i moją bibliotekę. W C++ oczywiście muszę mieć header, żeby kompilator
> wiedział co zawołać.
>
> Teraz mam pytanie jaka funkcja systemowa to wykonuje i czemu ten .so
> umie być obsługiwany jak C++, a nie jak C?
Nie rozumiem pytania. Pod Linuxem biblioteki C++ są obsługiwane tak samo
jak biblioteki C, tymi samymi mechanizmami. Linker nie musi rozróżniać,
czy ma do czynienia z C, czy z C++.
Jedyna różnica jest taka, że biblioteka C++ może wymagać inicjalizacji
zanim wywołamy jakąś funkcję z niej i podobnie może zawierać kod do
wykonania przy zamykaniu programu.
> To nie jest dlopen w wersji udostępnianej dla ludu, chociażby dlatego,
> że tamten (tajemniczy) cxxdlopen umie rozmanglować nazwy, wie co to
> jest klasa w C++ itd.
Nieprawda. Nazwy C++ nie są rozmanglowywane ani przy normalnym działaniu
programu, ani w kompilatorze (poza takimi narzędziami jak objdump i gdb,
gdzie zdemanglowane nazwy są pokazywane użytkownikowi), a manglowane są
tylko przez kompilator, nie w czasie działania. Linker nie wie, co to
jest klasa C++. Nie ma żadnego cxxdlopen, używany jest zwykły dlopen.
Masz jakieś fałszywe wyobrażenie na temat linkowania C++ (to już wyszło
parę miesięcy temu), ale nie wiem, na czym polega nieporozumienie.
> > Pomijam już to, że nawet znając interfejs biblioteki C++ na poziomie
> > języka C++ o wiele trudniej jest zrobić z niej sensowny interfejs
> > w innym języku niż w przypadku Javy albo Pythona. Na przykład interfejs
> > na poziomie C++ nie zawiera informacji o odpowiedzialności za czas
> > życia obiektów (a jest to potrzebne, żeby używać biblioteki C++),
>
> Co to jest czas życia obiektów? W kontekście czegoś takiego?
To jest wiedza o tym, czy jeśli przekażę wskaźnik na obiekt danej
funkcji, to ta funkcja zwolni kiedyś ten obiekt, czy nie. A jeśli
jakaś metoda zwraca wskaźnik, to jak długo mogę go używać oraz czy
muszę go zwolnić. Oraz w jaki sposób należy zwalniać; w C++ to jest
mniejszy problem niż w C, bo destruktory i delete są ustandaryzowanym
interfejsem, a w C każda biblioteka stosuje własne konwencje.
> Przecież w specyfikacji CPythona jest wołem napisane, że ten kto
> alokuje ten zwalnia, python nie potrafi zwalniać pamięci po {C}malloc.
Zwalnianie to nie tylko wołanie free. Z interfejsu na poziomie C w żaden
sposób nie wynika, że jeśli zawołam taką funkcję:
PyObject *PyString_FromString(const char *);
to muszę kiedyś zrobić Py_DECREF na wyniku (chyba że wynikiem jest NULL,
wtedy należy obsłużyć wyjątek), natomiast jeśli zawołam taką:
char *PyString_AsString(PyObject *);
to nie zwalniam pamięci wskazywanej przez wynik (bo ona siedzi wewnątrz
stringa), za to muszę ją skopiować jeśli ma być potrzebna dłużej niż
string. Ta wiedza musi być wyczytana z dokumentacji, więc jej użycie
nie może być zautomatyzowane.
Nie wynika również to, że taka funkcja:
int PyObject_SetItem(PyObject *o, PyObject *key, PyObject *v);
nie zmienia obowiązków kodu wołającego względem zwalniania obiektów
o, key i v, ale taka:
int PyList_SetItem(PyObject *, Py_ssize_t, PyObject *);
wkłada trzeci argument do pierwszego bez zwiększania mu licznika
odwołań, czyli kod wołający ma zawołać na trzecim argumencie o jeden
Py_DECREF mniej niż gdyby nie wołał tej funkcji (chyba że wynikiem
jest -1, wtedy należy obsłużyć wyjątek i samemu zadbać o zmniejszenie
licznika dla trzeciego argumentu). Podobnie jest z różnicą między
PySequence_GetItem a PyList_GetItem, ale niejako dualnie: nie względem
argumentu, tylko względem wyniku, gdzie PySequence_GetItem powoduje
obowiązek zmniejszenia licznika, a PyList_GetItem — nie.
> > ani nie mówi o wyjątkach, które dana funkcja może rzucić (a jest to
> > potrzebne, żeby użyć tej funkcji i przetłumaczyć dowolny idący z niej
> > wyjątek na inny język, w przeciwieństwie do Javy, kiedy to wystarczy
> > złapać Throwable i rozpoznać typ w czasie wykonania).
>
> Rozumiem. I kto tłumaczy te wyjątki? Maszyna wie że JRantajWyjątek
> ma przenieść na PyWywaliłSięSystem?
Można nawet nie tłumaczyć, byle przechować go w takiej formie, żeby po
wyjściu z powrotem do drugiego języka wyjątek był z powrotem taki sam.
Nie wiem, ile tłumaczy Jython. Wiem, ile tłumaczy wiązanie Pythona
z Kogutem: pewna liczba prostych typów jest konwertowana przez wartość
w obie strony (można definiować własne domyślne konwersje), natomiast
inne obiekty są owijane, chyba że już wcześniej były owinięte w drugą
stronę — wtedy są odwijane. To nie jest specyficzne dla wyjątków;
dotyczy większości sytuacji, w której obiekty są przekazywane między
językami. Można też jawnie unikać tłumaczenia w danym miejscu.
Rozpoznanie, jak tłumaczyć, dzieje się w czasie wykonania programu.
W C++ złapać wyjątek nie znając jego typu można tylko w ten sposób:
catch (...)
ale wtedy jedyne, co można zrobić z takim wyjątkiem, to rzucić go
dalej zanim blok catch się skończy. Nie można zapisać go w zmiennej,
zakończyć ten fragment kodu, a w innym miejscu odczytać go z tej
zmiennej i rzucić dalej. Przy czym to jest głupie ze strony C++,
bo w odróżnieniu od przekazywania argumentów funkcji itp. tutaj
język tak czy siak musi mieć dla siebie podobny mechanizm, ale nie
udostępnia go programiście. Można było zrobić to inaczej bez zmiany
charakteru języka, np. ustalić, że wyjątkami mogą być tylko podklasy
danej klasy zawierającej coś wirtualnego, że typem wyjątku jest
dynamiczny typ obiektu wyjątku, a nie statyczny typ argumentu throw,
i dać programiście jakąś kontrolę nad czasem życia obiektu wyjątku
(teraz jest niszczony na końcu bloku catch i nie można temu zapobiec).
Java nie ma tych problemów. Python zresztą też.
> Jeśli może używać CPythona to jest tylko zasługa tego co jest zawarte
> w plikach .so włączanych co CPythona.
Nie. To jest również zasługa plików nagłówkowych CPythona, dokumentacji
CPythona, zaglądania do źródeł CPythona (tam gdzie dokumentacja nie była
wystarczająco jasna) oraz mojej pracy.
Wynik ma być nie tylko wygodny, ale i bezpieczny, czyli bez zachowania
niezdefiniowanego. Gołe API CPythona dopuszcza mnóstwo zachowania
niezdefiniowanego, a moje owinięcie go — z założenia przynajmniej — nie
(modulo ewentualne błędy, jakie popełniłem ja bądź autorzy Pythona;
chodzi o podstawową odporność na błędy użytkownika wiązania).
> > - rozkład pól w strukturach (nazwy pól, typy, ich kolejność)
> > i metod w klasach (w przypadku C++)
>
> A po co?
Żeby się do nich odwołać. Skąd implementacja wiązania międzyjęzykowego
ma wiedzieć, że jeśli program w drugim języku chce się dostać do pola
"st_mtime" obiektu typu "struct stat", to zaczyna się ono pod offsetem
64 i ma typ time_t? W pliku *.so tej informacji nie ma.
> > - typy argumentów funkcji (w przypadku C; w przypadku C++ można
> > próbować to odmanglowywać), typy wyników funkcji (w przypadku C
> > i C++) i typy globalnych zmiennych
>
> W C++ .so używając cxxdlopen nie musisz tego robić.
Nie istnieje cxxdlopen. W pliku *.so tej informacji nie ma (chyba że
w sekcji dla debuggera, która jest opcjonalna i do której nie ma API,
bo program normalnie z niej nie korzysta). Ze zmanglowanych nazw można
od biedy odczytać tylko informację o typach argumentów, bo tylko ona
jest potrzebna do rozróżnienia przeciążonych funkcji. Zresztą nazwy
typów argumentów nie zawsze wystarczają, bo jeśli argumentem jest
struktura przekazywana przez wartość, to nie wiadomo, jaki jest jej
sizeof. W pliku *.so tej informacji nie ma, bo ona wynika z pliku
nagłówkowego i jest rozproszona w każdym miejscu skompilowanego kodu,
który alokuje tę strukturę (albo np. odwołuje się do pola innej
struktury występującego po tej strukturze) w formie nie nadającej się
do automatycznego odzyskania.
> > - treść funkcji inline
>
> One przecież mają być inline, a to znaczy, że są łączone
> w czasie kompilacji.
Czyli ich treści może nie być w *.so, jest tylko w pliku nagłówkowym;
w dodatku nie w formie skompilowanego kodu, tylko w formie źródłowej.
Dlatego żeby użyć ich z poziomu innego języka, trzeba mieć ze sobą
kompilator C/C++ i zastosować go do kodu włączającego ten plik
nagłówkowy.
W Javie inlajnowaniem zajmuje się kompilator JIT i ten fakt nie blokuje
możliwości dynamicznego rozwiązywania odwołań do metod w dynamicznie
łączonych bibliotekach. Najwyżej takie dynamicznie łączenie,
w odróżnieniu od kompilowania kodu razem, nie skorzysta na inlajnowaniu,
ale przynajmniej w ogóle jest możliwe bez ciągnięcia ze sobą połowy
kompilatora (frontendu).
--
__("< Marcin Kowalczyk
\__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/
Technical Details
Message-ID:
<1196000300.4731.76.camel@qrnik>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.gazeta.pl!newsfeed.pionier.net.pl!news.internetia.pl!newsfeed.tpinternet.pl!atlantis.news.tpi.pl!news.tpi.pl!not-for-mailReferences:
<fghgeo$spf$1@nemesis.news.tpi.pl> <fghsb4$kpq$1@news.dialog.net.pl> <fgo4c7$mtu$1@news.vectranet.pl> <20071106073039.433f4934@Vorago> <fgqn9m$74j$2@news.vectranet.pl> <618565031.20071107113041@pkierski.net> <fgt6sh$jov$1@news.vectranet.pl> <fgtb6j$nur$1@news.vectranet.pl> <fgv6ba$k14$1@bozon2.softax.pl> <fgvkne$u3b$1@news.vectranet.pl> <fh1tjd$dtt$1@bozon2.softax.pl> <fh2lj6$qkh$1@news.vectranet.pl> <fha3as$pap$1@bozon2.softax.pl> <fhaj8u$e02$1@news.vectranet.pl> <fhf09d$ohq$1@bozon2.softax.pl> <fhfi9l$640$1@news.vectranet.pl> <fhurmu$5d9$1@bozon2.softax.pl> <fhv020$8k1$1@news.vectranet.pl> <fi1bs4$nn3$1@bozon2.softax.pl> <fi27ds$gt1$1@kujawiak.man.lodz.pl> <fi458p$fmv$1@bozon2.softax.pl> <fi9ckc$2u5$1@kujawiak.man.lodz.pl> <1195918508.27382.40.camel@qrnik> <fi9hmg$dct$1@news.vectranet.pl> <1195922067.12235.1.camel@qrnik> <fia74t$28b$1@news.vectranet.pl> <1195947820.12235.76.camel@qrnik> <fibfe9$91q$1@news.vectranet.pl>Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.gazeta.pl!newsfeed.pionier.net.pl!news.internetia.pl!newsfeed.tpinternet.pl!atlantis.news.tpi.pl!news.tpi.pl!not-for-mail From: Marcin ‘Qrczak’ Kowalczyk <qrczak@knm.org.pl> Newsgroups: pl.comp.lang.c Subject: Re: Ciemna strona C++ Date: Sun, 25 Nov 2007 15:18:21 +0100 Organization: tp.internet - http://www.tpi.pl/ Lines: 266 Message-ID: <1196000300.4731.76.camel@qrnik> References: <fghgeo$spf$1@nemesis.news.tpi.pl> <fghsb4$kpq$1@news.dialog.net.pl> <fgo4c7$mtu$1@news.vectranet.pl> <20071106073039.433f4934@Vorago> <fgqn9m$74j$2@news.vectranet.pl> <618565031.20071107113041@pkierski.net> <fgt6sh$jov$1@news.vectranet.pl> <fgtb6j$nur$1@news.vectranet.pl> <fgv6ba$k14$1@bozon2.softax.pl> <fgvkne$u3b$1@news.vectranet.pl> <fh1tjd$dtt$1@bozon2.softax.pl> <fh2lj6$qkh$1@news.vectranet.pl> <fha3as$pap$1@bozon2.softax.pl> <fhaj8u$e02$1@news.vectranet.pl> <fhf09d$ohq$1@bozon2.softax.pl> <fhfi9l$640$1@news.vectranet.pl> <fhurmu$5d9$1@bozon2.softax.pl> <fhv020$8k1$1@news.vectranet.pl> <fi1bs4$nn3$1@bozon2.softax.pl> <fi27ds$gt1$1@kujawiak.man.lodz.pl> <fi458p$fmv$1@bozon2.softax.pl> <fi9ckc$2u5$1@kujawiak.man.lodz.pl> <1195918508.27382.40.camel@qrnik> <fi9hmg$dct$1@news.vectranet.pl> <1195922067.12235.1.camel@qrnik> <fia74t$28b$1@news.vectranet.pl> <1195947820.12235.76.camel@qrnik> <fibfe9$91q$1@news.vectranet.pl> NNTP-Posting-Host: hjd150.internetdsl.tpnet.pl Mime-Version: 1.0 X-Trace: nemesis.news.tpi.pl 1196000645 21900 79.187.237.150 (25 Nov 2007 14:24:05 GMT) X-Complaints-To: usenet@tpi.pl NNTP-Posting-Date: Sun, 25 Nov 2007 14:24:05 +0000 (UTC) In-Reply-To: <fibfe9$91q$1@news.vectranet.pl> X-Mailer: Evolution 2.12.1 PLD Linux Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit