2009-08-12 7 views
10

Byłem programistą Java i VB.Net przez około 4 lata i programistą C# przez około 6 miesięcy. Użyłem również wielu dynamicznych języków, takich jak Perl, Python, PHP i JavaScript.Dlaczego użycie preprocesora jest mniej powszechne w językach innych niż C/C++/ObjC?

Nigdy nie potrzebowałem preprocesora.

Moje pytanie brzmi: dlaczego widzisz tak szerokie zastosowanie preprocesorów w C, C++ i Objective-C, ale rzadko (lub nigdy) widzisz je w językach takich jak Java, C# czy Scala?

+7

Dowiedzieliśmy się od C. Z C/C++/Objective-C, otrzymali kompilator języka C, a następnie zbudowali go na górze, ponieważ było to łatwiejsze. Nowsze języki zostały po prostu lepiej zaprojektowane od podstaw. Większość osób, które znam z przeszłością w tle, jest przerażonych przedprocesorami. Nie nadaje się do prędkości, wkręca swój projekt, szkodzi zrozumieniu. Żadna przewaga, z wyjątkiem tego, że pozwala ci napisać gorszy kod - dlaczego ktoś miałby to celowo skopiować? –

+2

Zobacz http://stackoverflow.com/questions/652788/what-is-the-worst-real-world-macros-pre-processor-abuse-youve-ever-come-rossross i http: //blogs.msdn. com/oldnewthing/archive/2005/01/06/347666.aspx :) –

+1

Mam kilka programów dsp C++, w których liczy się każdy cykl (tak, naprawdę). Preprocesor pomaga mi sporo podczas optymalizacji. Nie używam go do niczego innego. Kocham to. Nie mógłbyś tego wyciągnąć z moich rąk. Czy w tych dynamicznych językach wykonałeś wiele programów dsp w czasie rzeczywistym? Pamiętaj, że bez względu na to, ile rodzajów programowania wykonałeś, właśnie podrapałeś powierzchnie tego rodzaju programowania, które można zrobić. – Nosredna

Odpowiedz

13

Nie znam Objective-C, więc moja odpowiedź będzie dotyczyła kontrastowania użycia preprocessora w C i C++.

Preprocesor był pierwotnie konieczny dla C z kilku powodów. Jeśli dobrze pamiętam, pierwotnie C nie miał stałych, więC# defikta było potrzebne, aby uniknąć magicznych liczb. Przed 1999 rokiem C nie miał wbudowanych funkcji, więc ponownie #define został użyty do stworzenia makr lub "pseudo-funkcji", aby zaoszczędzić na obciążeniu wywołania funkcji, przy zachowaniu struktury kodu. C również nie ma polimorfizmu czasu wykonywania lub kompilacji, więC#ifdefs były potrzebne do kompilacji warunkowej. Kompilatory zazwyczaj nie były wystarczająco inteligentne, aby zoptymalizować nieosiągalny kod, więc ponownie użyto #ifdefs do wstawienia kodu diagnostycznego lub debugowania.

Użycie preprocesora w C++ jest powrotem do C i generalnie jest marszczone. Funkcje językowe, takie jak stałe, funkcje wbudowane i szablony, mogą być używane w większości sytuacji, w których w C użyłbyś preprocesora.

Nieliczne przypadki, w których użycie preprocesora w C++ jest dopuszczalne, a nawet konieczne, obejmują osłony plików nagłówkowych, aby zapobiec wielokrotnemu dołączaniu tego samego nagłówka, #ifdef __cplusplus, aby użyć tego samego nagłówka dla obu nagłówków i C++, __FILE__ i __LINE__ do logowania oraz kilka innych.

Preprocesor jest często używany również w definicjach specyficznych dla platformy, chociaż Stephen Dewhurst zaleca, aby C++ Gotchas mieć oddzielne katalogi włączające dla definicji specyficznych dla platformy i używać ich w osobnych konfiguracjach kompilacji dla każdej platformy.

+2

To nie jest jedyny sposób, w jaki preprocesor w C++ jest uważany za OK. Inne zastosowania szeroko (jeśli nie uniwersalnie) uważane za OK obejmują wypisywanie "__FILE__" i "__LINE__" w logowaniu, makro Boost FOREACH, sprawdzanie obsługi funkcji kompilatora (w tym klasycznego '#ifdef __cplusplus // extern" C "{// #endif '), definicje specyficzne dla platformy (np. gdy potrzebujesz' __declspec' w MSVC i '__attribute__' w GCC), definiując NDEBUG przed włączeniem , ... –

+0

Stoję poprawiony. – Dima

1

Współczesne języki mają preprocesor zawarty w samym języku! W przypadku C++ preprocesor jest potrzebny tylko do zarządzania modułami i warunkowego włączenia, co jest bardzo przydatne.

Uważam, że było to oddzielne narzędzie, ponieważ kompilatory nie były jednym narzędziem, jakie znamy dzisiaj. Słyszałem, że bardzo stare kompilatory języka C używają do tworzenia tokenów w plikach, a następnie wykonują resztę kompilacji w oddzielnych etapach. Głównym powodem, dla którego mogę myśleć, jest to, że pamięć i inne zasoby były bardzo rzadkie w porównaniu do tego, co mamy dzisiaj.

0

Możesz być pewien, że współczesny język jest napisany w języku C lub C++, aw samej implementacji istnieją makra. Potrzebujesz ich, aby poradzić sobie z różnicami systemowymi dla jednej rzeczy. Zawijanie języków dynamicznych/wyższego poziomu i ukrywanie wielu rzeczy, które znajdują się gdzieś na dole, potrzebne są makra.

Ponadto makra są używane czasami do prędkości. W dynamicznych językach prędkość nie jest tak ważna.

1

Preprocesor w C i C++ posiada dwie różne funkcje

  • ciągnięcie pliki razem w procesie kompilacji - Języki jak Java i wsp. mają własne mechanizmy jak import to zrobić

  • wykonywania podstawienia tekstowe - to jest nadal potrzebna do pewnego stopnia w języku C, ale C++ może to zrobić (w większości przypadków) lepiej przy użyciu szablonów

Tak zarówno C, jak i C++ potrzebują go do pierwszej z nich, ale C++ może je odrzucić na sekundę, chociaż może być użyteczne nawet w C++ - patrz this question z wcześniejszego dnia.

10

Powodem, dla którego nie widzisz preprocesora używanego w Javie, C# lub Scali jest to, że te języki oczywiście nie mają takiego.

Jednym z typowych zastosowań preprocesora C jest pomoc w dostarczaniu kodu specyficznego dla platformy. Ponieważ C (w tym C++ i Objective-C) jest językiem niskiego poziomu, który musi bezpośrednio łączyć się z systemem operacyjnym, w przenośnym kodzie muszą istnieć różne sekcje kodu skompilowane dla różnych systemów operacyjnych. Można znaleźć szerokie przykłady tego rodzaju rzeczy w dojrzałej, bardzo przenośnej bazie kodu, takiej jak zlib.

Jako prosty przykład, aby zamknąć gniazdo sieciowe trzeba zrobić coś takiego (na pewnym poziomie, to z pewnością może być zawinięte w funkcji, ale musi istnieć gdzieś):

#ifdef WIN32 
    closesocket(s); 
#else 
    close(s); 
#endif 

Nowsze języki uruchamiane na maszynach wirtualnych nie potrzebują różnych sekcji kodu dla poszczególnych platform i mogą być napisane na podstawie pojedynczej, przenośnej biblioteki standardowej.

Preprocesor zapewnia również sposób definiowania stałych w C, które są dostarczane przez inne, lepsze funkcje językowe w nowszych językach.

W projekcie i ewolucji C++, Bjarne Stroustrup stwierdził, że chciał usunąć zależność od preprocesora w C++, ale nie powiodło się.

+4

"Powodem, dla którego nie widzisz preprocesora ... jest to, że te języki ... nie mają takiego." !! doskonały! – Cheeso

+1

Prawdziwy język może sprawdzić zmienną WIN32, zobaczyć, czy jest ostateczny i wygenerować prawidłową ścieżkę w linii. Nie ma praktycznie żadnego zastosowania dla preprocesorów w nowoczesnym języku. –

+0

Jak na ironię (jeśli się nie mylę), poprzednik Bjarne'a dla C++, C z klasami, był zasadniczo warstwą makro na C. –

3

Ponieważ Gosling i Heilsberg rozumieją zarówno ryzyko, jak i techniczny dług powstały w wyniku niewłaściwego użycia preprocesora!

4

Ponieważ konstrukcja i cele tych języków nie są takie same.

C został zbudowany z myślą o preprocesorze jako potężne narzędzie, został użyty do zaimplementowania bardzo podstawowych rzeczy (takich jak osłony), a programiści mogli go użyć do optymalizacji kodu za pomocą makr lub opcjonalnie dołączyć/wykluczyć niektóre bloki kodu oprócz innych rzeczy. C++ odziedziczyła większość idiomów c za makra nie są wykorzystywane do prędkości już (bo inline został wprowadzony), ale wciąż wykorzystywane do wiele rzeczy, patrz post What are preprocessor macros good for?

0

ściślej Należy spojrzeć trochę na Perl. Perl obsługuje source filters, które są w zasadzie niestandardowymi preprocesorami Perla napisanymi w Perlu :)

0

Java została zaprojektowana w celu uniknięcia kilku funkcji, które utrudniają używanie C++.

C# skopiować (lub dziedziczyć) większość decyzji projektowych z Java.

Języki programowania wyższego poziomu unikają tego rodzaju artefaktów o niskim poziomie.

1

Nie zgadzam się z tym, co wydaje się być zgodnym konsensusem, w którym ten cpp nie jest konieczny we współczesnych językach. Mam wiele przypadków, w których mam 3 nieznacznie różne wersje tego samego programu i chcę być w stanie dokonać wielu zmian dla każdej wersji.Dzięki CPP mogę umieścić je wszystkie w #if #else blocks i mogę zdefiniować #if na linii kompilacji. w Javie, muszę utworzyć coś w rodzaju statycznego globalnego i zainicjować go w czasie kompilacji. Nigdy tego nie zrobiłem, żeby działał poprawnie.

+0

@Brian: Eww! Nie dziwię się, że to nie działa poprawnie! Lepszym rozwiązaniem w języku Java byłoby użycie podklas lub wzorca "Strategia" w celu oddzielenia różnic między wersjami. –

+0

Ale jeśli różnice są 10 liniami porozrzucanymi w klasie lub gorszymi klasami? to dużo wysiłku. –

+0

@Brian: nie możesz wygrać tego argumentu. To jest Java, więc więcej zajęć => Lepiej ;-) –

8

Każdy język wymaga mechanizmu dla osobnej kompilacji. Idealnie język odróżnia interfejsy od implementacji, a moduł zależy tylko od interfejsów eksportowanych modułów. (Patrz np. Ada, Clu, Modula itd.)

C   nie zawiera konstrukcji językowej dla interfejsów lub implementacji. Ponieważ ważne jest, aby różne pliki .c miały wspólny widok interfejsów, rozwinęła się dyscyplina programowania polegająca na umieszczaniu deklaracji (tzn. Interfejsów) w plikach .h i współużytkowaniu tych deklaracji/interfejsów przy użyciu włączania tekstowego (#include). Zgodnie z zasadą  , można było zrezygnować z #define i #ifdef, ale #include nie mógł tego zrobić.

Współcześni projektanci języków uznają, że włączenie tekstu nie jest w stanie uruchomić linii kolejowej, więc języki zwykle działają albo na osobno skompilowane interfejsy (Ada, Modula, OCaml), na interfejsy generowane przez kompilator (Haskell), albo na systemy dynamiczne gwarantująca spójność interfejsu (Java, Smalltalk). Przy takim mechanizmie nie ma potrzeby używania preprocesora i wielu powodów, dla których go nie ma (pomyśl o analizie kodu źródłowego i debugowaniu).

+1

+1 Twoja odpowiedź wydaje się najbardziej neutralna i zawiera informacje. Cieszę się z tego. –

1

Preprocessing jest bardzo, bardzo popularny w świecie Java. Służy to zrekompensowaniu braków języka w odpowiednich wbudowanych obiektach abstrakcyjnych, które w przeciwnym razie doprowadziłyby do niekończącego się kopiowania i wklejania kodu standardowego.

Powodem, dla którego wiele osób nie zdaje sobie z tego sprawy, jest to, że w świecie Java nazywa się "generowanie kodu", a nie "przetwarzanie wstępne", ponieważ "preprocesor" brzmi jak nieprzyjemne stare C, a "generowanie kodu" brzmi jak profesjonalne narzędzie, które usprawnia dojrzałe procesy w przedsiębiorstwie. Nadal jest to jednak preprocesing, nawet jeśli musisz zapłacić fortunę za niekompatybilne, niestandardowe narzędzie, zamiast korzystać z wbudowanych w niego udogodnień.

+0

Co za wspaniała odpowiedź, naprawdę chciałbym móc dać więcej niż jedno Głosowanie! –

Powiązane problemy