Dlaczego C ++ nie ma odbicia?

337

To dość dziwne pytanie. Moim celem jest zrozumienie decyzji dotyczącej projektu języka i identyfikacja możliwości refleksji w C ++.

  1. Dlaczego komitet językowy C ++ nie zdecydował się na wdrożenie refleksji w języku? Czy odbicie jest zbyt trudne w języku, który nie działa na maszynie wirtualnej (takiej jak Java)?

  2. Gdyby wprowadzić implementację refleksji dla C ++, jakie będą wyzwania?

Sądzę, że zastosowania refleksji są dobrze znane: edytory można łatwiej pisać, kod programu będzie mniejszy, można wygenerować próbne testy jednostkowe i tak dalej. Ale byłoby wspaniale, gdybyś mógł również wypowiedzieć się na temat zastosowania refleksji.

amit
źródło

Odpowiedzi:

631

Istnieje kilka problemów z odbiciem w C ++.

  • Jest dużo pracy do dodania, a komitet C ++ jest dość konserwatywny i nie spędza czasu na radykalnie nowych funkcjach, chyba że są pewni, że to się opłaci. (Pojawiła się sugestia dodania systemu modułów podobnego do zestawów .NET i chociaż wydaje mi się, że istnieje ogólna zgoda co do tego, że byłoby miło mieć, nie jest to obecnie ich priorytetem i została odsunięta z powrotem dopiero po pewnym czasie C ++ 0x. Motywacją dla tej funkcji jest pozbycie się #includesystemu, ale umożliwiłby także przynajmniej niektóre metadane).

  • Nie płacisz za to, czego nie używasz. To jedna z podstawowych zasad projektowania C ++. Dlaczego mój kod powinien przenosić metadane, jeśli nigdy ich nie potrzebuję? Ponadto dodanie metadanych może uniemożliwić optymalizację kompilatora. Dlaczego powinienem płacić ten koszt w kodzie, jeśli nigdy nie potrzebuję tych metadanych?

  • Co prowadzi nas do kolejnej ważnej kwestii: C ++ daje bardzo niewiele gwarancji dotyczących skompilowanego kodu. Kompilator może robić, co tylko zechce, pod warunkiem, że oczekiwana jest wynikowa funkcjonalność. Na przykład, twoje zajęcia nie muszą tam być . Kompilator może je optymalizować, wstawiać wszystko, co robią, i często to właśnie robi, ponieważ nawet prosty kod szablonu ma tendencję do tworzenia całkiem wielu instancji szablonów. Standardowa biblioteka C ++ polega na tej agresywnej optymalizacji. Funkcje działają tylko wtedy, gdy można zoptymalizować narzut związany z tworzeniem i niszczeniem obiektu. operator[]na wektorze jest porównywalny pod względem wydajności tylko z indeksowaniem macierzy surowych, ponieważ cały operator można wstawić, a tym samym całkowicie usunąć ze skompilowanego kodu. C # i Java dają wiele gwarancji na temat wyjścia kompilatora. Jeśli zdefiniuję klasę w języku C #, klasa ta będzie istnieć w wynikowym zestawie. Nawet jeśli go nigdy nie używam. Nawet jeśli wszystkie wywołania jego funkcji członkowskich mogą być wstawiane. Klasa musi tam być, aby odbicie mogło ją znaleźć. Część tego jest złagodzona przez kompilację C # do kodu bajtowego, co oznacza, że ​​kompilator JIT możeusuń definicje klas i funkcje wstawiane, jeśli chce, nawet jeśli początkowy kompilator C # nie może. W C ++ masz tylko jeden kompilator i musi on generować wydajny kod. Jeśli pozwolono ci na sprawdzenie metadanych pliku wykonywalnego C ++, można oczekiwać, że zobaczysz każdą zdefiniowaną przez siebie klasę, co oznacza, że ​​kompilator będzie musiał zachować wszystkie zdefiniowane klasy, nawet jeśli nie są one konieczne.

  • A potem są szablony. Szablony w C ++ nie przypominają ogólnych w innych językach. Każda instancja szablonu tworzy nowy typ. std::vector<int>jest całkowicie odrębną klasą od std::vector<float>. To składa się na wiele różnych typów w całym programie. Co powinno zobaczyć nasze odbicie? Szablon std::vector ? Ale jak to zrobić, skoro jest to konstrukcja kodu źródłowego, która nie ma znaczenia w czasie wykonywania? Musiałby zobaczyć osobne klasy std::vector<int>i std::vector<float>. A std::vector<int>::iteratori std::vector<float>::iterator, sam dlaconst_iteratori tak dalej. Po wejściu w metaprogramowanie szablonów szybko tworzy się setki szablonów, z których wszystkie zostają ponownie wstawione i usunięte przez kompilator. Nie mają one żadnego znaczenia, chyba że jako część metaprogramu podczas kompilacji. Czy wszystkie setki klas powinny być widoczne do refleksji? Musieliby, bo inaczej nasza refleksja byłaby bezużyteczna, gdyby nawet nie gwarantowała, że ​​zdefiniowane przeze mnie klasy rzeczywiście tam będą . Problemem ubocznym jest to, że klasa szablonów nie istnieje, dopóki nie zostanie utworzona instancja. Wyobraź sobie program, który używa std::vector<int>. Czy nasz system refleksji powinien widzieć std::vector<int>::iterator? Z jednej strony na pewno byś się tego spodziewał. Jest to ważna klasa i jest zdefiniowana w kategoriach std::vector<int>, co robiistnieją w metadanych. Z drugiej strony, jeśli program nigdy nie używa tego szablonu klasy iteratora, jego typ nigdy nie zostanie utworzony, a więc kompilator nie wygeneruje klasy w pierwszej kolejności. I jest za późno, aby go utworzyć w czasie wykonywania, ponieważ wymaga on dostępu do kodu źródłowego.

  • I wreszcie, odbicie nie jest tak istotne w C ++, jak w C #. Powodem jest znowu, metaprogramowanie szablonu. Nie jest w stanie rozwiązać wszystkiego, ale w wielu przypadkach, w których można by się zastanowić, można napisać metaprogram, który robi to samo w czasie kompilacji. boost::type_traitsjest prostym przykładem. Chcesz wiedzieć o typie T? Sprawdź to type_traits. W języku C # będziesz musiał łowić ryby po swoim typie za pomocą odbicia. Refleksja nadal byłaby przydatna w niektórych rzeczach (głównym zastosowaniem, którego widzę, którego metaprogramowania nie można łatwo zastąpić, jest automatyczny generowanie kodu serializacji), ale wiązałby się to z poważnymi kosztami dla C ++ i po prostu nie jest konieczne tak często, jak to jest jest w innych językach.

Edycja: W odpowiedzi na komentarze:

cdleary: Tak, symbole debugowania robią coś podobnego, ponieważ przechowują metadane dotyczące typów używanych w pliku wykonywalnym. Ale cierpią również z powodu problemów, które opisałem. Jeśli kiedykolwiek próbowałeś debugować kompilację wydania, będziesz wiedział, co mam na myśli. Istnieją duże logiczne luki, w których utworzono klasę w kodzie źródłowym, która została usunięta w końcowym kodzie. Jeśli użyjesz refleksji do czegoś pożytecznego, potrzebujesz jej bardziej niezawodnej i spójnej. W tej chwili typy znikają i znikają prawie za każdym razem, gdy kompilujesz. Zmieniasz mały detal, a kompilator decyduje się na zmianę, które typy zostaną wstawione, a które nie, w odpowiedzi. Jak wydobyć z tego cokolwiek przydatnego, gdy „ nie masz nawet gwarancji, że najbardziej odpowiednie typy będą reprezentowane w twoich metadanych? Typ, którego szukałeś, mógł znajdować się w ostatniej wersji, ale teraz go nie ma. A jutro ktoś sprawdzi niewielką niewinną zmianę na małą niewinną funkcję, co sprawi, że ten typ będzie na tyle duży, że nie zostanie całkowicie uwypuklony, więc wróci z powrotem. Nadal jest to przydatne w przypadku symboli debugowania, ale niewiele więcej. Nienawidzę próbować wygenerować kod serializacji dla klasy na tych warunkach. ale niewiele więcej. Nienawidzę próbować wygenerować kod serializacji dla klasy na tych warunkach. ale niewiele więcej. Nienawidzę próbować wygenerować kod serializacji dla klasy na tych warunkach.

Evan Teran: Oczywiście te problemy można rozwiązać. Ale to wraca do mojego punktu # 1. Zajmie to dużo pracy, a komitet C ++ ma wiele rzeczy, które ich zdaniem są ważniejsze. Czy korzyść z posiadania ograniczonej refleksji (i byłaby ograniczona) w C ++ jest naprawdę wystarczająco duża, aby uzasadnić skupienie się na tym kosztem innych funkcji? Czy naprawdę jest ogromna korzyść z dodawania funkcji do podstawowego języka, który można już (głównie) zrobić za pomocą bibliotek i preprocesorów, takich jak QT? Być może, ale potrzeba jest o wiele mniej pilna niż gdyby takie biblioteki nie istniały. Sądzę jednak, że jeśli chodzi o konkretne sugestie, niedopuszczenie go do szablonów uczyni go całkowicie bezużytecznym. Nie można na przykład użyć refleksji na temat standardowej biblioteki. Jakiego rodzaju refleksja by niestd::vector? Szablony są ogromną częścią C ++. Funkcja, która nie działa na szablonach, jest w zasadzie bezużyteczna.

Ale masz rację, można wprowadzić jakąś formę refleksji. Ale to byłaby poważna zmiana w języku. Tak jak obecnie, typy są wyłącznie konstrukcjami czasu kompilacji. Istnieją na korzyść kompilatora i nic więcej. Gdy kod został skompilowany, nie mają żadnego zajęcia. Jeśli się rozciągniesz, możesz argumentować, że funkcje nadal istnieją, ale tak naprawdę wszystko to zawiera wiele instrukcji asemblera skoku i wiele stosów push / pop. Dodając takie metadane, nie ma wiele do zrobienia.

Ale, jak powiedziałem, istnieje propozycja zmian w modelu kompilacji, dodawanie niezależnych modułów, przechowywanie metadanych dla wybranych typów, pozwalanie innym modułom na odwoływanie się do nich bez konieczności mieszania się z #includes. To dobry początek i, szczerze mówiąc, jestem zaskoczony, że komitet standardowy nie tylko wyrzucił propozycję bycia zbyt dużą zmianą. Więc może za 5-10 lat? :)

jalf
źródło
2
Czy większość z tych problemów nie musi już zostać rozwiązana za pomocą symboli debugowania? Nie dlatego, że byłby wydajny (z powodu wspomnianego wbudowania i optymalizacji), ale można pozwolić na możliwość refleksji, robiąc cokolwiek, co robią symbole debugowania.
cdleary
2
Kolejna rzecz na temat twojego pierwszego punktu: o ile wiem, nikt nie próbował dodać refleksji do implementacji C ++. Nie ma z tym dobrego doświadczenia. Komitet prawdopodobnie niechętnie przejmie inicjatywę, szczególnie po exporti vector<bool>.
David Thornley,
18
Zgadzam się, że C ++ nie powinien mieć odzwierciedlenia w czasie wykonywania. Ale odbicie czasu kompilacji wiąże się z kilkoma powyższymi problemami i może być wykorzystane przez kogoś do zbudowania refleksji czasu wykonania na poszczególnych klasach, jeśli sobie tego życzy. Mając dostęp do typu i nazwy oraz funkcji n-tej metody i n-tego rodzica klasy za pośrednictwem szablonu? I uzyskać liczbę takich w czasie kompilacji? Czyni automatyczną refleksję opartą na CRTP wykonalną, a nikt nie płaci za to, czego nie używa.
Jak - Adam Nevraumont,
15
Twój trzeci punkt jest pod wieloma względami najważniejszy: C ++ jest przeznaczony do pisania samodzielnego kodu na platformach, na których pamięć kosztuje; jeśli wyeliminowanie jakiegoś nieużywanego kodu pozwoliłoby programowi zmieścić się w mikrokontrolerze, który kosztuje 2,00 USD, zamiast takiego, który kosztuje 2,50 USD, a jeśli kod jest w 1 000 000 jednostek, wyeliminowanie tego kodu może zaoszczędzić 500 000 USD. Bez refleksji analiza statyczna może często zidentyfikować 90% + nieosiągalnego kodu; jeśli dozwolone jest odbicie, wszystko, co można osiągnąć poprzez odbicie, musi zostać uznane za osiągalne, nawet jeśli 90% nie jest możliwe.
supercat,
2
nie jest z pewnością coś, co można poprawić przez KOMITET łatwo, to w końcu powiedzieć, czarno na białym, że typeinfojest name()funkcja musi zwrócić imię, które zostało wpisane w przez programistę, a nie coś niezdefiniowane. Podaj nam także stringfikator dla enumeratorów. Jest to w rzeczywistości kluczowe dla serializacji / deserializacji, pomagając w tworzeniu fabryk itp.
v.oddou 18.04.13
38

Refleksja wymaga przechowywania niektórych metadanych dotyczących typów w miejscach, w których można wyszukiwać. Ponieważ C ++ kompiluje się do natywnego kodu maszynowego i ulega znacznym zmianom z powodu optymalizacji, widok aplikacji na wysokim poziomie jest prawie całkowicie zagubiony w procesie kompilacji, w związku z tym nie będzie możliwe sprawdzenie go w czasie wykonywania. Java i .NET używają bardzo wysokiego poziomu reprezentacji w kodzie binarnym dla maszyn wirtualnych, umożliwiając ten poziom refleksji. W niektórych implementacjach C ++ istnieje jednak coś, co nazywa się informacją typu czasu wykonania (RTTI), którą można uznać za uproszczoną wersję odbicia.

Mehrdad Afshari
źródło
15
RTTI jest w standardzie C ++.
Daniel Earwicker,
1
Ale nie wszystkie implementacje C ++ są standardowe. Widziałem implementacje, które nie obsługują RTTI.
Mehrdad Afshari,
3
Większość implementacji obsługujących RTTI obsługuje także wyłączenie go za pomocą opcji kompilatora.
Michael Kohne,
21

Wszystkie języki nie powinny próbować uwzględniać wszystkich funkcji każdego innego języka.

C ++ jest zasadniczo bardzo wyrafinowanym asemblerem makr. NIE jest to (w tradycyjnym sensie) język wysokiego poziomu, taki jak C #, Java, Objective-C, Smalltalk itp.

Dobrze jest mieć różne narzędzia do różnych zadań. Jeśli mamy tylko młotki, wszystko będzie wyglądać jak gwoździe itp. Posługiwanie się językami skryptowymi jest przydatne do niektórych zadań, a odblaskowe języki OO (Java, Obj-C, C #) są przydatne do innej klasy zadań i super -wydajne, pozbawione kości języki zbliżone do maszyny są przydatne w jeszcze innej klasie zadań (C ++, C, asembler).

C ++ wykonuje niesamowitą pracę polegającą na rozszerzeniu technologii Asemblera na niewiarygodne poziomy zarządzania złożonością i abstrakcjach, dzięki czemu programowanie jest większe, bardziej złożone zadania są znacznie łatwiejsze dla ludzi. Ale niekoniecznie jest to język, który najlepiej nadaje się dla tych, którzy podchodzą do swojego problemu ze ściśle wysokiego poziomu (Lisp, Smalltalk, Java, C #). Jeśli potrzebujesz języka z tymi funkcjami, aby jak najlepiej wdrożyć rozwiązanie swoich problemów, dziękuj tym, którzy stworzyli takie języki, z których wszyscy możemy korzystać!

Ale C ++ jest dla tych, którzy z jakichkolwiek powodów muszą mieć silną korelację między swoim kodem a działaniem maszyny bazowej. Niezależnie od tego, czy jest to wydajność, czy programowanie sterowników urządzeń, czy interakcja z usługami systemu operacyjnego niższego poziomu, czy cokolwiek innego, C ++ jest bardziej odpowiedni do tych zadań.

C #, Java, Objective-C wszystkie wymagają znacznie większego, bogatszego systemu wykonawczego do obsługi ich wykonywania. Środowisko wykonawcze należy dostarczyć do systemu, o którym mowa, wstępnie zainstalowane w celu obsługi działania oprogramowania. Tę warstwę należy utrzymywać dla różnych systemów docelowych, dostosowanych przez NIEKTÓRY JĘZYK, aby działał na tej platformie. I ta środkowa warstwa - ta warstwa adaptacyjna między systemem operacyjnym hosta a twoim kodem - środowisko wykonawcze, prawie zawsze jest napisana w języku takim jak C lub C ++, gdzie wydajność jest na pierwszym miejscu, gdzie zrozumiałe jest dokładne zrozumienie dokładnej interakcji między oprogramowaniem a sprzętem zrozumiany i zmanipulowany do maksymalnego wzmocnienia.

Uwielbiam Smalltalk, Objective-C i posiadanie bogatego systemu wykonawczego z refleksją, metadanymi, wyrzucaniem elementów bezużytecznych itp. Można wykorzystać niesamowity kod, aby skorzystać z tych udogodnień! Ale to po prostu wyższa warstwa na stosie, warstwa, która musi spoczywać na niższych warstwach, które same muszą ostatecznie oprzeć się na systemie operacyjnym i sprzęcie. I zawsze będziemy potrzebować języka, który najlepiej nadaje się do budowania tej warstwy: C ++ / C / Asembler.

Dodatek: C ++ 11/14 nadal rozszerza możliwości C ++ do obsługi abstrakcyjnych poziomów i systemów. Wątki, synchronizacja, precyzyjne modele pamięci, bardziej precyzyjne abstrakcyjne definicje maszyn umożliwiają programistom C ++ osiągnięcie wielu abstrakcyjnych poziomów wysokiego poziomu, nad którymi niektóre z tych tylko języków wysokiego poziomu posiadały wyłączną domenę, jednocześnie zapewniając bliskość do wydajność metalu i doskonała przewidywalność (tj. minimalne podsystemy czasu wykonywania). Być może narzędzia do refleksji zostaną selektywnie włączone w przyszłej wersji C ++ dla tych, którzy tego chcą - a może biblioteka zapewni takie usługi środowiska uruchomieniowego (może jest teraz jedna, lub początki jednej w fazie ulepszenia?).

Mordaczaj
źródło
Twoja uwaga na temat środowiska uruchomieniowego języka, który musi zostać skompilowany w innym języku, nie jest prawdziwa w przypadku Celu-C, ponieważ jego środowisko wykonawcze jest napisane w C (którego Cel-C jest nadzbiorem).
Richard J. Ross III
To rozróżnienie bez różnicy. Jaką różnicę to robi, gdy w końcu podsystem wykonawczy, z którego korzysta Cel C, w rzeczywistości nie jest zapisany w Celu C, ale raczej w C?
Mordachai
3
Przepraszam; ale tak długo, jak podłączysz go poprawnie, możesz skompilować program C w C, tak naprawdę zrobiłem to tutaj: stackoverflow.com/a/10290255/427309 . Twoje całe powyższe stwierdzenie jest fałszywe. Środowisko wykonawcze jest w pełni dostępne za pośrednictwem C i jest to jedna z rzeczy, które sprawiają, że jest on tak potężnym, dynamicznym językiem.
Richard J. Ross III
1
„Środowisko wykonawcze C” to po prostu biblioteka dynamiczna zawierająca kod biblioteki standardowej C. To samo dotyczy „środowiska wykonawczego C ++”. Różni się całkowicie od systemu wykonawczego takiego jak Objective-C. Poza tym ... przypuszczam, że technicznie można użyć środowiska wykonawczego Objective-C w C, ale nadal jest to tylko program C, który używa środowiska wykonawczego Objective-C - nie można skompilować rzeczywistego programu Objective-C w C.
celticminstrel
2
C ++ 11 posiadający model pamięci + atomikę czyni go bardziej jak przenośny asembler. To nie są rzeczy wysokiego poziomu, to rzeczy niskiego poziomu, dla których C ++ wcześniej brakowało przenośnego wsparcia. Ale ilość UB w C ++, jeśli zrobisz coś źle, czyni go bardzo odmiennym od języków opartych na maszynach wirtualnych, takich jak Java, a także w przeciwieństwie do jakiegokolwiek konkretnego języka asemblera. np. przepełnienie ze znakiem jest całkowicie UB w źródle C ++ i kompilator może zoptymalizować na podstawie tego faktu, nawet jeśli kompilacja dla powiedzmy x86, ale w asm na prawie wszystkich platformach po prostu się zawinie. Nowoczesne C ++ jest bardzo dalekie od przenośnego języka asemblera.
Peter Cordes,
11

Jeśli naprawdę chcesz zrozumieć decyzje projektowe dotyczące C ++, znajdź kopię The Annotated C ++ Reference Manual autorstwa Ellis i Stroustrup. NIE jest na bieżąco z najnowszym standardem, ale przechodzi przez pierwotny standard i wyjaśnia, jak rzeczy działają, a często, w jaki sposób je osiągnęły.

Michael Kohne
źródło
6
Również projektowanie i ewolucja C ++ przez Stroustrup
James Hopkin
9

Refleksja dla języków, które go mają, dotyczy tego, ile kodu źródłowego kompilator jest skłonny pozostawić w kodzie obiektu, aby umożliwić odbicie, oraz ile maszyn analizy jest dostępnych do interpretacji tych odbitych informacji. O ile kompilator nie utrzymuje całego kodu źródłowego, refleksja będzie ograniczona w zakresie możliwości analizy dostępnych faktów na temat kodu źródłowego.

Kompilator C ++ nie utrzymuje niczego (no, ignorując RTTI), więc nie dostaniesz refleksji w języku. (Kompilatory Java i C # przechowują tylko klasy, nazwy metod i typy zwracane, więc otrzymujesz trochę danych refleksyjnych, ale nie możesz sprawdzać wyrażeń ani struktury programu, a to oznacza, że ​​nawet w tych językach z włączoną funkcją refleksji informacje, które można uzyskać, są dość rzadkie, w związku z czym naprawdę nie można przeprowadzić dużej analizy).

Ale możesz wyjść poza język i uzyskać pełne możliwości refleksji. Odpowiedź na inną dyskusję dotyczącą przepełnienia stosu na odbicie w C omawia to.

Ira Baxter
źródło
7

Refleksja może być i była wcześniej implementowana w c ++.

Nie jest to natywna funkcja języka C ++, ponieważ ma duże koszty (pamięć i szybkość), których domyślnie nie powinien ustawiać język - język jest zorientowany na „maksymalną wydajność domyślnie”.

Ponieważ nie powinieneś płacić za to, czego nie potrzebujesz, i jak sam twierdzisz, jest to potrzebne bardziej w edytorach niż w innych aplikacjach, dlatego powinno być implementowane tylko tam, gdzie jest to potrzebne, a nie „wymuszane” na cały kod ( nie potrzebujesz refleksji nad wszystkimi danymi, z którymi będziesz pracować w edytorze lub innej podobnej aplikacji).

Klaim
źródło
3
i nie wysyłasz symboli, ponieważ pozwoliłoby to twoim klientom / konkurentom spojrzeć na twój kod ... jest to często uważane za złe.
gbjbaanb,
Masz rację, nawet nie
pomyślałem
6

Powodem, dla którego C ++ nie ma odzwierciedlenia, jest to, że wymagałoby to od kompilatorów dodania informacji o symbolach do plików obiektowych, takich jak elementy należące do typu klasy, informacje o elementach, o funkcjach i wszystkim. Zasadniczo spowodowałoby to, że pliki dołączane byłyby bezużyteczne, ponieważ informacje przesyłane przez deklaracje byłyby następnie odczytywane z tych plików obiektowych (wówczas modułów). W C ++ definicja typu może występować wiele razy w programie poprzez dołączenie odpowiednich nagłówków (pod warunkiem, że wszystkie te definicje są takie same), więc należałoby zdecydować, gdzie umieścić informacje o tym typie, tak jak nazwać jeden komplikacja tutaj. Kolejną mocną stroną jest agresywna optymalizacja przeprowadzona przez kompilator C ++, który może zoptymalizować dziesiątki instancji szablonów klas. Jest to możliwe, ale ponieważ C ++ jest kompatybilny z C,

Johannes Schaub - litb
źródło
1
Nie rozumiem, w jaki sposób agresywna optymalizacja kompilatora jest mocną stroną. Czy możesz rozwinąć? Jeśli linker może usunąć zduplikowane definicje funkcji wbudowanych, jaki jest problem ze zduplikowanymi informacjami o odbiciu? Czy i tak informacje o symbolach nie są dodawane do plików obiektowych w przypadku debuggerów?
Rob Kennedy,
1
Problem polega na tym, że informacje o odbiciu mogą być nieprawidłowe. Jeśli kompilator eliminuje 80% definicji klas, co powie metadane odbicia? W języku C # i Javie język gwarantuje, że jeśli zdefiniujesz klasę, pozostanie ona zdefiniowana. C ++ pozwala kompilatorowi go zoptymalizować.
lipiec
1
@Rob, optymalizacje to kolejny punkt niezwiązany z komplikacją wielu klas. Zobacz komentarz @ jalfa (i jego odpowiedź), co mam na myśli.
Johannes Schaub - litb
4
Jeśli utworzę instancję odzwierciedlając <T>, nie wyrzucaj żadnej informacji T. Nie wydaje się to problemem nierozwiązywalnym.
Joseph Garvin
3

Istnieje mnóstwo przypadków użycia refleksji w C ++, których nie można odpowiednio rozwiązać za pomocą konstruktów czasu kompilacji, takich jak metaprogramowanie szablonu.

N3340 proponuje bogate wskaźniki jako sposób na wprowadzenie refleksji w C ++. Między innymi dotyczy to kwestii nieopłacania funkcji, chyba że z niej skorzystasz.

pong
źródło
2

Według Alistair Cockburn nie można zagwarantować podsieci w środowisku odbijającym światło .

Odbicie jest bardziej odpowiednie dla ukrytych systemów pisania. W C ++ wiesz, jaki masz typ i wiesz, co możesz z nim zrobić.

Nilone
źródło
Mówiąc bardziej ogólnie, możliwość sprawdzenia istnienia funkcji, która nie istnieje bez wprowadzenia niezdefiniowanego zachowania, umożliwia dodanie tej funkcji do późniejszej wersji klasy zmieni dobrze zdefiniowane zachowanie wcześniej istniejących programów i będzie w konsekwencji uniemożliwi to zagwarantowanie, że dodanie tej funkcji nie „coś zepsuje”.
supercat
2

Odbicie może być opcjonalne, podobnie jak dyrektywa preprocesora. Coś jak

#pragma enable reflection

W ten sposób możemy mieć to, co najlepsze z obu światów, bez tego pragmatyczne biblioteki byłyby tworzone bez refleksji (bez omówionych kosztów ogólnych), wtedy indywidualny programista będzie zależał od tego, czy chcą szybkości czy łatwości użytkowania.

użytkownik1401491
źródło
2

Jeśli C ++ mógłby mieć:

  • dane członka klasy dla nazw zmiennych, typów zmiennych i constmodyfikatora
  • iterator argumentów funkcji (tylko pozycja zamiast nazwy)
  • dane członka klasy dla nazw funkcji, typu zwracanego parametru i constmodyfikatora
  • lista klas nadrzędnych (w takiej samej kolejności, jak zdefiniowano)
  • dane dla członków szablonu i klas nadrzędnych; rozwinięty szablon (co oznacza, że ​​rzeczywisty typ będzie dostępny dla interfejsu API odbicia, a nie „informacje o szablonie, jak się tam dostać”)

Wystarczyłoby to do stworzenia bardzo łatwych w użyciu bibliotek w centrum typowego przetwarzania danych, które jest tak powszechne we współczesnych aplikacjach internetowych i bazach danych (wszystkie orms, mechanizmy przesyłania wiadomości, parsery xml / json, serializacja danych itp.).

Na przykład podstawowe informacje obsługiwane przez Q_PROPERTYmakro (część Qt Framework) http://qt.nokia.com/doc/4.5/properties.html rozszerzone o metody klasowe e) - byłyby niezwykle korzystne dla C ++ i społeczność oprogramowania w ogóle.

Z pewnością refleksja, o której mówię, nie obejmowałaby znaczenia semantycznego ani bardziej złożonych zagadnień (takich jak komentarze wierszy kodu źródłowego, analiza przepływu danych itp.) - ale nie sądzę, aby były one częścią standardu językowego.

Lekkość Wyścigi na orbicie
źródło
@Vlad: Tak, jeśli dodasz funkcje wspierające refleksję w języku, otrzymasz refleksję w tym języku. Stanie się tak prawdopodobnie tylko wtedy, gdy komitet językowy to wyda, i myślę, że nie ma tego od 2011 roku, i wątpię, aby przed 2020 rokiem obowiązywał inny standard C ++. Więc miła myśl. W międzyczasie, jeśli chcesz robić postępy, prawdopodobnie będziesz musiał wyjść poza C ++.
Ira Baxter,
0

Odbicie w C ++, uważam, że jest niezwykle ważne, jeśli C ++ ma być używany jako język dostępu do bazy danych, obsługi sesji WWW / HTTP i rozwoju GUI. Brak refleksji uniemożliwia ORM (takie jak Hibernacja lub LINQ), parsery XML i JSON, które powodują instancje klas, serializację danych i wiele innych problemów (w których początkowo dane beztypowe muszą być użyte do utworzenia instancji klasy).

Przełącznik czasowy kompilacji dostępny dla twórcy oprogramowania podczas procesu kompilacji może być wykorzystany do wyeliminowania problemu „płacisz za to, czego używasz”.

I programista oprogramowania nie potrzebuje refleksji, aby odczytać dane z portu szeregowego - wtedy dobrze nie używaj przełącznika. Ale jako programista baz danych, który chce nadal używać C ++, jestem ciągle wprowadzany w fazę z okropnym, trudnym do utrzymania kodem, który odwzorowuje dane między elementami danych a konstrukcjami bazy danych.

Ani serializacja Boost, ani żaden inny mechanizm tak naprawdę nie rozwiązuje problemu - musi to zrobić kompilator - a kiedy to nastąpi, C ++ będzie ponownie poszukiwany w szkołach i używany w oprogramowaniu zajmującym się przetwarzaniem danych

Dla mnie ten problem nr 1 (a naiwne prymitywy wątków to problem nr 2).

vsp
źródło
4
Kto powiedział, że C ++ ma być używany jako język dla DB Access, sesji hnadling lub gui dev? Istnieje wiele znacznie lepszych języków do tego typu rzeczy. A przełącznik czasu kompilacji nie rozwiąże problemu. Zwykle decyzja o włączeniu lub wyłączeniu odbicia nie będzie zależała od pliku. Może działać, jeśli można go włączyć dla poszczególnych typów. Jeśli programista może określić za pomocą atrybutu lub podobnego elementu podczas definiowania typu, należy wygenerować dla niego metadane odbicia. Ale globalna zmiana? Utraciłbyś 90% języka, aby uprościć 10%.
lipiec
Jeśli więc chcę mieć program wieloplatformowy i mieć dostęp do GUI, czego powinienem użyć? Nieelastyczna huśtawka Java? Windows tylko C #? Ale prawdę należy powiedzieć, a prawda jest taka, że ​​istnieje wiele programów, które są skompilowane w kodzie wykonywalnym i oferują interfejs GUI oraz dostęp do baz danych, więc muszą korzystać z obsługi baz danych i GUI ... I nie są t używając QT. (powinien był nazywać się MT (zestaw narzędzi potwora))
Coyote21
1
@ Coyote21: C # nie był przeznaczony tylko dla systemu Windows od lat. (Chociaż nie jestem fanem Mono, działa wystarczająco dobrze dla większości rzeczy.) A Swing nie jest jedynym zestawem GUI dla Java. Prawdę mówiąc, każdy z nich byłby lepszym wyborem, jeśli chcesz korzystać z wielu platform. C ++ prawie zawsze będzie zawierał części specyficzne dla platformy, jeśli robisz coś nietrywialnego.
cHao
Nie ma powodu, dla którego potrzebujesz refleksji nad ORM. Możesz to wszystko osiągnąć za pomocą szablonów. Istnieje szereg struktur zapewniających ORM dla C ++.
MrFox,
0

Jest to w zasadzie dlatego, że jest to „dodatek opcjonalny”. Wiele osób wybiera C ++ zamiast języków takich jak Java i C #, aby mieć większą kontrolę nad wyjściem kompilatora, np. Mniejszym i / lub szybszym programem.

Jeśli zdecydujesz się dodać odbicie, dostępne są różne rozwiązania .

Nacięcie
źródło