Article View: pl.comp.lang.c
Article #267828Re: Ciemna strona C++
From: Sektor van Skijlen
Date: Tue, 04 Dec 2007 11:45
Date: Tue, 04 Dec 2007 11:45
347 lines
17375 bytes
17375 bytes
Dnia Mon, 26 Nov 2007 14:43:32 +0100, Marcin ?Qrczak? Kowalczyk skrobie:
> Dnia 26-11-2007, Pn o godzinie 12:26 +0000, Sektor van Skijlen pisze:
> > Podobny los spotkał przecież Tcl-a, a jest to z języków inspirowanych
> > Lispem (nawet w niewielkim stopniu) jeden z najsensowniejszych.
> W tym przypadku od podróbki wolę oryginał.
To nie jest żadna podróbka. W Tcl-u zastosowano znacznie sensowniejsze reguły
określania co jest symbolem, a co nie. Faktem jest, że przykre są niekiedy
tego konsekwencje (w sensie, dowolny ciąg znaków może potencjalnie być nazwą
komendy lub zmiennej).
> Największy grzech Tcl to
> nieuznawanie wskaźników, czyli konieczność symulowania ich unikalnymi
> symbolicznymi nazwami, nieodróżnialnymi od stringów i bez szans na
> odśmiecanie.
Wskaźnikiem w Tcl-u jest literowa nazwa zmiennej, jaka obowiązuje dla niej w
danej ramce. Owszem, można by zrobić do tego coś w stylu "lepszego
rozróżniania" nazwy pisanej (jako string niczego nie dotyczący) i nazwy
konkretnej zmiennej lub komendy, ale wtedy Tcl zatraciłby swój minimalizm i
pełny dynamizm. Coś za coś.
> No i z dwojga złego wolę składnię Lispa od Tcla. Poleganie
> na eskejpowaniu cudzysłowów i spacji w semantyce podstawowych konstrukcji
> językowych jest chore.
No cóż, to jest typowy problem dla języka, który stosuje zasadę EIAS.
Mnie za to drażni sytuacja, w której język "teoretycznie" oparty o składnię
listy (element1 element2 element3) posiada "słowa kluczowe" i różne
konstrukcje, sterty symboli, zmieniających znaczenie konstrukcji teoretycznie
opartych tylko o listę.
Pamiętam jak swego czasu wzięło mnie na ambicję, żeby choć zapoznać się
gruntownie ze składnią Common Lispa. Jak doszedłem do kolejnego symbolu typu
`#$, który coś kolejnego ciekawego oznaczał w składni języka, to się poddałem.
W Tcl-u przynajmniej nie muszę każdej najmniejszej cząstki tego, co piszę,
pisać w nawiasach (w tym wypadku kwadratowych). Zagnieżdżone nawiasy
kwadratowe nawet na tyle mnie niekiedy drażnią, że specjalnie dorobiłem sobie
komendę pipe:
pipe { lindex $x 0 | split - : | lindex - 1 }
zamiast
lindex [split [lindex $x 0] :] 1
Tak że efektywnie o ile doceniam sensowność wzorowania się Tcl-a na Lispie w
kwestii tych list, w miarę możliwości staram się nie nadużywać tego, a nawet
czasem z tym walczę.
Jak zauważyłem zresztą, nie jestem jedynym - obejrzyj sobie dowolny kod w
Tcl-u i kodu z głęboko zagnieżdżonymi nawiasami (kwadratowymi) znajdziesz jak
na lekarstwo. Dla mnie to najlepszy dowód na to, że ludzie "składni
nawiasowo-drzewiastej" po prostu nie lubią i nie polubią. Inna sprawa, gdy
mamy do czynienia z serią deklaracji, z tym że - niezależnie od tego, czy w
języku koniec linii jest separatorem, czy nie - koniec linii jest tam i tak
naturalnym punktem podziału.
> > > Różnica jest zasadnicza. Bibliotekę Javy można owinąć w interfejs innego
> > > języka programowania polegając wyłącznie na informacji dostępnej w czasie
> > > wykonania. Podobnie jak w wielu innych językach. Można zobaczyć, jakie są
> > > w niej klasy, jakie one mają metody, wywoływać metody po nazwie znanej
> > > w czasie wykonania, przekazywać obiekty nie znając statycznie ich typu.
> >
> > Pod warunkiem, że projekt jest skompilowany javac-em, a nie gcj-em.
> Nieprawda, refleksja działa tak samo w gcj.
> http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/package-summary.html
A, o to chodzi.
Zgadza się, możesz, chociaż to nie zmienia faktu, że daje ci to możliwość
DOSTĘPU, a refleksja daje ci tylko tyle, że możesz "wiązania" dokonać w czasie
wykonywnia (dokładnie to w czasie startu modułu korzystającego z tej
biblioteki).
Nie widzę, jaka tu miałaby być różnica, poza ewentualnie tym, że refleksję w
Javie można owinąć w refleksję w innym języku i na tej podstawie mieć
"interface binding factory".
A i inna sprawa, że przydałoby się mieć coś takiego w C++, co więcej, szczerze
mówiąc wcale nie widzę przeszkód, żeby to zaimplementować. Tylko po prostu
komitet radośnie poprzestał na nic nie dającym type_info i nic nie mówiącym
type_info::name (nie wspominając już o przez nikogo nie używanym
type_info::before).
> > > W C++ niczego takiego nie ma
> >
> > Poprawka: dla C++ nikt do tej pory nie napisał kompilatora kompilującego na
> > taką platformę, która by to udostępniała.
> A w Javie to jest w standardzie, więc jest w każdej implementacji.
W każdej jednej (mówimy na razie o implementacjach tego języka może różnych
producentów, ale wciąż na jedną platformę).
> > Przecież jeśli - dajmy na to - chciałbym wywołać sobie metodę Javy spod
> > skryptu w Tcl-u (Jacl?), to MUSZĘ znać nazwę tej metody, jej właściwości,
> > interfejs, typy argumentów, typ zwracany, posiadać facility do przekształcania
> > danych w razie konieczności itd.
> Nie, dokładnych typów argumentów nie trzeba znać. Na przykład nie trzeba
> w miejscu wołania wiedzieć, czy argument jest typu int, czy long, ani
> nie trzeba w żaden sposób jawnie informować o typie wyniku - on musi być
> odczytywalny automatycznie, skoro jest wyznaczony jednoznacznie przez
> funkcję.
Jeśli mówimy o java.lang.reflection (skoro już podałeś to jako przykład), to
niestety nie masz racji. Tam, żeby dobrać się do określonej metody musisz znać
nie tylko jej nazwę, ale również typy argumentów, które podajesz jako obiekty
typu Class (swoją drogą nie wiem, jak się tam podaje typ int, ale może i jest
coś takiego, jak int.class...).
> Niedopuszczalna byłaby konieczność powiadamiania o dokładnym
> typie (w tym typie wyniku) połączona z zachowaniem niezdefiniowanym,
> jeśli nie trafimy.
Tak, zgadza się - ale dla mnie w tym niedopuszczalność kończy się na UB,
natomiast konieczność powiadamiania o dokładnym typie (ale tylko argumentów,
bo tylko to stanowi o identyfikacji metody) połączona z rzucaniem wyjątku lub
informacji o nie znalezieniu dopasowania jest jak najbardziej pożądana.
> A tak jest przy dlsym w C i C++.
Co ty chrzanisz? Od kiedy to w dlsym musisz powiadamiać o dokładnym typie?
Czy chodzi ci o to, że z dlsym dostajesz void* i masz UB jeśli zrzutujesz to
na nie taki wskaźnik do funkcji jaki tam był podczas kompilacji?
W C++ problem istnieje również, owszem, chociaż nie jest on nie do obejścia (w
C owszem jest). Można by było dorobić w gcc coś typu takiego makra, które po
podaniu odpowiedniego symbolu lub deklaracji zwracałoby tekstowo zmanglowaną
nazwę do wiązania zewnętrznego. Przykładowo (piszę wedle starych reguł, bo mi
tak wygodniej):
typedef int f_t( int, const char* );
f_t* pf = dlsym( lib, CXX_MANGLE( f_t, "f" ) );
Co spowodowałoby, że dlsym będzie szukać symbolu f__FiPCc.
To mogłoby bez większych problemów pozwolić na tworzenie konstrukcji typu:
template <class Ret, class... Args>
Ret (* get_function( std::library l, const char* name )( Args... args )
{
typedef Ret fn_t( Args... args );
return (fn_t*) dlsym( l.handle, CXX_MANGLE( fn_t, name ) );
}
Odrobinę większy problem byłby z metodami, ale to też da się zrobić - wskaźnik
do implementacji dostać z dlsym, a dane delta/index wyczarować podczas
kompilacji na podstawie budowy klasy.
Ok, to wszystko jest jak najbardziej prawda - tylko wyjaśnij mi, dlaczego
nikt z tej refleksji nie korzysta? Pomijam oczywiście różne wynalazki, które
na bazie tego potrafią zrobić obiekt na podstawie jakiejś koślawej definicji w
XML-u. Dlaczego refleksje nie są powszechnie używane?
Przecież refleksje można by zastosować do zaimplementowania jakiejś sensownej
postaci wskaźnika do funkcji, którego w Javie potwornie brakuje. Za pomocą
obiektu typu Method można zapamiętać samą metodę, a tworząc parę (obiekt
metoda) stworzyć "Callable". Wtedy żeby zarejestrować reakcję na zdarzenie
można by było zrobić dosłownie:
button.set_clicked_response( delegate( this.MyClickedResponse( int ) ) );
Pod warunkiem oczywiście posiadania facility do wyduszenia obiektu typu Method
na podstawie deklaracji metody użytej tu powyżej.
Nawet gdyby używać tego w "toporny" sposób:
button.set_clicked_response( this, "MyClickedResponse", int.class );
to i tak jest to nic w porównaniu z
button.set_clicked_response( new Button.ClickedResponse() {
void onClick() {
MyClickedResponse();
}
} );
A ludzie wolą się męczyć. Dlaczego?
> Oraz w niektórych
> prymitywnych FFI do C opartych wyłącznie na dlsym, bez wykorzystywania
> informacji pochodzących z pliku nagłówkowego.
Te pomińmy.
> > Na cholerę mi do tego jest potrzebne iterowanie po wszystkich
> > elementach definiowanych przez bibliotekę!?
> Może być bez iterowania. Byle dało się użyć elementu znając jego nazwę
> (dynamicznie) i wiedząc mniej więcej, skąd pochodzi. Może nie być
> możliwości sprawdzenia, ilu argumentów potrzebuje, byle pomyłka była
> wykryta, a nie skutkowała zachowaniem niezdefiniowanym. Chcę uzyskać
> z danej biblioteki interfejs bez zachowania niezdefiniowanego, bo ono
> nie pasuje do docelowego języka.
Ale nie mów mi, że chciałbyś to jednocześnie móc zrobić nie zagłębiając się w
szczegóły interfejsu tej biblioteki.
> Kogut ma minimalną refleksję. Zresztą w dynamicznie typowanym języku
> granica między refleksją a zwykłym użyciem się rozmywa (statycznie
> typowany język może sobie nazwać refleksją wszystko, co sprawdza typy
> dynamicznie zamiast statycznie, czyli co dla dynamicznie typowanego
> języka jest normą).
Ale nazwy wyszukiwane po stringu to już nie jest coś typowego dla języka
dynamicznie typowanego.
> W Kogucie można:
> - Odwołać się do pola obiektu znając nazwę pola (odwołanie do pola
> jest aplikacją do symbolu). To nie jest refleksja, tylko dynamizm.
Zgadza się. Przecież nazwy symbolu nie podajesz w stringu, tylko nazwa pola
już wcześniej musi być symbolem.
Chyba że się mylę.
> - Odczytać nazwę typu i nazwę nazwanej funkcji.
Jako string? A co z operacją w drugą stronę?
> - Dodać nadtyp istniejącego typu.
Zidentyfikowanego w jaki sposób?
> - Sprawdzać relację podtypu, wylistować nadtypy danego typu.
Znów...
> - Dodać metodę funkcji generycznej dla danych typów dispatchowanych
> argumentów (to też nie jest refleksja).
> - Uzyskać konstruktor i listę nazw pól obiektu typu rekordowego.
> Typ rekordowy to mniej więcej odpowiednik typu POD w C++.
> - Uzyskać obiekt reprezentujący dany moduł (pola tego obiektu to
> zmienne eksportowane przez moduł). Nazwa modułu musi być znana
> statycznie, ale wzbogacenie tego o znalezienie modułu dynamicznie
> byłoby trywialne (z użyciem dlsym).
> - Uzyskać opis aktywnych funkcji na stosie z historią rzuconych,
> nieobsłużonych jeszcze wyjątków, ale bez gwarancji dokładności -
> służy to tylko pokazaniu tego użytkownikowi w ramach debugowania
> przy niezłapanym wyjątku.
> - Wylistować zawartość modułu na poziomie potrzebnym do kompilacji
> kodu, który używa tego modułu. To będzie potrzebne, kiedy będę robił
> interpreter używający skompilowanych modułów swojego hosta z tymi
> samymi typami.
> - Zrobić typ z konstruktorem bądź nazwaną funkcję generyczną na
> podstawie informacji znanej dynamicznie. To są dodatkowe konstrukcje,
> które będą potrzebne do interpretera, bo normalnie te rzeczy tworzy
> się bardziej statycznymi sposobami, np. normalnie nazwa tworzonego
> typu musi być znana statycznie.
> - Monitorować kończenie programu, wypadnięcie nieobsłużonego wyjątku
> poza program, tworzenie wątków, fork procesu.
> - Iterować po istniejących wątkach.
I nadal to samo.
Spoko, że możesz potraktować jako obiekty (wartości etc.) wszystko, co się
rusza w języku, ale jak to identyfikujesz?
> Nie można za to:
> - Odczytać informacji o liczbie argumentów oczekiwanych przez funkcję
> (w ogóle nie musi to być jedna liczba ani nawet skończony zbiór,
> formalnie nie ma takiego pojęcia, choć kompilator statycznie to
> sprawdza kiedy tylko może) ani tym bardziej oczekiwanych typów
> ani typu wyniku.
To nie jest do niczego potrzebne. W Javie (i pożądane by to było również w
C++) jest to konieczne z uwagi na przeciążanie - tylko i wyłącznie.
> - Odczytać informacji o polach obiektu (z wyjątkiem typów rekordowych).
> Obiekty w ogóle nie muszą mieć pól, np. mogą być zrealizowane w C.
I w takim przypadku uważam, że można postawić taki wymóg, że do obiektu nie
możesz się dobrać w ogóle w jakikolwiek sposób nie przewidziany przez twórcę
interfejsu. Z tego względu informacja o delcie pola w przypadku refleksji w
C++ NIE POWINNA BYĆ POTRZEBNA.
> - Skonstruować obiektu danego typu mając tylko ten typ.
Mając ten typ, czyli mając co?
W C++ mając wszystkie potrzebne informacje o typie jesteś w stanie utworzyć
obiekt tego typu (wszystkie informacje, to znaczy również konstruktory i
możliwość ich poprawnego wywoływania).
> - Dostać się do prywatnych składników obiektu albo modułu.
Tego to chyba w żadnej refleksji nie można :)
> - Zmienić czegoś, co jest uznawane za niezmienialne, np. typu
> istniejącego obiektu albo działania istniejącej funkcji.
> - Usunąć nadtypu danego typu ani usunąć metody funkcji generycznej.
Refleksja chyba nie polega na modyfikacji programu w trakcie wykonania?
> - Wylistować podtypów danego typu (nie są nawet fizycznie zebrane
> w jednym miejscu).
Ale wylistować wszystkie typy, które są pochodnymi określonego typu, tylko w
zakresie podanych modułów, już chyba można?
> - Wylistować istniejących metod danej funkcji generycznej.
Na tej samej zasadzie.
> - Wyekstrahować implementacji metody funkcji generycznej dla danych
> argumentów bez jej wołania.
Nie rozumiem.
> To, co można, wystarcza np. do tego, żeby interpreterowi Pythona
> udostępnić funkcjonalność danego modułu Koguta (tudzież zbioru modułów)
> bez konieczności listowania zawartości tego modułu. Obecnie trzeba
> jednak statycznie zaimportować ten moduł, tzn. nie można znaleźć go
> z poziomu Pythona znając dynamicznie jego nazwę.
No ale czy do tego nie potrzebowałeś czasem dorobić odpowiedniej przystawki do
tego w Pythonie?
> > > W C++ nie można też złapać wyjątku i np. przekazać go do innego wątku
> > > bez statycznej znajomości jego typu.
> >
> > Owszem, można. Pod warunkiem oczywiście, że jest wyprowadzony z
> > std::exception.
> Nie można go potem rzucić z powrotem bez wyliczenia każdej możliwej
> podklasy, bo przy rzucaniu wyjątku liczy się statyczny typ argumentu
> throw, a nie dynamiczny typ rzucanego obiektu.
No nie można, ale ty go chciałeś przekazać do innego wątku, czy innego
wyjątku? :)
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethouris(O)gmail.com>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"Java is answer for a question that has never been stated"
Technical Details
Message-ID:
<fj3el2$j5u$1@kujawiak.man.lodz.pl>Path:
mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.rmf.pl!agh.edu.pl!news.agh.edu.pl!newsfeed.silweb.pl!news.man.lodz.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> <fiee2a$af0$1@kujawiak.man.lodz.pl> <1196084612.2991.60.camel@qrnik>Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!news.rmf.pl!agh.edu.pl!news.agh.edu.pl!newsfeed.silweb.pl!news.man.lodz.pl!not-for-mail
From: Sektor van Skijlen <ethouris@guess.if.gmail.com.is.valid.or.invalid>
Newsgroups: pl.comp.lang.c
Subject: Re: Ciemna strona C++
Date: Tue, 4 Dec 2007 11:45:38 +0000 (UTC)
Organization: LODMAN - Metropolitan Area Network in LODZ, Poland
Lines: 347
Sender: Michal Malecki <sektor@student.kis.p.lodz.pl>
Message-ID: <fj3el2$j5u$1@kujawiak.man.lodz.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> <fiee2a$af0$1@kujawiak.man.lodz.pl> <1196084612.2991.60.camel@qrnik>
NNTP-Posting-Host: student.kis.p.lodz.pl
Mime-Version: 1.0
X-Trace: kujawiak.man.lodz.pl 1196768738 19646 212.191.89.21 (4 Dec 2007 11:45:38 GMT)
X-Complaints-To: abuse@man.lodz.pl
NNTP-Posting-Date: Tue, 4 Dec 2007 11:45:38 +0000 (UTC)
User-Agent: tin/1.8.3-20070201 ("Scotasay") (UNIX) (FreeBSD/6.2-RELEASE-p3 (sparc64))
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit