Czy C ++ jest odpowiedni dla systemów wbudowanych?


167

Często zadawane pytanie tutaj i gdzie indziej. Czy C ++ jest odpowiedni dla systemów wbudowanych?

Mikrokontrolery? RTOS? Tostery? Komputery wbudowane?

Czy OOP jest użyteczny w mikrokontrolerach?

Czy C ++ usuwa programator zbyt daleko od sprzętu, aby był wydajny?

Czy Cdu Arduino (bez dynamicznego zarządzania pamięcią, szablonów, wyjątków) powinien być uważany za „prawdziwy C ++”?

(Mam nadzieję, że ta wiki będzie służyć jako miejsce do powstrzymania tej potencjalnej świętej wojny)


5
Szybkie pytanie: kiedy mówisz osadzony , masz na myśli mikrokontroler? mikroprocesor? embedded x86 / embedded PC?
J. Polfer,

1
Nie zamierzałem wywoływać świętej wojny; chodziło o to, aby dowiedzieć się, jakie były twoje argumenty przeciwko niemu.
J. Polfer,

2
Wcześniej pojawiło się kilka pytań, więc pomyślałem, że centralne miejsce będzie dobre.
Toby Jaffey,

4
C ++ vs embedded jest kontrowersyjnym tematem. Mam silną opinię, ale nie sądziłem, że zadawanie pytań i granie w zdobywanie punktów jest niesprawiedliwe. Mam nadzieję, że wiki społeczności przyczyni się do bardziej zrównoważonej dyskusji.
Toby Jaffey,

13
To złe pytanie, ponieważ „osadzony” jest nieistotnym atrybutem przy podejmowaniu decyzji, czy dany język i związany z nim bagaż są odpowiednie. Chodzi o to, że są małe w porównaniu do dużych systemów, w których małe systemy nie mają systemu operacyjnego, mają ograniczoną pamięć, mogą nie być von-Neuman, mogą mieć różne ograniczenia sprzętowe dotyczące stosów połączeń, stosów danych, nie można po prostu dynamicznie przydzielić Mb a nawet KB, itp. Większość mikrokontrolerów to „małe” systemy. Komputery jednopłytkowe są zwykle osadzone, ale zazwyczaj są to systemy „duże”.
Olin Lathrop,

Odpowiedzi:


135

Tak, C ++ jest nadal użyteczny w systemach wbudowanych. Jak wszyscy powiedzieli, nadal zależy to od samego systemu, tak jak 8-bitowy interfejs użytkownika prawdopodobnie nie dałby mi żadnej książki, mimo że jest tam kompilator i niektórzy to robią (wzdrygają się). Korzystanie z C ++ jest nadal zaletą, nawet jeśli zmniejszysz go do czegoś takiego jak „C +”, nawet w 8-bitowym świecie mikro. Co mam na myśli przez „C +”? Mam na myśli: nie używaj nowych / usuń, unikaj wyjątków, unikaj wirtualnych klas z dziedziczeniem, ewentualnie unikaj dziedziczenia razem, bądź bardzo ostrożny z szablonami, używaj funkcji wbudowanych zamiast makr i używaj constzmiennych zamiast #defines.

Pracuję zarówno w C, jak i C ++ w systemach wbudowanych od ponad dekady, a część mojego młodego entuzjazmu dla C ++ zdecydowanie się skończyła z powodu problemów w świecie rzeczywistym, które wstrząsają naiwnością. Widziałem najgorsze C ++ w systemach wbudowanych, które chciałbym nazwać „programiści CS oszaleli w świecie EE”. W rzeczywistości nad tym pracuję z moim klientem, aby ulepszyć tę jedną bazę kodów, którą mają między innymi.

Niebezpieczeństwo związane z C ++ polega na tym, że jest to bardzo potężne narzędzie, podobnie jak miecz obosieczny, który może odciąć zarówno rękę, jak i nogę, jeśli nie jest odpowiednio wykształcony i zdyscyplinowany w swoim języku i samym programowaniu. C jest bardziej jak miecz obosieczny, ale wciąż równie ostry. W C ++ zbyt łatwo jest uzyskać bardzo wysoki poziom abstrakcji i tworzyć zaciemnione interfejsy, które stają się bez znaczenia w dłuższej perspektywie, a częściowo jest to spowodowane elastycznością C ++ w rozwiązywaniu tego samego problemu z wieloma różnymi funkcjami językowymi (szablony, OOP, procedury, Szablony RTTI, OOP +, przeciążanie, wstawianie).

Ukończyłem dwa 4-godzinne seminaria na temat oprogramowania wbudowanego w C ++ prowadzone przez guru C ++, Scotta Meyersa. Wskazał kilka rzeczy na temat szablonów, których nigdy wcześniej nie brałem pod uwagę, oraz o ile bardziej mogą one pomóc w tworzeniu krytycznego dla bezpieczeństwa kodu. Najważniejsze jest to, że nie można mieć martwego kodu w oprogramowaniu, które musi spełniać rygorystyczne wymagania dotyczące bezpieczeństwa. Szablony mogą ci w tym pomóc, ponieważ kompilator tworzy tylko potrzebny kod podczas tworzenia szablonów. Jednak należy lepiej poznać ich zastosowanie do prawidłowego projektowania tej funkcji, co jest trudniejsze do osiągnięcia w C, ponieważ linkery nie zawsze optymalizują martwy kod.

Scott Meyers jest bardzo wielkim zwolennikiem szablonów i rozsądnego korzystania z inliningu, i muszę powiedzieć, że nadal jestem sceptyczny wobec tego, że jestem ostrożny w kwestii szablonów. Mam tendencję do unikania ich, chociaż mówi, że powinny być stosowane tylko tam, gdzie stają się najlepszym narzędziem. Wskazuje również, że C ++ daje narzędzia do tworzenia naprawdę dobrych interfejsów, które są łatwe w użyciu i utrudniają niewłaściwe użycie. Ponownie, to jest najtrudniejsza część. Trzeba przejść do poziomu biegłości w C ++, zanim będziesz mógł wiedzieć, jak stosować te funkcje w najbardziej efektywny sposób, aby być najlepszym rozwiązaniem projektowym.

To samo dotyczy OOP. W świecie osadzonym musisz zapoznać się z kodem, który kompilator wypluje, aby dowiedzieć się, czy możesz poradzić sobie z kosztami polimorfizmu w czasie wykonywania. Musisz także być gotów wykonać pomiary, aby udowodnić, że Twój projekt spełni wymagania dotyczące terminu. Czy ta nowa klasa InterruptManager spowoduje, że moje opóźnienie przerwań będzie zbyt długie? Istnieją inne formy polimorfizmu, które mogą lepiej pasować do twojego problemu, takie jak polimorfizm w czasie połączenia, który C może również robić, ale C ++ może to zrobić poprzez wzorzec projektowy Pimpl (nieprzezroczysty wskaźnik) .

Mówię to wszystko, że C ++ ma swoje miejsce w świecie osadzonym. Możesz nienawidzić tego, co chcesz, ale to nie odchodzi. Można go napisać w bardzo wydajny sposób, ale trudniej jest nauczyć się, jak robić to poprawnie niż w C. Może czasem działać lepiej niż C w rozwiązywaniu problemu i czasami wyrażać lepszy interfejs, ale znowu musisz kształć się i nie bój się uczyć.


1
Jest to zgodne z tym, co przeczytałem od innych konsultantów systemów wbudowanych. Zawsze uczono mnie, że z C nieustannie wycinasz siebie, ale błąd w C ++ będzie rzadszy, ale kiedy spieprzysz, stracisz nogę. Dziękuję za napisanie jasnej odpowiedzi z pewną wiedzą fachową, której nie mam.
Kortuk

3
Na notatkę informatyki, że główne firmy wariują na ziemi EE. W mojej pracy najgorszy kod, jaki mieliśmy, napisał major CS. Spędziliśmy wieczność, próbując nauczyć go, czym jest sprzęt. Stworzył ustrukturyzowany system za pomocą UML i zbudował na nim cały system w java, stosując odpowiednie dziedziczenie i tak dalej. Działało, dopóki nic się nie zmieniło, a potem dodawanie funkcji lub całkowite przeprojektowanie było złym zadaniem łatania. Kod prawie nie nadaje się do użytku z powodu tego, jak dokładnie zaciemnił on całą dziedziczenie.
Kortuk

2
Gryzą cię nie tylko błędy, ale i nie dający się utrzymać kod. Pewnie, kiedy zaczynasz ten projekt przy użyciu tych schludnych funkcji C ++, wszystko idzie płynnie, ale po 2 lub 3 latach entropia zaczyna się, jeśli nie wkładasz żadnych wysiłków w refaktoryzację w miarę rozwoju. Właśnie z tym mam teraz do czynienia… kod z czasem gnije szybciej w C ++.
Jay Atkinson

2
UML i statemachines. Naprawdę musisz zajrzeć do materiałów Miro Samka na state-machine.com . Zbudował wydajny system, który można łatwo refaktoryzować i zmieniać, ale zajmie to trochę czasu.
Jay Atkinson

2
To zależy od twojego systemu i ilości dostępnej pamięci. Czy piszesz kod na 8-bitowej karcie micro z bardzo małą pamięcią RAM? W takim razie lepiej byłoby uniknąć szaleństwa na abstrakcyjnych interfejsach. Jeśli piszesz coś w rodzaju 32-bitowych systemów osadzonych z płytami pamięci, wybierz to. Naprawdę musisz to zważyć. Na przykład za każdym razem, gdy umieścisz świat jako „wirtualny” na tej klasie, otrzymasz dodatkowy wskaźnik, który może być 8-bitowy, 16-bitowy lub 32-bitowy w zależności od systemu, dla każdej pojedynczej instancji, którą zadeklarujesz tego obiektu. Nawet nie zdajesz sobie sprawy, a stary
Jay Atkinson

56

C ++ jest absolutnie odpowiedni dla systemów wbudowanych. Teraz używam obecności / braku dobrych narzędzi programistycznych (lub ich braku) jako mojego głównego kryterium przy wyborze konkretnego mikroprocesora.

Obszary C ++, które są dobre w systemach wbudowanych, ponieważ mają niskie koszty zasobów:

  • modułowość wynikająca z dobrego wykorzystania klas / struktur
  • szablony, jeśli kompilator dobrze je skompiluje. Szablony są dobrym narzędziem do ponownego wykorzystania algorytmów dla różnych typów danych.

OK obszary:

  • funkcje wirtualne - kiedyś byłem temu przeciwny, ale koszt zasobów jest bardzo mały (jeden vtable na klasę , nie na obiekt; jeden wskaźnik do vtable na obiekt; jedna operacja dereferencji na wywołanie funkcji wirtualnej) i duża zaleta tego jest to, że pozwala mieć tablicę zawierającą kilka różnych typów obiektów, bez konieczności wiedzieć, jaki to jest typ. Użyłem tego niedawno, aby mieć tablicę obiektów, z których każdy reprezentuje urządzenie I2C, każdy z osobnymi metodami.

Obszary, z których nie należy korzystać, głównie z powodu narzutu w czasie wykonywania, który jest niedopuszczalny w małych systemach:

  • dynamiczny przydział pamięci - inni o tym wspominali, ale innym ważnym powodem, aby nie używać dynamicznego przydziału pamięci jest to, że oznacza to niepewność w czasie; wiele powodów, dla których warto korzystać z systemów wbudowanych, dotyczy aplikacji działających w czasie rzeczywistym.
  • RTTI (informacje o typie wykonania) - koszt pamięci jest dość duży
  • wyjątki - zdecydowanie nie, ze względu na osiągniętą prędkość wykonania

Dzięki za wkład. Ciekawe i bardzo podobne do tego, co przeczytałem.
Kortuk

1
W rzeczywistości dynamiczny przydział pamięci jest w porządku, a czasem nieunikniony. Problemem jest alokacja pamięci dynamicznej (i późniejsze ponowne użycie). RTTI to świnia pamięci, zgadzam się co do tego. Ale jaki jest problem z wyjątkami?
Wouter van Ooijen

1
@WoutervanOoijen: Problem z wyjątkami polega na tym, że jeśli foowywołuje barwewnątrz bloku try/ catchi bartworzy niektóre obiekty i wywołania boz, co generuje wyjątek, system musi jakoś wywołać destruktory dla obiektów barutworzonych przed przywróceniem kontroli foo. O ile wyjątki nie zostaną całkowicie wyłączone, nie barbędzie w stanie wiedzieć, czy bozmożna je wyrzucić, i dlatego musi zawierać dodatkowy kod, aby umożliwić taką możliwość. Chciałbym zobaczyć odmianę C ++ z „sprawdzonymi wyjątkami”, aby sobie z tym poradzić; jeśli procedury, które mogłyby pozwolić na uniknięcie wyjątków ...
supercat

1
... musiało zostać zadeklarowane jako takie, wówczas kompilator musiałby tylko dołączyć kod obsługi wyjątków do wywołujących takich procedur. Oczywiście, dodanie wszystkich wymaganych deklaracji, przy jednoczesnym unikaniu niepotrzebnych deklaracji, byłoby nieco uciążliwe, ale pozwoliłoby na stosowanie wyjątków w miejscach, w których są przydatne, bez dodawania kosztów ogólnych tam, gdzie nie są.
supercat

3
@WoutervanOoijen: Nawiasem mówiąc, gdybym projektował ABI do obsługi wyjątków na ARM, wskazałbym, że kod, który wywołuje procedurę, która może wyjść przez wyjątek, powinien mieć R14 wskazuje adres dwa bajty przed pożądanym adresem zwrotnym (to by występują naturalnie, jeśli dzwoniący zastosował się do instrukcji CALL z 16-bitowym słowem). Wywołana procedura kończy się normalnie przez add r15,r14,#2zamiast mov r15,r14; ujść przez wyjątku ldrhs r0,[r14] / add r15,r14,r0. Zerowy koszt cyklu dla normalnego wyjścia i bez ograniczeń ramek stosu.
supercat

36

Tak, C ++ jest z pewnością odpowiedni dla systemów wbudowanych. Najpierw wyjaśnijmy kilka nieporozumień na temat różnicy między C i C ++:

We wbudowanej mikro zawsze będziesz musiał ostrożnie używać języków wysokiego poziomu, jeśli martwisz się ograniczeniami czasowymi lub przestrzennymi. Na przykład wiele MCU nie radzi sobie dobrze ze wskaźnikami, a więc są bardzo nieefektywne podczas używania stosu. Oznacza to, że musisz uważać na przekazywanie zmiennych do funkcji, używanie tablic i wskaźników oraz rekurencję. Prosta linia C, taka jak:

a[i] = b[j] * c[k];

może wygenerować około 4 stron instrukcji w zależności od charakteru tych zmiennych.

Kiedykolwiek używasz żadnej języku wysokiego poziomu, i jesteś zaniepokojony ograniczeniami czasu i przestrzeni, to trzeba wiedzieć, jak każdy cechą tego języka przekłada się na swojej instrukcji maszynowych MCU (przynajmniej każdą funkcję, której używasz). Dotyczy to C, C ++, Ada, cokolwiek. Prawdopodobnie wszystkie języki będą zawierały funkcje, które nie tłumaczą się skutecznie na małych MCU. Zawsze sprawdzaj listę dezasemblacji, aby upewnić się, że kompilator nie generuje ryz instrukcji dotyczących czegoś trywialnego.

Czy C nadaje się do wbudowanych MCU? Tak, o ile masz oko na wygenerowany kod.
Czy C ++ jest odpowiedni dla osadzonych MCU? Tak, o ile masz oko na wygenerowany kod.

Oto dlaczego uważam, że C ++ jest lepszy niż C nawet na 8-bitowych MCU: C ++ zapewnia ulepszoną obsługę:

  • Ukrywanie danych
  • Silniejsze pisanie / sprawdzanie
  • Przezroczystość wielu peryferii za pomocą klas
  • Szablony (jak zawsze, jeśli są używane ostrożnie)
  • Listy inicjalizacji
  • const

Żadna z tych cech nie jest cięższa niż typowe cechy C.

Gdy przesuwasz się do 16 lub 32-bitowych jednostek MCU, wtedy sensowne staje się używanie cięższych funkcji C (stos, stos, wskaźniki, tablice, printf itp.) W ten sam sposób, na bardziej wydajnym MCU staje się właściwe używać cięższych funkcji C ++ (stos, stos, referencje, STL, nowy / usuń).

Więc nie ma potrzeby drżeć na myśl o C ++ na PIC16. Jeśli dobrze znasz swój język i MCU, będziesz wiedział, jak skutecznie używać ich obu razem.


3
To bardzo dobrze wyrażona i rozsądna odpowiedź na pytanie. +1 na zdrowie!
vicatcu

1
a[i] = b[j] * c[k];może wygenerować około 4 stron instrukcji w zależności od charakteru tych zmiennych”. Jeśli robi to MCU / kompilator, dzieje się tak dlatego, że używasz jakiegoś hobbystycznego procesora z garażu z lat 80.
Lundin,

@Lundin - Westchnienie. Nie, oznacza to, że używasz taniego małego MCU, które jest zaprojektowane tak małe i tak tanie, jak to możliwe, aby nie mieć skomplikowanych rzeczy, takich jak indeksowanie stosu.
Rocketmagnet

2
@Rocketmagnet Ok może w latach 90.? W dzisiejszych czasach kiepskie 8 gorzków ma tę samą cenę co 32 gorzkie. Jedynym powodem wyboru tego pierwszego jest bieżące zużycie. A jeśli chodzi o te wyjątkowo kiepskie 8-gorzkie bez stosu: jeśli piszesz C zamiast asemblera dla tak ograniczonego MCU, prawdopodobnie robisz to źle. Wygenerowane 4 strony są zatem Twoją własną winą za pisanie zbyt skomplikowanych programów dla CPU, a zasadniczo C jest niewłaściwym narzędziem do zadania. (Zrobiłem to w przeszłości na Freescale RS08, to był bardzo głupi pomysł.)
Lundin

@Lundin 32-bitowy procesor nie jest potrzebny szybciej niż 16-bit. Było to widoczne już w czasach, gdy w świecie komputerów PC odbywała się zmiana programu z 16 na 32 bity.
Barleyman

24

Zawsze uważam te debaty za interesujące. Nie tyle za intelektualną dyskusję na temat zalet i wad różnych dostępnych języków, ale dlatego, że zazwyczaj można ustalić czyjąś postawę na ten temat w oparciu o pracę / doświadczenie / obszar zainteresowania. To właśnie tam, z argumentami „przedwczesnej optymalizacji”, w których specjaliści CS i programiści cytują Knutha w lewo i prawo, a ci, którzy pracują w prawdziwym świecie, w którym liczy się wydajność, uważają, że wszyscy są szaleni (jestem członkiem tej ostatniej grupy Aby być uczciwym).

Na koniec dnia możesz opracować doskonałe oprogramowanie w C lub C ++ lub wstawić tutaj język . Wszystko sprowadza się do możliwości programisty, a nie języka. Bycie ekspertem w języku jest zwykle wymagane tylko wtedy, gdy wybrałeś niewłaściwy język na początku, a teraz musisz go wypaczać w celu rozwiązania problemu, w większości przypadków są to jedyne sytuacje, w których musisz zagłębić się w niejasne funkcje lub kompilator sztuczki, aby osiągnąć cel.

Często słyszę, jak ludzie rozpoczynają te argumenty: „Jestem ekspertem w języku X i bla bla”. Szczerze od razu zdyskredytuję tych ludzi, ponieważ moim zdaniem podeszli już do problemu z niewłaściwego punktu widzenia, a wszystko po nim jest skażone poprzez chęć użycia narzędzia do rozwiązania problemu i pokazania, jak „fajne” jest.

Tak często obserwuję, jak programiści najpierw wybierają zestaw narzędzi, a następnie próbuję zgiąć go do drugiego problemu, co jest całkowicie błędne i skutkuje bzdurnymi rozwiązaniami.

Jak wspomniałem w komentarzu do innej odpowiedzi, te wojny językowe często przekształcają się w argument, że język X pozwala programiście robić bardziej głupie rzeczy. Mimo że zabawne są do przeczytania, wszystkie te stwierdzenia naprawdę oznaczają, że masz problem z zatrudnieniem dobrych programistów i musisz bezpośrednio rozwiązać ten problem, zamiast próbować pomóc w tej sytuacji, nadal zatrudniając złych programistów i wybierając narzędzia, które mogą zrobić tak mało uszkodzenie jak to możliwe.

Moim zdaniem dobrzy programiści, niezależnie od tego, czy zajmują się tworzeniem oprogramowania, czy sprzętu, badają problem, projektują rozwiązanie i znajdują narzędzia, które pozwalają im wyrazić rozwiązanie w „najlepszy sposób”. Nie powinno mieć znaczenia, czy wymagane narzędzie jest czymś, z czego nigdy wcześniej nie korzystałeś, po tym, jak użyjesz 3-4 języków / narzędzi programistycznych w projektach, które wybierają nowe, powinno to mieć minimalny wpływ na twój czas programowania.

Oczywiście „najlepszy sposób” jest pojęciem subiektywnym i należy je również zdefiniować na etapie badań. Należy wziąć pod uwagę wiele problemów: wydajność, łatwość wyrażania, gęstość kodu itp. W zależności od danego problemu. Z tego powodu nie umieściłem konserwacji na tej liście, nie dbam o to, jaki język wybierzesz, jeśli wybrałeś odpowiednie narzędzie i poświęciłeś czas na zrozumienie problemu, który powinien pojawić się „za darmo”. Trudny w utrzymaniu kod jest często wynikiem wyboru niewłaściwego narzędzia lub złej struktury systemu, co powoduje brzydki, zepsuty bałagan, aby działał.

Twierdzenie, że jakikolwiek język jest „lepszy” niż jakikolwiek inny, jest głupie bez definiowania konkretnego problemu. Podejście obiektowe nie zawsze jest lepsze niż podejście funkcjonalne. Istnieją pewne problemy, które bardzo dobrze nadają się do paradygmatu projektowania obiektowego. Jest wiele takich osób. To samo można powiedzieć o wielu funkcjach językowych, z których ludzie wydają się lubić czerpać przyjemność.

Jeśli spędzasz ponad 20% swojego czasu na problemie z pisaniem kodu, prawdopodobnie produkujesz bardzo słaby system lub masz bardzo słabych programistów (lub nadal się uczysz). Powinieneś spędzać większość czasu z góry na schematowaniu problemu i określaniu, w jaki sposób różne elementy aplikacji wchodzą w interakcje. Umieszczenie grupy utalentowanych programistów w pokoju z tablicą znaczników i problemem do rozwiązania i powiedzenie im, że nie wolno im pisać żadnego kodu ani wybierać żadnych narzędzi, dopóki nie poczują się dobrze z całym systemem, aby zrobić więcej, aby poprawić jakość wydajność i szybkość rozwoju niż wybór jakiegokolwiek nowego, gorącego narzędzia, które gwarantuje skrócenie czasu rozwoju. (spójrz na rozwój scrum jako odniesienie przeciwnego bieguna do mojego argumentu)

Często niefortunną rzeczywistością jest to, że wiele firm może mierzyć wartość dewelopera tylko na podstawie liczby zapisanych wierszy lub widząc „namacalne wyniki”. Widzą 3 tygodnie w pokoju z tablicą jako utratę wydajności. Deweloperzy są często zmuszani do przechodzenia przez fazę „przemyślenia” lub są zmuszeni do korzystania z narzędzi określonych przez pewne problemy polityczne w firmie: „Brat mojego szefa pracuje dla IBM, abyśmy mogli korzystać tylko z ich narzędzi”, tego rodzaju śmieci . Albo, co gorsza, otrzymujesz stale zmieniający się zestaw wymagań od firmy, ponieważ nie są oni w stanie przeprowadzić odpowiednich badań rynku lub nie rozumieją wpływu zmian na cykl rozwoju.

Przepraszam, że jestem trochę nie na temat tego zdania, mam dość mocne opinie na ten temat.


2
Teraz nie puka testów jednostkowych na poziomie aplikacji (powyżej sterownika) w niektórych systemach wbudowanych. Natychmiastowe sprzężenie zwrotne z testowaniem jednostkowym i usuwaniem błędów ma pewną wartość na wczesnym etapie rozwoju, ale cały paradygmat TDD, który dał początek projektowi, wydaje mi się trochę mądry. Wolę poświęcić trochę czasu, aby „pomyśleć” o problemie i wykreślić go w mojej głowie, na papierze lub na tablicy, zanim zacznę kodować. Myślę też, że TDD zachęca rynek do nie przeprowadzania wstępnych badań wymagań, ponieważ ma to pomóc w ciągłej zmianie wymagań.
Jay Atkinson,

2
I żeby dodać ostatnią notatkę do mojego bardzo długiego komentarza. Nie potrzebujemy ekspertów językowych, aby popracować nad projektem. Potrzebujemy ekspertów projektantów, którzy potrafią pracować w języku (językach).
Jay Atkinson,

1
PRD = dokument wymagań dotyczących produktu, MRD = dokument wymagań marketingowych, TRD = dokument wymagań technicznych. TDD = Test Driven Development.
Jay Atkinson,

1
@ Mark - Zgadzam się z twoimi sentymentami dotyczącymi projektowania, ale tylko do pewnego stopnia. Myślę, że ciężkie prace projektowe opłaca się, jeśli a) Twoje wymagania są dość stabilne / znane i b) programiści zajmujący się projektowaniem są doświadczeni . W poprzedniej zadanie, miałem za zadanie zaprojektować projekt, który został w dużej mierze opóźniony przez mojego kierownika zespołu i pomyślałem: „Co za głupota! Projekt od samego początku oszczędza pieniądze (por. książka Code Complete)?” Ale podczas kodowania odkryłem mnóstwo rzeczy, o których nie wiedziałem, że mogę ich szukać. Gdybym zrobił dużo projektowania i zminimalizował czas kodu, byłoby to marnotrawstwem. JME.
J. Polfer,

1
@sheepsimulator Oczywiście zgadzam się co do drugiej kwestii, zakładam, że architekci wiodących systemów są doświadczonymi programistami. W pierwszej kwestii nie zgadzam się. Myślę, że im bardziej oczekujesz zmiany wymagań, tym więcej czasu powinieneś poświęcić na etapie projektowania, ponieważ musisz stworzyć dobry, łatwy do zmiany projekt. Wiem, że niektóre filozofie proponują szybki rozwój. W niektórych przypadkach działa to dobrze, jak wielu złych lub niedoświadczonych programistów na personel. Wszystkie te filozofie projektowania sprowadzają się do: „nie modlę się o zaprojektowanie elastycznego systemu, więc nie marnujmy czasu na próby”.
Mark

17

Dowolny język może być odpowiedni dla systemu wbudowanego. Osadzony oznacza po prostu: część większego urządzenia, w przeciwieństwie do komputera bezpłatnego.

Pytanie ma większe znaczenie, gdy zostanie poproszony o (twardy) system czasu rzeczywistego lub ograniczonych zasobów .

W systemie czasu rzeczywistego C ++ jest jednym z najwyższych języków, który jest nadal odpowiedni przy programowaniu pod kątem ścisłych ograniczeń czasowych. Z wyjątkiem użycia sterty (operator wolny) nie ma żadnych konstrukcji, które mają nieokreślony czas wykonania, więc możesz przetestować, czy twój program spełnia wymagania dotyczące czasu, a przy większym doświadczeniu możesz nawet go przewidzieć. Oczywiście należy unikać korzystania ze stosów, chociaż nowy operator nadal może być wykorzystywany do jednorazowego przydziału. Konstrukty oferowane przez C ++ w stosunku do C można dobrze wykorzystać w systemie osadzonym: OO, wyjątki, szablony.

W przypadku systemów o bardzo ograniczonych zasobach (8-bitowe układy, mniej niż kilka Kb pamięci RAM, brak dostępnego stosu) pełny C ++ może być nieodpowiedni, chociaż może być nadal używany jako „lepszy C”.

Myślę, że to niefortunne, że Ada wydaje się być używana tylko w niektórych niszach. Na wiele sposobów jest to Pascal ++, ale bez ciężaru bycia w górę kompatybilnym z językiem, który był już poważnym bałaganem na początek. (edytuj: poważny bałagan to oczywiście C. Pascal to piękny, ale nieco niepraktyczny język).

================================================== ==============

EDYCJA: Pisałem odpowiedź na nowe pytanie („W jakich przypadkach C ++ jest konieczne, gdy programujemy mikrokontrolery”?), Które zostało zamknięte w odniesieniu do tego, więc dodam to, co napisałem:

Nigdy nie ma nadrzędnego powodu do używania jakiegokolwiek języka programowania, ale mogą istnieć argumenty, które mają większą lub mniejszą wagę w konkretnej sytuacji. Dyskusje na ten temat można znaleźć w wielu miejscach, zajmując stanowiska w zakresie od „nigdy nie używaj C ++ do mikrokontrolera” do „zawsze używaj C ++”. Jestem bardziej z ostatnią pozycją. Mogę podać kilka argumentów, ale musisz sam zdecydować, ile wnoszą one w danej sytuacji (i w jakim kierunku).

  • Kompilatory C ++ są rzadsze niż kompilatory C; w przypadku niektórych obiektów docelowych (na przykład 12 i 14-bitowych rdzeni PIC) w ogóle nie ma kompilatorów C ++.
  • (dobre) programiści C ++ są rzadziej niż (dobrzy) programiści C, szczególnie wśród tych, którzy posiadają (nieco) wiedzę w zakresie elektroniki.
  • C ++ ma więcej konstrukcji niż C, które nie są odpowiednie dla małych systemów (takich jak wyjątki, RTTI, częste używanie sterty).
  • C ++ ma bogatszy zestaw (standardowych) bibliotek niż C, ale konsekwencją poprzedniego punktu jest to, że biblioteki C ++ często używają funkcji, które są nieodpowiednie dla małych systemów, a zatem nie są dostępne w małych systemach.
  • C ++ ma więcej konstrukcji niż C, które pozwalają strzelać sobie w stopę.
  • C ++ ma więcej konstrukcji niż C, które pozwalają zapobiec strzelaniu sobie w stopę (tak, zarówno IMO, jak i poprzednie są prawdziwe).
  • C ++ ma bogatszy zestaw mechanizmów abstrakcji, dzięki czemu umożliwia lepsze sposoby programowania, szczególnie dla bibliotek.
  • Funkcje języka C ++ (na przykład konstruktory / destruktory, funkcje konwersji) utrudniają zapoznanie się z kodem, aby zobaczyć wygenerowaną maszynę, a tym samym koszt przestrzeni i czasu konstrukcji języka.
  • Konstrukcja języka C ++ sprawia, że ​​nie trzeba zdawać sobie sprawy z tego, jak dokładnie są one tłumaczone na kod maszynowy, ponieważ robią „właściwą rzecz” w bardziej abstrakcyjny sposób.
  • Standard języka C ++ ewoluuje szybko i jest szybko wdrażany przez duże kompilatory (gcc, clang, microsoft). C ewoluuje dość mocno, a przyjęcie niektórych nowszych funkcji (tablic wariantów) jest przerażające, a nawet zostało przywrócone w późniejszym standardzie. Ten punkt jest szczególnie interesujący, ponieważ różni ludzie używają go do wspierania przeciwnych pozycji.
  • C ++ jest niewątpliwie ostrzejszym narzędziem niż C. Czy ufasz swoim programistom (lub sobie), że użyje takiego narzędzia do stworzenia pięknej rzeźby, czy boisz się, że sami się skrzywdzą i wolisz raczej mniej piękny, ale mniej ryzykowny produkt ? (Pamiętam, że mój nauczyciel rzeźby powiedział mi kiedyś, że tępe narzędzia mogą w niektórych sytuacjach być bardziej niebezpieczne niż ostre).

Na moim blogu jest kilka artykułów na temat używania C ++ na małych systemach (= mikrokontrolery).


15

Z mojego doświadczenia wynika, że ​​C ++ jest zwykle nieodpowiedni dla małych systemów osadzonych. Rozumiem przez to mikrokontrolery i urządzenia bez OS.

Wiele technik C ++ OOP opiera się na dynamicznej alokacji pamięci. Tego często brakuje w małych systemach.

STL i Boost naprawdę pokazują moc C ++, oba mają ogromne rozmiary.

C ++ zachęca programistę do wyodrębnienia maszyny, gdzie w ograniczonych systemach należy ją objąć.

W ubiegłym roku przeniosłem komercyjny produkt zdalnego pulpitu na telefony komórkowe. Został napisany w C ++ i działał na systemach Windows, Linux i OSX. Ale w dużej mierze opierał się na STL, pamięci dynamicznej i wyjątkach C ++. Aby działało w środowisku WinCE, Symbian i bez OS, przepisanie C było najrozsądniejszą opcją.


Zgadzam się w odniesieniu do małych systemów, ale myślę, że mamy różne definicje małych systemów. Gdy masz 1kB pamięci ROM i dobrze napisany kod C zajmuje prawie wszystkie bajty pamięci ROM, to jest to mały system.
Kortuk

6
Nie twierdzę, że C nie może mieć mniejszej powierzchni, ale mógłbyś nadal używać C ++ i uzyskać bardzo podobny wynik do projektowania tego, co właśnie omówiono. Myślę, że problem polega na tym, że większość programistów OOP jest przyzwyczajona do systemów z pamięcią dynamiczną i przy użyciu bardzo nieefektywnych konstrukcji, co skutkuje całkowicie bezużytecznym kodem dla systemów o niższej mocy.
Kortuk

4
więc, co mówisz, nie chcesz używać C ++, chcesz użyć czegoś między C i C ++ (nazwijmy to C +?). W takim przypadku zgadzam się, że wiele bzdur w ludziach C ++ używa tylko dlatego, że jest dostępna, a nie dlatego, że jest optymalna. Prawie każdy język jest w stanie wygenerować dobry, szybki kod, to kwestia tego, jak jest używany. Większość świętych wojen o języki nie wynika z możliwości językowych, ale spiera się o to, jak idiota może robić idiotyczne rzeczy, co jest naprawdę idiotycznym argumentem: p
Mark

2
„Większość świętych wojen o języki nie wynika z możliwości językowych, ale spiera się o to, jak idiota może robić idiotyczne rzeczy, co jest naprawdę idiotycznym argumentem”. To było bardzo przyjemne zdanie. Potrzebuję twojego nazwiska, żeby móc je zacytować.
Kortuk

1
Jednak tak naprawdę nie używam pamięci dynamicznej w C. Nigdzie nie muszę tego mieć. W dłuższej perspektywie czytałem, że może się bardzo segmentować i zacząć powodować problemy. Muszę mieć bardzo jasno zaprojektowane przypadki wyczerpania pamięci i muszę być w stanie dokładnie monitorować, ile pozostało.
Kortuk

11

Mam nadzieję, że dodam więcej światła niż ciepła do dyskusji na temat C ++ w systemach bez systemu metalowego i zasobów.

Problemy w C ++:

  • Wyjątki stanowią w szczególności problem z pamięcią RAM, ponieważ wymagany „bufor awaryjny” (na przykład dotyczy wyjątku braku pamięci) może być większy niż dostępna pamięć RAM i z pewnością marnuje mikrokontrolery. Aby uzyskać więcej informacji, patrz n4049 i n4234 . Powinny być wyłączone (co jest obecnie nieokreślonym zachowaniem, więc bądź pewien i nigdy nie rzucaj). SG14 pracuje obecnie nad lepszymi sposobami na zrobienie tego.

  • RTTI prawdopodobnie nigdy nie jest warte narzutu, należy go wyłączyć

  • Duże kompilacje debugowania, chociaż nie jest to problem w klasycznym projektowaniu pulpitu, jeśli debugowanie nie mieści się w układzie, może to stanowić problem. Problem wynika z kodu szablonowego lub dodatkowych wywołań funkcji dodanych dla przejrzystości. Te dodatkowe funkcje zostaną ponownie usunięte przez optymalizator, a dodatkowa przejrzystość lub elastyczność może być wielką zaletą, jednak w kompilacjach debugowania może to stanowić problem.

  • Przydział stert. Chociaż STL pozwala na użycie niestandardowych alokatorów, może to być skomplikowane dla większości programistów. Alokacja sterty nie jest deterministyczna (tzn. Nie jest trudna w czasie rzeczywistym), a fragmentacja może prowadzić do nieoczekiwanych sytuacji braku pamięci, mimo że pracowała w testowaniu. Prowadzenie ksiąg przez stertę w celu śledzenia wolnej przestrzeni i różnych rozmiarów może być problemem dla małych obiektów. Zazwyczaj lepiej jest używać alokacji puli (zarówno w C, jak i C ++), ale może to być nieprawidłowe w przypadku programistów C ++, którzy używali tylko sterty.

  • Polimorfizm środowiska wykonawczego i inne wywołania pośrednie są zwykle dużym hitem wydajności, problem jest zwykle większy, ponieważ optymalizator nie widzi ich bardziej niż faktyczne pobieranie i przeskakiwanie do adresu. Z tego powodu należy unikać wywołań pośrednich w C i C ++, gdzie podobnie jak w C ++ są one bardziej zakorzenione w kulturze (i są całkiem przydatne w innych domenach).

  • niejawne połączenie z biblioteką Clib może być problematyczne. Może być sprzeczne z intuicją, że problemy z biblioteką znajdują się w kategorii C ++, ale problem wynika z niejawnego udostępniania zasobów w współbieżnych środowiskach (udostępnianie jest bardziej wyraźne w C). Korzystanie ze wspólnej implementacji newLib często powoduje wiele wzdęć, które zwykle nie są potrzebne w UC, z drugiej strony newLibNanno nie jest ponownie wysyłany, więc dostęp do niego musi zostać zserializowany (tutaj nadmierne uproszczenie). Jest to problem również dla C, ale dostęp jest bardziej wyraźny. Zasadniczo nie należy zasadniczo używać niczego z przestrzeni nazw std w kontekście ISR, chyba że masz pewność, że nie uzyska dostępu do stanu w bibliotece clib (na przykład errorno lub sterta). Jest to również ważne, jeśli używasz wątków (wolę RTC), aby zastąpić nowe i usunąć, aby zsynchronizować dostęp do malloc i za darmo.

Podsumowując, C ++ ma pewne problemy, ale zasadniczo można je naprawić lub uniknąć.

Teraz dla C, tutaj problemem jest wyższy porządek. Nie mam zdolności syntaktycznej w C do abstrakcyjnego tworzenia rzeczy w sposób umożliwiający mi optymalizację lub sprawdzanie niezmienników w czasie kompilacji. Dlatego nie mogę poprawnie hermetyzować rzeczy w taki sposób, że użytkownik nie musi wiedzieć, jak działają, aby z nich korzystać, a większość moich wykrywania błędów jest wykonywana w czasie wykonywania (co nie tylko jest za późno, ale także powoduje dodatkowe koszty). Zasadniczo jedynym sposobem, aby być ogólnym w C, są dane, przekazuję ciąg formatu do printf lub scanf, który jest oceniany na przykład w czasie wykonywania. Kompilatorowi trudno jest udowodnić, że nie korzystam z niektórych opcji, które teoretycznie są możliwe po przekazaniu odpowiednich danych, co oznacza potencjalne generowanie martwego kodu i utratę potencjału optymalizacyjnego.

Wiem, że mogę tutaj rozpętać burzę, ale moje doświadczenie z 32-bitowymi mikrokontrolerami polega na tym, że w porównaniu jabłek do jabłek porównanie C i C ++ zarówno napisanych przez ekspertów (jak w C ++ potencjalnie wysoce szablonowych) C ++ jest znacznie bardziej wydajnym językiem, gdy tylko wszystko musi być w ogóle ogólne (jak w każdej bibliotece) i są zasadniczo równoważne w przypadkach innych niż ogólne. Nowicjuszowi łatwiej jest również wykorzystać wiedzę eksperta w dziedzinie implementacji bibliotek w C ++.

Jednocześnie jest tak naprawdę naprawdę niewiele funkcji, do których nie mogę przekazać niepoprawnych danych, gdy tylko dane wejściowe nie są liczbą całkowitą, ale somethingdla której akurat używam liczby wewnętrznej jako metody reprezentacji, istnieje możliwość jej uzyskania niepoprawnie (przekaż niepoprawną wartość lub „inną rzecz” zamiast „czegoś”). W C moją jedyną metodą sprawdzenia, czy użytkownik nie pomylił się, jest w czasie wykonywania. W C ++ mam możliwość wykonywania niektórych kontroli, nie wszystkich kontroli, ale niektóre kontrole w czasie kompilacji, które są bezpłatne.

Pod koniec dnia zespół C jest często tak potężny, jak jego najsłabszy programista, a korzyści wynikające z kodu wynikają z gry wieloosobowej równej 1 lub obniżenia wydajności. Rozumiem przez to, że jest to albo wysoka wydajność dla jednego i tylko jednego wyjątkowego zadania w unikalnym środowisku z unikalnymi decyzjami projektowymi, albo jest wystarczająco ogólna, aby można ją było stosować w wielu środowiskach (inny mikrokontroler, inna strategia zarządzania pamięcią, inne opóźnienie vs. kompromisy związane z wydajnością itp. itp.), ale wiąże się to z nieodłącznym kosztem wydajności.

W C ++ rzeczy mogą być enkapsulowane przez ekspertów i używane w wielu środowiskach, w których generowanie kodu czasowego dostosowuje się do konkretnego zadania, a sprawdzanie statyczne powstrzymuje użytkowników przed robieniem głupich rzeczy przy zerowym koszcie. Tutaj mamy znacznie mniejszy kompromis między byciem ogólnym a szybkim, a zatem ostatecznie z punktu widzenia kosztów i korzyści są bardziej wydajne, bezpieczniejsze i bardziej produktywne.

Jest to słuszna krytyka, że ​​wciąż brakuje dobrych bibliotek C ++ do osadzania, może to prowadzić do pragmatycznych decyzji o używaniu głównie C na kompilatorze C ++. Decyzje o zastosowaniu tylko C w projekcie są zasadniczo albo motywowane ideologicznie, z potrzeby starszego wsparcia lub przyznania, że ​​zespół nie jest wystarczająco zdyscyplinowany, aby powstrzymać się od bardzo wybranego zestawu głupich rzeczy, które można zrobić w C ++, ale nie w C i jednocześnie zdyscyplinowany na tyle, aby nie robić żadnego z głupszych rzeczy, przed którymi nie można się bronić w C, ale w C ++.


Miły dodatek do mojej odpowiedzi :) Kim byłby ten tajemniczy kochanek C ++? Jego profil brzmi: „Najwyraźniej użytkownicy ci wolą zachować tajemnicę”. (kiepski angielski, BTW) ALE AHA lokalizacja to „Bochum, Niemcy” ..... Do zobaczenia na konferencji!
Wouter van Ooijen

Ach tak, zaktualizowałem mój profil;) miło wiedzieć, że przyszedłeś do emBO ++, to będzie dobry tłum
odinthenerd

10

Moje pochodzenie: tuż po szkoleniu u starych programistów Bell Labs; pracuję od 3 lat, 2 nad studiami licencjackimi; akwizycja danych / kontrola procesu w VB.NET. Spędziłem 1,5 roku pracując nad aplikacją korporacyjnej bazy danych w VB6. Obecnie pracuje nad projektem dla wbudowanego komputera z 2 GB pamięci, 512 MB pamięci RAM, procesorem 500 MHz x86; kilka aplikacji działających jednocześnie w C ++ z mechanizmem IPC pomiędzy nimi. Tak, jestem młoda.

Moja opinia: Myślę, że C ++ może działać skutecznie w środowisku, które napisałem powyżej . Trzeba przyznać, że wydajność aplikacji w czasie rzeczywistym nie jest wymagana dla aplikacji, w której korzystam, aw niektórych aplikacjach wbudowanych może to stanowić problem. Ale oto rzeczy, których się nauczyłem:

  • C ++ zasadniczo różni się od C (tzn. Nie ma C / C ++). Podczas gdy wszystko, co jest poprawne, C jest poprawne C ++, C ++ jest bardzo innym językiem i trzeba nauczyć się programowania w C ++, a nie C, aby skutecznie używać go w każdej sytuacji. W C ++ musisz programować obiektowo, nie proceduralnie, a nie hybrydę dwóch (duże klasy z dużą ilością funkcji). Ogólnie rzecz biorąc, powinieneś skupić się na tworzeniu małych klas z kilkoma funkcjami i skomponować wszystkie małe klasy razem w większe rozwiązanie. Jeden z moich współpracowników wyjaśnił mi, że programowałem proceduralnie w obiektach, co jest wielkim bałaganem i jest trudne do utrzymania. Kiedy zacząłem stosować techniki bardziej obiektowe, zauważyłem, że mój kod / łatwość obsługi / odczytu wzrosła.

  • C ++ zapewnia dodatkowe funkcje w postaci programowania obiektowego, które mogą zapewnić sposób uproszczenia kodu, aby ułatwić czytanie / utrzymanie . Szczerze mówiąc, nie sądzę, że jest wiele na drodze do poprawy wydajności / wydajności przestrzeni w robieniu OOP. Ale myślę, że OOP to technika, która może pomóc w podzieleniu złożonego problemu na wiele małych kawałków. Jest to pomocne dla osób pracujących nad kodem, elementu tego procesu, którego nie należy ignorować.

  • Wiele argumentów przeciwko C ++ dotyczy przede wszystkim dynamicznej alokacji pamięci. C ma również ten sam problem. Możesz pisać aplikacje zorientowane obiektowo bez użycia pamięci dynamicznej, chociaż jedną z zalet korzystania z obiektów jest to, że możesz przydzielać te rzeczy dynamicznie w łatwy sposób. Podobnie jak w C, musisz uważać na sposób zarządzania danymi w celu zmniejszenia wycieków pamięci, ale technika RAII upraszcza to w C ++ (automatyczne niszczenie pamięci dynamicznej przez hermetyzację jej w obiektach). W niektórych aplikacjach, gdzie liczy się każda lokalizacja pamięci, może to być zbyt dzikie i wełniste, aby zarządzać.

EDYTOWAĆ:

  • WRT na pytanie „Arduino C ++” : argumentowałbym, że C ++ bez dynamicznego zarządzania pamięcią może być nadal użyteczny. Możesz zorganizować swój kod w obiekty, a następnie umieścić te obiekty w różnych lokalizacjach w aplikacji, skonfigurować interfejsy zwrotne itp. Teraz, gdy rozwijam w C ++, widzę wiele sposobów, w jakie aplikacja z wszystkimi danymi przydzielonymi na stos może być nadal przydatny w przypadku obiektów. Przyznam jednak, że nigdy nie napisałem takiej aplikacji dla Arduino, więc nie mam dowodów na moje roszczenia. Mam kilka okazji, aby rozwinąć Arduino w nadchodzącym projekcie - mam nadzieję, że mogę tam sprawdzić moje twierdzenie.

2
Chciałbym skomentować twoją drugą kwestię, mówisz, że pomaga ona rozbić złożony problem na wiele małych kawałków i tę funkcję należy zignorować. To jest dokładnie powód, dla którego jestem tak pro-C ++. Bardzo duża liczba badań nad programowaniem pokazuje, że liniowy wzrost wielkości programu daje wykładniczy wzrost czasu rozwoju. postępuje to w odwrotny sposób, jeśli można poprawnie podzielić program, wówczas można gwałtownie zmniejszyć czas programowania. To zdecydowanie najważniejsza rzecz.
Kortuk

również na drugim punkcie: samo użycie metodologii projektowania OOP nie daje kodu bardziej podzielonego na przedziały. Jeśli ma się dobry projekt bazowy, to, w jaki sposób można wyrazić ten projekt, należy do autora. OOP nie określa, że ​​poprawnie oddzielasz kod, zapewnia inną opcję, a ponadto wygląd, który zrobiłeś, ale z pewnością nie wymusza dobrego projektu, który zależy od programisty.
Mark

To zawsze jest prawda. Nigdy nie słyszałem o języku, który wymusza dobry design. Myślę, że głównie sugerujemy, że jest to praca programistów, a C ++ ułatwia korzystanie i wdrażanie w zorganizowany sposób.
Kortuk

@ Mark - Zgadzam się. To był dla mnie proces uczenia się.
J. Polfer,

7

Tak, problem z C ++ polega na zwiększonej powierzchni kodu.

W niektórych systemach liczysz bajty, w takim przypadku będziesz musiał zaakceptować koszt działania zbliżony do granic swoich systemów, to zwiększone koszty rozwoju C.

Ale nawet w C, dla dobrze zaprojektowanego systemu, musisz wszystko zamknąć. Dobrze zaprojektowane systemy są trudne, a C ++ daje programistom miejsce na bardzo ustrukturyzowaną i kontrolowaną metodę rozwoju. Nauka OOP wiąże się z pewnymi kosztami, a jeśli chcesz się na nią przełączyć, bardzo ją akceptujesz, aw wielu przypadkach zarząd wolałby kontynuować C i nie płacić kosztów, ponieważ trudno jest zmierzyć wyniki zmiany, która zwiększa wydajność. Artykuł autorstwa guru systemów osadzonych Jacka Ganssle'a można znaleźć tutaj .

Dynamiczne zarządzanie pamięcią to diabeł. Nie bardzo, diabeł ma automatyczną trasę, dynamiczne zarządzanie pamięcią działa świetnie na komputerze, ale możesz oczekiwać, że uruchomisz komputer co najmniej co kilka tygodni. Przekonasz się, że ponieważ system osadzony działa przez 5 lat, dynamiczne zarządzanie pamięcią może się naprawdę zepsuć i zacząć działać. Ganssle omawia w swoim artykule takie rzeczy, jak stos i stos.

W C ++ są pewne rzeczy, które są bardziej podatne na problemy i zużywają wiele zasobów, usuwanie dynamicznego zarządzania pamięcią i szablony to duże kroki, aby utrzymać ślad C ++ bliżej do śladu C. Jest to nadal C ++, nie potrzebujesz dynamiki zarządzanie pamięcią lub szablony do pisania dobrego C ++. Nie zdawałem sobie sprawy, że usunęli wyjątki, uważam wyjątki za ważną część mojego kodu, którą usuwam w wersji, ale używam do tego momentu. W testach terenowych mogę mieć generowane wyjątki, które informują mnie o przechwyceniu wyjątku.


1
Kiedyś zgadzałem się, że ślad kodu jest problemem, ale ostatnio wydaje się, że rozmiar flash ma bardzo mały wpływ na cenę mikrokontrolera, znacznie mniej niż rozmiar pamięci RAM lub liczba pinów IO.
Wouter van Ooijen

Argument dotyczący pamięci dynamicznej jest ważniejszy dla IMO. Widziałem systemy przemysłowe, które mogłyby działać bez przerwy przez kilka tygodni, ale warstwa diagnostyczna (napisana w C ++) ograniczyłaby czas ponownego uruchomienia do około 12 godzin.
Dmitrij Grigoriew

6

Myślałem, że ten anty-C ++ rząd Linusa Torvaldsa był interesujący.

Jedną z absolutnie najgorszych cech C ++ jest to, że sprawia, że ​​wiele rzeczy jest tak zależnych od kontekstu - co oznacza po prostu, że kiedy patrzysz na kod, widok lokalny rzadko daje wystarczająco dużo kontekstu, aby wiedzieć, co się dzieje.

Nie mówi o świecie systemów osadzonych, ale o rozwoju jądra Linuksa. Dla mnie znaczenie ma to: C ++ wymaga zrozumienia szerszego kontekstu i mogę nauczyć się korzystać z zestawu szablonów obiektów, nie ufam sobie, że je zapamiętam, gdy muszę zaktualizować kod za kilka miesięcy.

(Z drugiej strony, obecnie pracuję na urządzeniu wbudowanym przy użyciu Pythona (nie C ++, ale przy użyciu tego samego paradygmatu OOP), który będzie miał dokładnie ten problem. W mojej obronie jest to system wbudowany wystarczająco silny, aby nazwać go PC 10 lat temu.)


5
Możemy się różnić, ale uważam, że otwierając każdy projekt, nie mogę od razu powiedzieć, co się dzieje, ale jeśli wiem coś o tym, co się dzieje i mam coś dobrze zakodowanego w C i coś dobrze zakodowanego w C ++, C ++ zawsze wydaje się bardziej jasny. Nadal musisz zaimplementować enkapsulację dla dobrego rozwoju w C, co C ++ bardzo ułatwia. Właściwe użycie klas może bardzo wyraźnie pokazać, gdzie znajdują się interfejsy, i można je całkowicie obsługiwać przez obiekt.
Kortuk

Całkowicie uzgodniono enkapsulację i klasy. Przeciążenie operatora i dziedziczenie, nie tyle.
pingswept

1
Haha, tak, przeciążenie operatora może być użyte do zaciemnienia funkcji kodu. Jeśli ktoś przeciąża operatora, musi to być z jasnych powodów lub wcale. Dziedziczenia należy używać tylko w szczególnych przypadkach, gdy faktycznie robisz coś, co jest podobne do rodzica z kilkoma dodatkami. Myślę, że nie każdy używałby żadnej funkcji w OOP. Użyłem obu, ale w systemie wbudowanym nie mogę wymyślić przypadku, gdybym to zrobił. Tak jak myślę, że kompilator z limitem 80 znaków w nazwach zmiennych powinien zostać natychmiast wyrzucony.
Kortuk

2
Właśnie zwymiotowałem trochę na myśl o zaprogramowaniu MCU w Pythonie ...
vicatcu

Nie jesteś jedyny, ale jeśli działa dobrze i jest wydajny, mogę wybaczyć.
Kortuk

6

Sądzę, że inne odpowiedzi stanowiły całkiem dobrą argumentację za plusami i minusami oraz czynnikami decyzyjnymi, dlatego chciałbym tylko podsumować i dodać kilka komentarzy.

Dla małych mikrokontrolerów (8-bitowych) nie ma mowy. Po prostu prosisz o zranienie siebie, nie ma zysku, a oddasz za dużo zasobów.

W przypadku wysokiej klasy mikrokontrolerów (np. 32-bitowych, 10 lub 100 MB pamięci RAM i pamięci masowej), które mają przyzwoity system operacyjny, jest to całkowicie OK i, śmiem powiedzieć, nawet zalecane.

Pytanie brzmi: gdzie jest granica?

Nie wiem na pewno, ale kiedy opracowałem system dla 16-bitowego interfejsu użytkownika z 1 MB pamięci RAM i 1 MB pamięci w C ++, żałuję tego później. Tak, działało, ale dodatkowa praca, którą miałem, nie była tego warta. Musiałem go dopasować, dopilnować, aby takie wyjątki nie powodowały wycieków (obsługa OS + RTL była dość błędna i zawodna). Co więcej, aplikacja OO zazwyczaj wykonuje wiele małych alokacji, a narzuty związane z nimi były kolejnym koszmarem.

Biorąc pod uwagę to doświadczenie, zakładam, że w przyszłych projektach wybiorę C ++ tylko w systemach co najmniej 16-bitowych i co najmniej 16 MB na pamięć RAM i pamięć. To arbitralny limit i prawdopodobnie będzie się różnić w zależności od rodzaju aplikacji, stylów kodowania i idiomów itp. Ale z zastrzeżeniem, zaleciłbym podobne podejście.


2
Muszę się z tym nie zgodzić, nie jest nagłym punktem, w którym C ++ staje się akceptowalny ze względu na zasoby systemowe, dobra praktyka projektowania może utrzymać ślad C ++ tam, gdzie jest ślad C. Powoduje to kod z projektami OOP, które zajmują to samo miejsce. Źle napisane C może być równie złe.
Kortuk

1
Zależy to od tego, jak duża jest Twoja aplikacja i ile korzystasz z niektórych funkcji, które wymagają więcej miejsca (np. Szablony i wyjątki). Ale osobiście wolałbym używać C niż ograniczać się do powściągliwego C ++. Ale nawet wtedy będziesz miał narzuty większej RTL, wirtualnych metod, wywołania łańcucha konstruktora / destruktora ... te efekty mogą być złagodzone przez staranne kodowanie, ale wtedy tracisz główny powód używania C ++, abstrakcji i perspektywa na wysokim poziomie.
fceconel 12.01.11

4

Niektóre funkcje C ++ są przydatne w systemach wbudowanych. Istnieją inne, na przykład wyjątki, które mogą być kosztowne i których koszty nie zawsze są oczywiste.

Gdybym miał moje druty, byłby popularny język, który łączyłby to, co najlepsze z obu światów, i zawierał pewne funkcje, których brakuje w obu językach; niektórzy dostawcy zawierają kilka takich funkcji, ale nie ma żadnych standardów. Kilka rzeczy, które chciałbym zobaczyć:

  1. Wyjątek obsługujący trochę bardziej jak Java, gdzie funkcje, które mogą zgłaszać lub ujawniać wyjątki, muszą być jako takie deklarowane. Chociaż wymaganie dotyczące takich deklaracji może być nieco denerwujące z punktu widzenia programowania, poprawiłoby to przejrzystość kodu w przypadkach, w których funkcja może zwrócić dowolną liczbę całkowitą, jeśli się powiedzie, ale może również zawieść. Wiele platform może sobie z tym poradzić niedrogo w kodzie, np. Posiadając wartość zwracaną w rejestrze i wskazanie powodzenia / niepowodzenia w flagi przenoszenia.
  2. Przeciążenie tylko funkcji statycznych i wbudowanych; rozumiem, że organy normalizacyjne dla języka C uniknęły przeciążenia funkcji, aby uniknąć konieczności zmiany nazwy. Zezwolenie na przeciążenie tylko funkcji statycznych i wbudowanych uniknęłoby tego problemu i dałoby 99,9% korzyści z przeciążenia funkcji zewnętrznych (ponieważ pliki .h mogłyby zdefiniować przeciążenia wbudowane pod względem funkcji zewnętrznych o różnych nazwach)
  3. Przeciążenia dla arbitralnych lub określonych wartości stałych parametrów, które można rozwiązać w czasie kompilacji. Niektóre funkcje mogą być wstawiane bardzo skutecznie po przekazaniu z dowolną stałą wartością, ale wstawiane bardzo słabo, jeśli są przekazywane zmienne. Innym razem kod, który może być optymalizacją, jeśli wartość jest stała, może być pesymizacją, jeśli nie jest. Na przykład:
    inline void copy_uint32s (uint32_t * dest, const uint32_t * src, __is_const int n)
    {
      jeśli (n <= 0) zwraca;
      else if (n == 1) {dest [0] = src [0];}
      else if (n == 2) {dest [0] = src [0]; dest [1] = src [1];}
      else if (n == 3) {dest [0] = src [0]; dest [1] = src [1]; dest [2] = src [2];}
      else if (n == 4) {dest [0] = src [0]; dest [1] = src [1]; dest [2] = src [2]; dest [3] = src [3];}
      else memcpy ((void *) dest, (const void *) src, n * sizeof (* src));
    }
    
    Jeśli „n” może być ocenione w czasie kompilacji, powyższy kod będzie bardziej wydajny niż wywołanie memcpy, ale jeśli „n” nie może być ocenione w czasie kompilacji, wygenerowany kod byłby znacznie większy i wolniejszy niż kod, który po prostu o nazwie memcpy.

Wiem, że ojciec C ++ nie przepada za wbudowaną wersją C ++, ale sądzę, że może on zaoferować pewne znaczące ulepszenia w porównaniu do zwykłego używania C.

Czy ktoś wie, czy coś takiego jak wyżej jest rozważane dla jakiegokolwiek rodzaju standardu?



@Joby Taffey: Wydaje mi się, że zredagowałem swój post, aby pominąć wzmiankę, że twórca C ++ nie był zainteresowany osadzonym podzbiorem; Wiem, że były wysiłki, ale z mojego zrozumienia, że ​​tak naprawdę nie zaszli tak daleko. Wydaje mi się, że byłoby standardowe zastosowanie standardowego języka, który byłby dostosowany do procesorów 8-bitowych, a funkcje, takie jak opisane powyżej, wydają się przydatne na każdej platformie. Czy słyszałeś o językach, które oferują coś takiego jak nr 3 powyżej? Wydawałoby się to bardzo przydatne, ale nigdy nie widziałem, żeby oferował to jakikolwiek język.
supercat

„Ojciec C ++” nie ma doświadczenia w programowaniu systemów wbudowanych, więc dlaczego ktokolwiek miałby przejmować się jego opinią?
Lundin

@Lundin: Fakt, że niektórzy wpływowi ludzie dbają o jego opinie w różnych sprawach, wydaje się sam w sobie powodem, dla którego inni to robią. Myślę, że odkąd napisałem powyżej, rosnąca moc szablonów mogła dodać nowe możliwości przeciążenia w oparciu o to, jakie stałe można rozwiązać w czasie kompilacji, choć o wiele mniej czysto, niż gdyby takie rzeczy były obsługiwane jako kompilacja- funkcja czasu (z tego, co rozumiem, należy określić szablon, który powinien wypróbować różne rzeczy w kolejności i przejść do pierwszego, który nie zawiedzie ...
supercat

... ale wymagałoby to od kompilatora marnowania sporo wysiłku na skompilowanie potencjalnych podstawień, które ostatecznie zostałyby odrzucone. Możliwość wyraźniejszego powiedzenia „Jeśli jest to stała, zrób to; w przeciwnym razie zrób to” bez „fałszywych rozruchów” wydaje się czystszym podejściem.
supercat

3

C ++ to więcej niż jeden język programowania:

a) To „lepszy” C b) To język zorientowany obiektowo c) To język, który pozwala nam pisać programy ogólne

Chociaż wszystkie te funkcje mogą być używane osobno, najlepsze wyniki osiąga się, gdy trzy z nich są używane jednocześnie. Niemniej jednak, jeśli wybierzesz tylko jedną z nich, jakość wbudowanego oprogramowania wzrośnie.

a) To „lepsze” C

C ++ jest silnie napisanym językiem; silniejszy niż C. Twoje programy skorzystają z tej funkcji.

Niektórzy boją się wskazówek. C ++ zawiera referencje. Przeciążone funkcje.

I warto powiedzieć: Żadna z tych funkcji nie pojawiła się w większych lub wolniejszych programach.

b) Jest to język obiektowy

Ktoś powiedział w tym poście, że abstrakcja maszyny w mikrokontrolerach nie jest dobrym pomysłem. Źle! Każdy z nas, inżynierów osadzonych, zawsze wyabstrahował maszynę, tylko z innym sintaxem niż C ++. Problem, który widzę w tym argumencie, polega na tym, że niektórzy programiści nie są przyzwyczajeni do myślenia w obiektach, w ten sposób nie widzą korzyści z OOP.

Ilekroć jesteś gotowy do korzystania z urządzenia peryferyjnego mikrokontrolera, jest prawdopodobne, że urządzenie peryferyjne zostało dla nas (od ciebie lub osoby trzeciej) wyodrębnione w formie sterownika urządzenia. Jak powiedziałem wcześniej, ten sterownik używa sintax C, jak pokazuje następny przykład (wzięty bezpośrednio z przykładu NXP LPC1114):

/ * Ustawienia timera dla dopasowania i przerwania w TICKRATE_HZ * /

Chip_TIMER_Reset (LPC_TIMER32_0);

Chip_TIMER_MatchEnableInt (LPC_TIMER32_0, 1);

Chip_TIMER_SetMatch (LPC_TIMER32_0, 1, (timerFreq / TICKRATE_HZ2));

Chip_TIMER_ResetOnMatchEnable (LPC_TIMER32_0, 1);

Chip_TIMER_Enable (LPC_TIMER32_0);

Czy widzisz abstrakcję? Tak więc, używając C ++ do tego samego celu, abstrakcja jest przenoszona na wyższy poziom dzięki mechanizmowi abstrakcji i enkapsulacji C ++, przy zerowym koszcie!

c) Jest to język, który pozwala nam pisać ogólne programy

Programy ogólne są realizowane za pomocą szablonów, a szablony również nie wiążą się z żadnymi kosztami dla naszych programów.

Poza tym za pomocą szablonów osiąga się statyczny polimorfizm.

Metody wirtualne, RTTI i wyjątki.

Podczas korzystania z metod wirtualnych istnieje kompromis: lepsze oprogramowanie vs. pewna obniżka wydajności. Pamiętaj jednak, że powiązanie dynamiczne prawdopodobnie zostanie zaimplementowane przy użyciu wirtualnej tabeli (tablicy wskaźników funkcji). Robiłem to samo w C wiele razy (nawet regularnie), więc nie widzę wad korzystania z metod wirtualnych. Ponadto metody wirtualne w C ++ są bardziej eleganckie.

Na koniec rada na temat RTTI i wyjątków: NIE UŻYWAJ ich w systemach wbudowanych. Unikaj ich za wszelką cenę !!


2

Moje tło, osadzone (MCU, PC, UNIX, inne), w czasie rzeczywistym. Bezpieczeństwo krytyczne. Przedstawiłem poprzedniego pracodawcę firmie STL. Już tego nie robię.

Trochę treści Flame

Czy C ++ jest odpowiedni dla systemów wbudowanych?

Meh C ++ jest trudny do napisania i trudny do utrzymania. C + jest w porządku (nie używaj niektórych funkcji)

C ++ w mikrokontrolerach? RTOS? Tostery? Komputery wbudowane?

Znowu mówię Meh. C + nie jest takie złe, ale ADA jest mniej bolesne (a to naprawdę coś mówi). Jeśli masz szczęście, tak jak ja, możesz zainstalować wbudowaną Javę. Sprawdzony dostęp do tablicy i brak arytmetyki wskaźnika zapewnia bardzo niezawodny kod. Garbage collectors in embedded Java nie mają najwyższego priorytetu, a zakres pamięci i ponownego użycia obiektów jest ograniczony, więc dobrze zaprojektowany kod może działać wiecznie bez GC.

Czy OOP jest użyteczny w mikrokontrolerach?

Jasne, że tak. UART jest obiektem ..... DMAC jest obiektem ...

Maszyny stanu obiektu są bardzo łatwe.

Czy C ++ usuwa programator zbyt daleko od sprzętu, aby był wydajny?

Chyba że jest to PDP-11, C nie jest twoim procesorem. C ++ był pierwotnie preprocesorem na C, więc Bjarne Stroustrup przestał się śmiać z powolnych symulacji Simula podczas AT&T. C ++ to nie twój procesor.

Idź dostać MCU, który uruchamia jatecodes bajtów. Program w Javie. Śmiej się z chłopaków z C.

Czy Cdu Arduino (bez dynamicznego zarządzania pamięcią, szablonów, wyjątków) należy uważać za „prawdziwe C ++”?

Nie. podobnie jak wszystkie drobne kompilatory C dostępne dla MCU.

Po drugie, Embedded Java lub Embedded ADA są znormalizowane (ish); wszystko inne to smutek.


2
Czy łatwo jest znaleźć mikrokontrolery obsługujące Javę? Myślę, że to znacznie ograniczy wybór. A jakie są twoje doświadczenia związane z karą za wydajność (ponieważ w USA zazwyczaj nie miałbyś JIT)? Co z wpływem nieprzewidywalności GC na systemy czasu rzeczywistego?
fceconel,

2
Jakie są MCU, które obsługują osadzoną Javę?
J. Polfer,

www.ajile.com na początek.
Tim Williscroft 13.01.11

+1 dla Ady. Wiele się w tym osadza, w tym Arduinos.
Brian Drummond,

Portable Java Java dla micros napisanych w c jest open source. dmitry.co/index.php?p=./04.Thoughts/…
Tim Williscroft

-2

Systemy wbudowane są zaprojektowane do wykonywania określonych zadań, a nie do komputerów ogólnego przeznaczenia do wykonywania wielu zadań. System osadzony to połączenie sprzętu komputerowego i oprogramowania. C jest matką wszystkich współczesnych języków. Jest to niski poziom, ale obsługuje pełny język i obsługuje wszelkiego rodzaju sprzęt. Tak więc C / C ++ jest optymalnym wyborem do tworzenia oprogramowania dla systemu wbudowanego, który jest bardzo przydatny dla każdego systemu wbudowanego. Jak wiemy, C jest językiem rozwijającym się. System operacyjny UNIX jest napisany w C. Ponieważ udane tworzenie oprogramowania tak często polega na wyborze najlepszego języka dla danego projektu, zaskakujące jest stwierdzenie, że język C / C ++ okazał się odpowiedni zarówno dla procesorów 8-bitowych, jak i 64-bitowych ; w systemach z bajtami, kilobajtami i megabajtami pamięci. Zaletą C jest niezależność procesora, co pozwala programistom skoncentrować się na algorytmach i aplikacjach, a nie na szczegółach dotyczących konkretnej architektury procesora. Jednak wiele z tych zalet dotyczy w równym stopniu innych języków wysokiego poziomu. Ale C / C ++ odniósł sukces tam, gdzie tak wiele innych języków w dużej mierze zawiodło?


6
Naprawdę nie jestem pewien, co to dodaje do dyskusji.
Dave Tweed

-3

<rant>

Myślę, że C ++ to przede wszystkim gówniany język. Jeśli chcesz korzystać z OOP, pisz programy Java. C ++ nie robi nic, by wymusić paradygmaty OOP, ponieważ bezpośredni dostęp do pamięci jest w pełni w twojej mocy do (ab) użycia.

Jeśli masz MCU, mówisz o najprawdopodobniej mniej niż 100 kB pamięci flash. Chcesz programować w języku, którego abstrakcją pamięci jest: kiedy deklaruję zmienną lub tablicę, dostaje pamięć, kropkę; malloc (inaczej „nowe” słowo kluczowe w C ++) powinno być mniej więcej zakazane w oprogramowaniu wbudowanym, z wyjątkiem być może w rzadkich przypadkach jednego połączenia podczas uruchamiania programu.

Do diabła, w programowaniu wbudowanym są (często) czasy, w których C nie jest wystarczająco niskiego poziomu, i musisz robić takie rzeczy, jak przydzielanie zmiennych do rejestrów i pisanie wbudowanego zestawu, aby zacieśnić swoje procedury obsługi przerwań (ISR). Słowa kluczowe takie jak „niestabilny” stają się naprawdę ważne, aby je zrozumieć. Dużo czasu spędzasz na manipulowaniu pamięcią na poziomie bitów , a nie na poziomie obiektu .

Dlaczego miałbyś oszukiwać się, myśląc, że rzeczy są prostsze niż w rzeczywistości?

</rant>


Mój problem polega po prostu na tym, dlaczego chcę poznać złożoność sterownika, który został napisany do sterowania USART1, jeśli został w pełni opracowany do obsługi interfejsu.
Kortuk

1
Nie oddałem głosu, ale chciałbym zauważyć, że C ++ nie musi wymuszać OOP, tylko daje narzędzia, aby to zrobić. Egzekwowanie dobrych standardów kodowania jest zadaniem programisty. Może to pomóc, jeśli język to ułatwi, ale język nigdy nie zrobi tego sam. C może być w niektórych przypadkach nieczytelny.
Kortuk

1
Wszystkie języki są na coś dobre. C ++ jest szybki. OOP, jeśli dobrze zrobione, znacznie ułatwia wielu programistom pracę równoległą i kodowanie nieznanego. Myślę, że właśnie dlatego ma tak dużą przyczepność w tworzeniu gier.
Toby Jaffey,

1
Tak, zgadzam się. Widzę to w świecie osadzonym, ponieważ ogromna liczba funkcji i funkcji została dodana do wielu różnych już istniejących systemów i opracowywanych nowych. Projekt staje się coraz większy. Albo zajmujemy im więcej czasu, albo zaczynamy stosować i przekręcać to, co świat CS już zrobił na komputerach PC.
Kortuk

5
Jeszcze jedna osoba, która nie rozumie poprawnie C ++. Zawsze mnie zadziwia, ile ich jest.
Rocketmagnet
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.