Tworzenie aplikacji użytkownika w C++ ma wiele zalet, więc nie jest zaskakujące, że język ten staje się coraz bardziej popularny wszędzie, w tym w systemach opartych na małych mikrokontrolerach. Projekt „mbed” jest w pełni skoncentrowany na tym języku. Wiele RTOSów zapewnia warstwę kompatybilności z C++, ale w przeciwieństwie do „dużych” systemów (tj. takich, które wyposażone są w MMU), większość RTOSów ma pewne ograniczenia. W poniższym artykule przyjrzymy się wnętrzu implementacji C++ i poznamy przyczyny tych ograniczeń.
Istnieją dwa główne ograniczenia dla C++ uruchamianego na mikrokontrolerze: ponowne uruchamianie aplikacji i wielowątkowe funkcje standardowej biblioteki C++.
Większość przykładów w artykule zostanie omówiona w systemie Embox RTOS. Ten system czasu rzeczywistego (RTOS) umożliwia uruchamianie tak złożonych projektów C++ jak OpenCV na niewielkich mikrokontrolerach (MCU). Projekt ten wymaga obsługi wątków w standardowej bibliotece C++. Ponadto Embox, w przeciwieństwie do innych RTOSów na MCU, umożliwia ponowne uruchomienie aplikacji C++. W przykładach wykorzystano płytkę deweloperską z STM32F769i z zewnętrzną pamięcią SDRAM, aby zademonstrować OpenCV, ponieważ struktura ta wymaga setek kilobajtów pamięci RAM. Jednak kilka kilobajtów pamięci RAM wystarczy do uruchomienia prostych aplikacji C++.
Podstawowa składnia
Składnia języka C++ jest implementowana przez kompilator. Te funkcje są zawarte w bibliotece obsługi języków o nazwie „libsupc++”. Istnieje kilka części, które muszą być obsługiwane podczas działania aplikacji. Na przykład konieczne jest obsłużenie globalnych konstruktorów i destruktorów.
Globalne konstruktory i destruktory
Przyjrzyjmy się, jak każda aplikacja C++ współpracuje z globalnymi konstruktorami i destruktorami. Wszystkie globalne obiekty C++ są tworzone przed wywołaniem przez program funkcji main(). W tym celu istnieją specjalne sekcje: .init_array, .init, .preinit_array, .ctors. Są to tablice wskaźników do funkcji, przez które należy przejść od początku do końca wywołując odpowiedni element tablicy.
Kod do wywoływania konstruktorów obiektów globalnych wygląda następująco:
Zobaczmy też, jak działa kończenie aplikacji C++, a mianowicie wywołanie destruktorów obiektów globalnych. Istnieją dwa sposoby. Po pierwsze, najczęściej stosowanym w kompilatorach jest użycie __cxa_atexit() z interfejsu binarnego aplikacji C++ (ABI). Jest to odpowiednik POSIXowego atexit(). Oznacza to, że można zarejestrować specjalne programy obsługi, które będą wywoływane podczas zamykania programu. Gdy globalne konstruktory są wywoływane na początku aplikacji, jak opisano powyżej, istnieje również kod generowany przez kompilator, który rejestruje procedurę obsługi destruktora za pomocą __cxa_atexit(). Drugim sposobem jest przechowywanie wskaźników do destruktorów w specjalnych sekcjach .fini_array i .fini. Kompilator GCC użyłby tego sposobu, gdyby podano flagę -fno-use-cxa-atexit. W takim przypadku podczas kończenia aplikacji destruktory muszą być wywoływane w odwrotnej kolejności (od wysokiego adresu do niskiego). Ta metoda jest mniej powszechna, ale może być przydatna w mikrokontrolerach. Ponieważ w tym przypadku można dowiedzieć się, ile programów obsługi jest wymaganych w czasie kompilacji.
Kod do wywoływania globalnych destruktorów obiektów wygląda następująco:
Destruktory globalne są wymagane, aby móc ponownie uruchomić aplikację napisaną w C++. Większość RTOS-ów dla mikrokontrolerów uruchamia pojedynczą aplikację, która nie wymaga restartu. Dlatego w takich RTOS-ach globalne destruktory są puste, ponieważ nie powinny być używane.
Kod z Zephyr RTOS do wywoływania globalnych destruktorów wygląda następująco:
Gdy potrzebujesz aplikacji, które można ponownie uruchomić, musisz użyć systemów operacyjnych, w których zaimplementowana jest obsługa globalnych destruktorów. Embox udostępnia tę funkcję w C++ dla różnych platform, w tym mikrokontrolerów. Poniższy film przedstawia wielokrotne uruchamianie różnych aplikacji C++ na płytce STM32F769i-discovery.
Operatory new i delete
W kompilatorze GCC implementacja operatorów new oraz delete jest umieszczona w bibliotece libsupc++, a ich deklaracje znajdują się w pliku nagłówkowym. Można więc użyć implementacji new oraz delete z libsupc++, ale są one dość proste w wariancie podstawowym i można je również zaimplementować samodzielnie, na przykład za pomocą standardowych komend malloc oraz free lub ich analogów.
Oto kod Ebox, który implementuje operatory new i delete (z wykorzystaniem tylko podstawowego C++):
RTTI i wyjątki
Jeśli rozwijana aplikacja jest prosta, wystarczy podstawowy C++ bez wyjątków i informacje o typie czasu wykonywania (RTTI). W takim przypadku funkcje te można wyłączyć za pomocą flag kompilatora [i]-no-exception i -no-rtti. Ale jeśli te funkcje C++ są wymagane, muszą zostać zaimplementowane. Jest to znacznie trudniejsze do zrobienia niż new/delete. Co więcej, w tym przypadku new/delete także muszą mieć bardziej złożone implementacje.
Dobrą wiadomością jest to, że implementacje tych funkcji są niezależne od systemu operacyjnego i zostały już zaimplementowane w bibliotece libsupc++ w kompilatorze skrośnym. W związku z tym najłatwiejszym sposobem dodania ich obsługi jest użycie libsupc++. Prototypy umieszczane są w plikach nagłówkowych.
Istnieją pewne wymagania, które wymagają użycia „standardowej biblioteki obsługi C++” z kompilatora skrośnego z własnym środowiskiem wykonawczym C++. Skrypt linkera musi mieć specjalną sekcję .eh_frame. A przed użyciem środowiska uruchomieniowego standardowa biblioteka obsługi C++ musi zostać zainicjowana adresem początku tej sekcji. Musi korzystać z biblioteki libunwind. Jest to biblioteka, która definiuje przenośny i wydajny interfejs programowania aplikacji w języku C (API) w celu określenia łańcucha wywołań programu.
Oto kod z Embox do inicjalizacji libunwind:
W przypadku architektury ARM wykorzystywane są inne sekcje z własną strukturą informacji – .ARM.exidx oraz .ARM.extab. Format tych sekcji jest zdefiniowany w standardzie Exception Handling ABI for the ARM Architecture (EHABI). .ARM.exidx to tabela indeksów, a .ARM.extab to tabela rzeczywistych pozycji wymaganych do obsługi wyjątku. Aby użyć tych sekcji do obsługi wyjątków, trzeba uwzględnić je w skrypcie linkera:
Aby włączyć obsługę wyjątków, należy określić symbole początku i końca sekcji .ARM.exidx – __exidx_start oraz __exidx_end. Więcej szczegółów na temat organizacji stosu można znaleźć w artykule "Jak działa śledzenie stosu w ARM".
Standardowa biblioteka językowa (libstdc++)
Samodzielna implementacja libstdc++
Obsługa C++ obejmuje nie tylko składnię języka, ale także standardową bibliotekę libstdc++. To, podobnie jak składnia, może być podzielone na różne poziomy. Istnieją podstawowe elementy, takie jak wrappery libc dla przykładów pracy z łańcuchami lub wersja C++ setjmp() . Można je łatwo zaimplementować za pomocą standardowej biblioteki C. Są też bardziej złożone rzeczy, takie jak na przykład biblioteka szablonów standardowych (STL).
Libstdc++ z kompilatora skrośnego
Podstawowe rzeczy, takie jak te opisane powyżej, są zaimplementowane w Emboxie. Jeśli te funkcje są wystarczające, nie ma konieczności dołączania zewnętrznej biblioteki standardowej C++. Ale jeśli potrzebne są na przykład STL, najłatwiej jest użyć biblioteki i plików nagłówkowych z kompilatora skrośnego.
Niemniej jednak istnieje kilka ważnych opcji dla kompilatora skrośnego. Na przykład standardowe arm-none-eabi-gcc wygląda tak:
Przykład ten został zbudowany z opcją –with-newlib. Oznacza to, że potrzebuje ‘newlib’ jako standardowej biblioteki C.
Aby zminimalizować obciążenie, Embox używa własnej implementacji biblioteki standardowej C. Oznacza to jednak również, że do obsługi środowiska uruchomieniowego konieczne jest zaimplementowanie warstwy kompatybilności z newlib.
Kod w Embox, który implementuje jedną z niezbędnych, ale nieoczywistych części do obsługi standardowej biblioteki, wygląda następująco:
Wszystkie elementy warstwy kompatybilności newlib dedykowane są do używania libstdc++ w kompilatorze skrośnym, co widać w Embox w folderze third-party/lib/toolchain/newlib_compat/.
Zaawansowane wsparcie dla standardowych bibliotek std::thread oraz std::mutex
Jeśli skompilujemy następujący kod:
W projekcie mbed, kompilator zwróci następujący błąd:
Dzieje się tak, ponieważ istnieje jeszcze jeden istotny atrybut w kompilatorze skrośnym. Przyjrzyjmy się innemu komunikatowi wyjściowemu:
Gdy GCC jest uruchomione z opcją pracy z pojedynczym wątkiem, obsługa wątków jest wyłączona w STL. Oznacza to, że np. std::thread lub std::mutex nie są dostępne. W związku z tym pojawią się problemy z budowaniem tak złożonych aplikacji C++ jak OpenCV. Innymi słowy, ta wersja biblioteki nie wystarczy do budowania aplikacji wymagających takich funkcji. Rozwiązaniem, które używane jest w Emboxie, jest zbudowanie skrośnego kompilatora gcc dla standardowej biblioteki z POSIXowym modelem wątków. W tym przypadku std::thread i std::mutex są zaimplementowane ze standardowymi pthread_ * i pthread_mutex_ *.
Konfigurowanie Embox
Powtórna budowa kompilatora jest najbardziej niezawodnym podejściem i zapewnia najbardziej kompletne i kompatybilne rozwiązanie. Jednocześnie zajmuje to dużo czasu i może wymagać dodatkowych zasobów, które nie są za bardzo dostępne w mikrokontrolerze. Dlatego nie zaleca się stosowania tej metody w każdym przypadku. Aby zapewnić najlepszy wybór obsługi C++ dla aplikacji użytkownika, do Emboxa dodano kilka klas abstrakcyjnych (interfejsów) z różnymi implementacjami:
* embox.lib.libsupcxx – określa, której metody użyć do obsługi podstawowej składni języka.
* embox.lib.libstdcxx – określa, której implementacji standardowej biblioteki użyć.
Istnieją trzy możliwości dla libsupcxx:
* embox.lib.cxx.libsupcxx_standalone – własna implementacja w Emboxie (tylko podstawowe funkcje).
* third_party.lib.libsupcxx_toolchain – wykorzystanie libsupc++ z kompilatora hosta
* third_party.gcc.tlibsupcxx – budowa libsupc++ ze źródeł
Minimalna opcja może działać nawet bez standardowej biblioteki C++. Embox posiada implementację opartą na najprostszych funkcjach ze standardowej biblioteki C. Jeśli to nie wystarczy, można wybrać trzy warianty libstdcxx:
* STLport.libstlportg – standardowa biblioteka zawierająca STL oparta na projekcie STLport. Nie wymaga budowania dedykowanego GCC. Ale projekt nie jest wspierany od 2008 roku.
* lib.libstdcxx_toolchain – standardowa biblioteka C++ z kompilatora hosta
* gcc.libstdcxx – pełna kompilacja biblioteki libstdc++ ze źródeł
W ten sposób Embox z powodzeniem uruchamia tak złożone aplikacje pisane w C++ jak Qt czy nawet OpenCV.
Podsumowanie
Korzystanie z C++ jest bardzo wygodne, także na mikrokontrolerze. Większość funkcji C++ jest obsługiwana przez kompilator skrośny (libsupc++ i libstdc++). Ponadto C++ wymaga wsparcia ze strony systemów operacyjnych. Większość systemów operacyjnych dla mikrokontrolerów nie zakłada, że aplikacja może zostać ponownie uruchomiona, dlatego nie implementują wywoływania globalnych destruktorów. Innym ograniczeniem jest obsługa wielowątkowości w libstdc++. Aby tego uniknąć, konieczna jest budowa kompilatora skrośny z obsługą modelu innego niż jednowątkowy. Embox RTOS rozwiązuje oba te problemy, umożliwiając wielokrotne uruchamianie aplikacji takich jak np. OpenCV na MCU.
Źródło: https://www.embedded.com/running-advanced-c-software-on-mcus/
Istnieją dwa główne ograniczenia dla C++ uruchamianego na mikrokontrolerze: ponowne uruchamianie aplikacji i wielowątkowe funkcje standardowej biblioteki C++.
Większość przykładów w artykule zostanie omówiona w systemie Embox RTOS. Ten system czasu rzeczywistego (RTOS) umożliwia uruchamianie tak złożonych projektów C++ jak OpenCV na niewielkich mikrokontrolerach (MCU). Projekt ten wymaga obsługi wątków w standardowej bibliotece C++. Ponadto Embox, w przeciwieństwie do innych RTOSów na MCU, umożliwia ponowne uruchomienie aplikacji C++. W przykładach wykorzystano płytkę deweloperską z STM32F769i z zewnętrzną pamięcią SDRAM, aby zademonstrować OpenCV, ponieważ struktura ta wymaga setek kilobajtów pamięci RAM. Jednak kilka kilobajtów pamięci RAM wystarczy do uruchomienia prostych aplikacji C++.
Podstawowa składnia
Składnia języka C++ jest implementowana przez kompilator. Te funkcje są zawarte w bibliotece obsługi języków o nazwie „libsupc++”. Istnieje kilka części, które muszą być obsługiwane podczas działania aplikacji. Na przykład konieczne jest obsłużenie globalnych konstruktorów i destruktorów.
Globalne konstruktory i destruktory
Przyjrzyjmy się, jak każda aplikacja C++ współpracuje z globalnymi konstruktorami i destruktorami. Wszystkie globalne obiekty C++ są tworzone przed wywołaniem przez program funkcji main(). W tym celu istnieją specjalne sekcje: .init_array, .init, .preinit_array, .ctors. Są to tablice wskaźników do funkcji, przez które należy przejść od początku do końca wywołując odpowiedni element tablicy.
Kod do wywoływania konstruktorów obiektów globalnych wygląda następująco:
Code: c
Zobaczmy też, jak działa kończenie aplikacji C++, a mianowicie wywołanie destruktorów obiektów globalnych. Istnieją dwa sposoby. Po pierwsze, najczęściej stosowanym w kompilatorach jest użycie __cxa_atexit() z interfejsu binarnego aplikacji C++ (ABI). Jest to odpowiednik POSIXowego atexit(). Oznacza to, że można zarejestrować specjalne programy obsługi, które będą wywoływane podczas zamykania programu. Gdy globalne konstruktory są wywoływane na początku aplikacji, jak opisano powyżej, istnieje również kod generowany przez kompilator, który rejestruje procedurę obsługi destruktora za pomocą __cxa_atexit(). Drugim sposobem jest przechowywanie wskaźników do destruktorów w specjalnych sekcjach .fini_array i .fini. Kompilator GCC użyłby tego sposobu, gdyby podano flagę -fno-use-cxa-atexit. W takim przypadku podczas kończenia aplikacji destruktory muszą być wywoływane w odwrotnej kolejności (od wysokiego adresu do niskiego). Ta metoda jest mniej powszechna, ale może być przydatna w mikrokontrolerach. Ponieważ w tym przypadku można dowiedzieć się, ile programów obsługi jest wymaganych w czasie kompilacji.
Kod do wywoływania globalnych destruktorów obiektów wygląda następująco:
Code: c
Destruktory globalne są wymagane, aby móc ponownie uruchomić aplikację napisaną w C++. Większość RTOS-ów dla mikrokontrolerów uruchamia pojedynczą aplikację, która nie wymaga restartu. Dlatego w takich RTOS-ach globalne destruktory są puste, ponieważ nie powinny być używane.
Kod z Zephyr RTOS do wywoływania globalnych destruktorów wygląda następująco:
Code: c
Gdy potrzebujesz aplikacji, które można ponownie uruchomić, musisz użyć systemów operacyjnych, w których zaimplementowana jest obsługa globalnych destruktorów. Embox udostępnia tę funkcję w C++ dla różnych platform, w tym mikrokontrolerów. Poniższy film przedstawia wielokrotne uruchamianie różnych aplikacji C++ na płytce STM32F769i-discovery.
Operatory new i delete
W kompilatorze GCC implementacja operatorów new oraz delete jest umieszczona w bibliotece libsupc++, a ich deklaracje znajdują się w pliku nagłówkowym. Można więc użyć implementacji new oraz delete z libsupc++, ale są one dość proste w wariancie podstawowym i można je również zaimplementować samodzielnie, na przykład za pomocą standardowych komend malloc oraz free lub ich analogów.
Oto kod Ebox, który implementuje operatory new i delete (z wykorzystaniem tylko podstawowego C++):
Code: c
RTTI i wyjątki
Jeśli rozwijana aplikacja jest prosta, wystarczy podstawowy C++ bez wyjątków i informacje o typie czasu wykonywania (RTTI). W takim przypadku funkcje te można wyłączyć za pomocą flag kompilatora [i]-no-exception i -no-rtti. Ale jeśli te funkcje C++ są wymagane, muszą zostać zaimplementowane. Jest to znacznie trudniejsze do zrobienia niż new/delete. Co więcej, w tym przypadku new/delete także muszą mieć bardziej złożone implementacje.
Dobrą wiadomością jest to, że implementacje tych funkcji są niezależne od systemu operacyjnego i zostały już zaimplementowane w bibliotece libsupc++ w kompilatorze skrośnym. W związku z tym najłatwiejszym sposobem dodania ich obsługi jest użycie libsupc++. Prototypy umieszczane są w plikach nagłówkowych.
Istnieją pewne wymagania, które wymagają użycia „standardowej biblioteki obsługi C++” z kompilatora skrośnego z własnym środowiskiem wykonawczym C++. Skrypt linkera musi mieć specjalną sekcję .eh_frame. A przed użyciem środowiska uruchomieniowego standardowa biblioteka obsługi C++ musi zostać zainicjowana adresem początku tej sekcji. Musi korzystać z biblioteki libunwind. Jest to biblioteka, która definiuje przenośny i wydajny interfejs programowania aplikacji w języku C (API) w celu określenia łańcucha wywołań programu.
Oto kod z Embox do inicjalizacji libunwind:
Code: c
W przypadku architektury ARM wykorzystywane są inne sekcje z własną strukturą informacji – .ARM.exidx oraz .ARM.extab. Format tych sekcji jest zdefiniowany w standardzie Exception Handling ABI for the ARM Architecture (EHABI). .ARM.exidx to tabela indeksów, a .ARM.extab to tabela rzeczywistych pozycji wymaganych do obsługi wyjątku. Aby użyć tych sekcji do obsługi wyjątków, trzeba uwzględnić je w skrypcie linkera:
Code: c
Aby włączyć obsługę wyjątków, należy określić symbole początku i końca sekcji .ARM.exidx – __exidx_start oraz __exidx_end. Więcej szczegółów na temat organizacji stosu można znaleźć w artykule "Jak działa śledzenie stosu w ARM".
Standardowa biblioteka językowa (libstdc++)
Samodzielna implementacja libstdc++
Obsługa C++ obejmuje nie tylko składnię języka, ale także standardową bibliotekę libstdc++. To, podobnie jak składnia, może być podzielone na różne poziomy. Istnieją podstawowe elementy, takie jak wrappery libc dla przykładów pracy z łańcuchami lub wersja C++ setjmp() . Można je łatwo zaimplementować za pomocą standardowej biblioteki C. Są też bardziej złożone rzeczy, takie jak na przykład biblioteka szablonów standardowych (STL).
Libstdc++ z kompilatora skrośnego
Podstawowe rzeczy, takie jak te opisane powyżej, są zaimplementowane w Emboxie. Jeśli te funkcje są wystarczające, nie ma konieczności dołączania zewnętrznej biblioteki standardowej C++. Ale jeśli potrzebne są na przykład STL, najłatwiej jest użyć biblioteki i plików nagłówkowych z kompilatora skrośnego.
Niemniej jednak istnieje kilka ważnych opcji dla kompilatora skrośnego. Na przykład standardowe arm-none-eabi-gcc wygląda tak:
Code: c
Przykład ten został zbudowany z opcją –with-newlib. Oznacza to, że potrzebuje ‘newlib’ jako standardowej biblioteki C.
Aby zminimalizować obciążenie, Embox używa własnej implementacji biblioteki standardowej C. Oznacza to jednak również, że do obsługi środowiska uruchomieniowego konieczne jest zaimplementowanie warstwy kompatybilności z newlib.
Kod w Embox, który implementuje jedną z niezbędnych, ale nieoczywistych części do obsługi standardowej biblioteki, wygląda następująco:
Code: c
Wszystkie elementy warstwy kompatybilności newlib dedykowane są do używania libstdc++ w kompilatorze skrośnym, co widać w Embox w folderze third-party/lib/toolchain/newlib_compat/.
Zaawansowane wsparcie dla standardowych bibliotek std::thread oraz std::mutex
Jeśli skompilujemy następujący kod:
Code: c
W projekcie mbed, kompilator zwróci następujący błąd:
namespace "std" has no member "mutex"
Dzieje się tak, ponieważ istnieje jeszcze jeden istotny atrybut w kompilatorze skrośnym. Przyjrzyjmy się innemu komunikatowi wyjściowemu:
$ arm-none-eabi-gcc -v
***
Thread model: single
gcc version 9.3.1 20200408 (release) (GNU Arm Embedded Toolchain 9-2020-q2-update)
Gdy GCC jest uruchomione z opcją pracy z pojedynczym wątkiem, obsługa wątków jest wyłączona w STL. Oznacza to, że np. std::thread lub std::mutex nie są dostępne. W związku z tym pojawią się problemy z budowaniem tak złożonych aplikacji C++ jak OpenCV. Innymi słowy, ta wersja biblioteki nie wystarczy do budowania aplikacji wymagających takich funkcji. Rozwiązaniem, które używane jest w Emboxie, jest zbudowanie skrośnego kompilatora gcc dla standardowej biblioteki z POSIXowym modelem wątków. W tym przypadku std::thread i std::mutex są zaimplementowane ze standardowymi pthread_ * i pthread_mutex_ *.
Konfigurowanie Embox
Powtórna budowa kompilatora jest najbardziej niezawodnym podejściem i zapewnia najbardziej kompletne i kompatybilne rozwiązanie. Jednocześnie zajmuje to dużo czasu i może wymagać dodatkowych zasobów, które nie są za bardzo dostępne w mikrokontrolerze. Dlatego nie zaleca się stosowania tej metody w każdym przypadku. Aby zapewnić najlepszy wybór obsługi C++ dla aplikacji użytkownika, do Emboxa dodano kilka klas abstrakcyjnych (interfejsów) z różnymi implementacjami:
* embox.lib.libsupcxx – określa, której metody użyć do obsługi podstawowej składni języka.
* embox.lib.libstdcxx – określa, której implementacji standardowej biblioteki użyć.
Istnieją trzy możliwości dla libsupcxx:
* embox.lib.cxx.libsupcxx_standalone – własna implementacja w Emboxie (tylko podstawowe funkcje).
* third_party.lib.libsupcxx_toolchain – wykorzystanie libsupc++ z kompilatora hosta
* third_party.gcc.tlibsupcxx – budowa libsupc++ ze źródeł
Minimalna opcja może działać nawet bez standardowej biblioteki C++. Embox posiada implementację opartą na najprostszych funkcjach ze standardowej biblioteki C. Jeśli to nie wystarczy, można wybrać trzy warianty libstdcxx:
* STLport.libstlportg – standardowa biblioteka zawierająca STL oparta na projekcie STLport. Nie wymaga budowania dedykowanego GCC. Ale projekt nie jest wspierany od 2008 roku.
* lib.libstdcxx_toolchain – standardowa biblioteka C++ z kompilatora hosta
* gcc.libstdcxx – pełna kompilacja biblioteki libstdc++ ze źródeł
W ten sposób Embox z powodzeniem uruchamia tak złożone aplikacje pisane w C++ jak Qt czy nawet OpenCV.
Podsumowanie
Korzystanie z C++ jest bardzo wygodne, także na mikrokontrolerze. Większość funkcji C++ jest obsługiwana przez kompilator skrośny (libsupc++ i libstdc++). Ponadto C++ wymaga wsparcia ze strony systemów operacyjnych. Większość systemów operacyjnych dla mikrokontrolerów nie zakłada, że aplikacja może zostać ponownie uruchomiona, dlatego nie implementują wywoływania globalnych destruktorów. Innym ograniczeniem jest obsługa wielowątkowości w libstdc++. Aby tego uniknąć, konieczna jest budowa kompilatora skrośny z obsługą modelu innego niż jednowątkowy. Embox RTOS rozwiązuje oba te problemy, umożliwiając wielokrotne uruchamianie aplikacji takich jak np. OpenCV na MCU.
Źródło: https://www.embedded.com/running-advanced-c-software-on-mcus/
Cool? Ranking DIY