Article View: pl.comp.lang.c
Article #267816Re: Ciemna strona C++
From: Seweryn Habdank-Wojewódzki
Date: Sun, 25 Nov 2007 21:19
Date: Sun, 25 Nov 2007 21:19
265 lines
15415 bytes
15415 bytes
Witam
Marcin ?Qrczak? Kowalczyk wrote:
>> 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++.
No zatem czemu po użyciu dlopen w kodzie należy w zasadzie wszystko tworzyć
od zera (extern "C", funkcje trampolinowe, fabryka obiektów), a kiedy mam
swoje .so wołane przez mój program w C++ nie mam ANI JEDNEGO Z TYCH cudów.
Nie rozumiem, co należy zrobić, aby w swoim programie móc użyć dlopen i nie
musieć się bawić w C. Jak wiesz to powiedz. Jak chcesz wyślę Ci kod demo o
co mi chodzi.
> 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.
Odnoszę wrażenie, że nie tylko.
> 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.
To powiedz jak?
> 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.
Na tym, że ja wiem co mi działa i próbuję zrozumieć dlaczego, a gro robi na
odwrót -- zafixuje się, że coś się nie da zrobić i nawet nie próbują
walczyć z tematem.
>> 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ć.
Ale kto Ci to udostępnia?
Nie wiedzę tego w innych językach. Połączenie C <-> Python wymaga dziwadeł.
Java <-> C# no super, "wszystko" jest rozwiązane (?).
A jest płacz, że im API C++ nie chce się listować w programach. Tylko, że
generalnie wszyscy sprowadzają programy do API C.
Pprzykładem jest decyzja projektowa koderów biblioteki ODE. Oni ODE piszą w
C++, ale API wystawiają do C, aby C koderzy też mogli jej użyć, a potem C++
koderzy muszą robić swoje wrapery w C++ -- to nie jest złe (ja tak
zrobiłem), ale faktem jest, że typowe API C++ programowo nie często używane
jako podstawa nawet w projektach OS, gdzie nie ma problemu z kompilacją i
dziwnościami ABI.
> 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.
No więc czemu narzekasz.
>> 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.
Wiem o czym mówisz? Czepiasz się dziwnych rzeczy.
No więc jak to jest rozwiązane na połączeniu Java <-> C# i CPython <-> Java?
Żaden model obiektu języka X nie pasuje do języka Y. Jython pasuje do J, bo
jest napisany w J. Ale juz załadowanie klas J do CPythona nie jest różowe.
> 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.
No i? Co chcesz pokazać? Bo to co napisałeś rozumiem. Jednak nie kapuję jak
to się ma do C++?
>> > 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.
No ale (tak mi się wydaje) że jeśli złapiesz wyjątek i wyślesz go z powrotem
do C++, to on go zrozumie.
> 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.
Ale przecież Kogut jest dynamiczny (czy nie)? Python też. Oba języki w
podstawie mają C. Tak więc musisz zadbać o poprawne przepisanie
dynamicznego modelu obiektu z Koguta na Pythona. To tak jakbyś w locie
używał słownika + umieszczał słowa w różnych miejscach. Nawet jeśli C++
jest wadliwy, to spróbuj to zrobić dla Ady, trochę będziesz musiał
kombinować, bo Ada również nie jest dynamiczna. Poza tym Python jest
minimalnie funkcyjny więc masz (Kogut) dużo podobieństw, dlatego konwersja
nie jest trudna.
> 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.
Są dwa wyjścia, albo tworzysz sensowną strukturę klas wyjątków dynamicznych,
albo zamieniasz wyjątki na error code. Rozumiem, że na początku był
{J}Object, ale tak jest w J świecie. W C++ nie ma jedynego stwórcy. Więc
aby dynamicznie zrobić niewiadomą, musisz mieć obranego jakiegoś stwórcę
podstawowego, to może być Allah, Siva, Jahve, ktokolwiek.
Czy to Ci przeszkadza? Do tego możesz wszystkie typowe wyjątki konwersować w
to coś. To chodzi o fakt, że takie myślenie nie każdemu odpowiada, że jest
ściśle określona pierwocina typu {J}Object.
> 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).
Ale czemu nie możesz rzucać dynamicznego wyjątku? I łapać go jako wskaźnik,
lub nawet smart_ptr<>. Nie mam pod ręką kodu, ale kiedyś to sprawdzałem i
działa. Potrzebujesz mieć base_wyjatek to go piszesz. Naprawdę, w czym jest
problem aby coś takiego zrobić. Kiedyś mnie zrugałeś, za pomysł wrapowania
statycznych nie dziedziczonych typów w drzewo dziedziczenia, a tu właśnie
możesz mieć to zrobione i nadodatek dzięki szablonom możesz to ładnie
wszystko włącznie z std::wyjątkami ustawić w drzewko i łapać wątek
dynamicznie.
> Java nie ma tych problemów. Python zresztą też.
Tak. Ale to samo możesz zrobić w C++. Przynajmniej ja nie widzę kłopotu --
modulo fakt, że C++ jest najbardziej z nich statyczny.
>> 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.
Twojej zasługi nie mam zamiaru pomniejszać nie o to mi chodziło, ale np. z J
musiałbyś zrobić dokładnie to samo.
> 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).
Rozumiem. Wydaje mi się, że to samo Cię czeka kiedy będziesz się np. wbijał
w C#.
>> > - 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.
Nie ma, hmm ... na pewno masz rację. Ale mam wątpliwości, że ta impotencja
jest cechą fizyczną, a raczej cechą umysłu kogoś kto to tam poukładał.
Mam taki plik .so gdzie wystawiam stosowne API globalne. Reszta jest w
pimplu tak poukładana, że usera tego modułu naprawdę takie detale nie
interesują. W zasadzie mam takiego {J}Beana. Jest ustalone API obiektowe i
to jest wołane. Headery są naprawdę minimalne, nie ma tam żadnych gołych
pól. Z resztą {J}Beany też tak mają.
> 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.
To w jaki sposób mój .so wie jak się wpiąć do programu? Skąd mój program wie
co ma pobierać? Co jest uruchamiane w kodzie? Rozumiem że się nie znam na
kompilatorach i nie wiem co to linkowanie (tego nie miałem na studiach).
Możesz mi powiedzieć, będę wdzięczny.
> 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.
Albo zrobić forward tej funkcji inline do zwykłej funkcji.
> 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.
Może. Nie znam się na JIT. Wiem tyle, że nawet JIT i wszystkie zalety i cuda
zawarte w J dla mnie są pochowane.
Kod moich algorytmów jest wolniejszy i pożera dużo więcej pamięci niż
odpowiednik w C++. Może tam są cuda na kiju, ale dla moich prostych
zastosowań te cuda na kiju się nie wykazują pozytywnie, zresztą dlatego
przestałem zawodowo pisać w J.
> 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).
Tak. Dlatego lepszy od J jest Python i Tcl -- każdy język dynamiczny, bo
wszystko w ogóle może być pisane w locie i nie ma problemu. Tak więc co
oferuje J?
Pozdrawiam.
--
|\/\/| Seweryn Habdank-Wojewódzki
\/\/
Technical Details
Message-ID:
<fico27$h3j$1@news.vectranet.pl>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!newsfeed.gazeta.pl!news.vectranet.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> <1196000300.4731.76.camel@qrnik>Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!newsfeed.gazeta.pl!news.vectranet.pl!not-for-mail From: Seweryn Habdank-Wojewódzki <shw_mail@wp.pl> Newsgroups: pl.comp.lang.c Subject: Re: Ciemna strona C++ Date: Sun, 25 Nov 2007 22:19:32 +0100 Organization: VectraNET - http://www.vectranet.pl Lines: 266 Message-ID: <fico27$h3j$1@news.vectranet.pl> 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> <1196000300.4731.76.camel@qrnik> Reply-To: shw_mail@wp.pl NNTP-Posting-Host: 078088193244.osw.vectranet.pl Mime-Version: 1.0 X-Trace: news.vectranet.pl 1196024712 17523 78.88.193.244 (25 Nov 2007 21:05:12 GMT) X-Complaints-To: usenet@news.vectranet.pl NNTP-Posting-Date: Sun, 25 Nov 2007 21:05:12 +0000 (UTC) User-Agent: KNode/0.10.5 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit