dzisiaj chciałbym zadać ci pytanie dotyczące możliwości C ++ do realizacji określonej architektury oprogramowania.
Oczywiście skorzystałem z wyszukiwania, ale nie znalazłem żadnej bezpośrednio powiązanej odpowiedzi.
Zasadniczo moim celem jest zbudowanie programu, który pozwala użytkownikowi modelować i symulować dowolnie złożone układy fizyczne, np. Samochód. Zakładam, że mam bibliotekę modeli fizycznych (funkcje w ramach klas). Każda funkcja może mieć pewne dane wejściowe i zwracać niektóre dane wyjściowe w zależności od opisu fizycznego, np. Model silnika spalinowego, model aerodynamiczny, model koła itp.
Teraz chodzi o to, aby zapewnić użytkownikowi strukturę, która pozwala mu komponować dowolne funkcje zgodnie z jego potrzebami, tj. Mapować wszelkie zachowania fizyczne. Ramy powinny zapewniać funkcje łączenia wyjść i wejść różnych funkcji. Dlatego środowisko udostępnia klasę kontenera. Nazywam to COMPONENT, który może pomieścić jeden lub wiele obiektów modelu (FUNKCJA). Kontenery te mogą również pomieścić inne komponenty (patrz wzór złożony), a także połączenia (ZŁĄCZE) między parametrami funkcji. Dodatkowo klasa komponentów zapewnia pewne ogólne funkcje numeryczne, takie jak solver matematyczny i tak dalej.
Składanie funkcji powinno odbywać się w czasie wykonywania. W pierwszej kolejności użytkownik powinien mieć możliwość skonfigurowania kompozycji poprzez zaimportowanie pliku XML, który definiuje strukturę kompozycji. Później można pomyśleć o dodaniu GUI.
Dla lepszego zrozumienia tutaj jest bardzo uproszczony przykład:
<COMPONENT name="Main">
<COMPONENT name="A">
<FUNCTION name="A1" path="lib/functionA1" />
</COMPONENT>
<COMPONENT name="B">
<FUNCTION name="B1" path="lib/functionB1" />
<FUNCTION name="B2" path="lib/functionB2" />
</COMPONENT>
<CONNECTIONS>
<CONNECTOR source="A1" target="B1" />
<CONNECTOR source="B1" target="B2" />
</CONNECTIONS>
</COMPONENT>
Nie jest konieczne zagłębianie się w możliwości frameworka, ponieważ mój problem jest znacznie bardziej ogólny. Podczas kompilowania kodu / programu frameworkowego opis problemu fizycznego, a także funkcje zdefiniowane przez użytkownika, nie są znane. Gdy użytkownik wybiera (za pomocą XML lub później za pomocą GUI) funkcję, środowisko powinno odczytać informacje o funkcji, tj. Powinno uzyskać informacje o parametrach wejściowych i wyjściowych, aby zaoferować użytkownikowi możliwość połączenia funkcji.
Znam zasady refleksji i jestem świadomy, że C ++ nie zapewnia tej funkcji. Jestem jednak pewien, że bardzo często wymagana jest koncepcja „budowania obiektów podczas działania”. Jak skonfigurować architekturę oprogramowania w C ++, aby osiągnąć cel? Czy C ++ jest właściwym językiem? Co przeoczyłem?
Z góry dziękuję!
Na zdrowie, Oliver
źródło
Odpowiedzi:
W czystym standardzie C ++ nie można „zezwolić na import funkcji w czasie wykonywania”; zgodnie ze standardem zestaw funkcji C ++ jest statycznie znany w czasie kompilacji (w praktyce, czas linkowania), ponieważ został ustalony na podstawie połączenia wszystkich jednostek tłumaczeniowych tworzących program.
W praktyce przez większość czasu (z wyłączeniem systemów wbudowanych) program C ++ działa powyżej pewnego systemu operacyjnego . Przeczytaj Systemy operacyjne: trzy łatwe elementy, aby uzyskać dobry przegląd.
Kilka nowoczesne systemy operacyjne umożliwiają dynamiczne ładowanie z wtyczek . POSIX w szczególności określa
dlopen
&dlsym
. Windows ma coś innegoLoadLibrary
(i gorszy model łączenia; musisz jawnie opisać odpowiednie funkcje, dostarczone lub używane przez wtyczki). BTW w Linuksie możesz praktyczniedlopen
wiele wtyczek (patrz mójmanydl.c
program , z wystarczającą cierpliwością może wygenerować, a następnie załadować prawie milion wtyczek). Więc twój XML może napędzać ładowanie wtyczek. Twój opis wieloskładnikowego / wielozłącza przypomina mi sygnały Qt i gniazda (co wymagamoc
preprocesora ; możesz także potrzebować czegoś takiego).Większość implementacji w C ++ używa manglingu nazw . Z tego powodu lepiej zadeklarować jako
extern "C"
funkcje związane z wtyczkami (i zdefiniowane w nich i dostępne przezdlsym
program główny). Przeczytaj C ++ dlopen mini HowTo (przynajmniej dla Linuksa).BTW, Qt i POCO to frameworki C ++ zapewniające przenośne i wyższe podejście do wtyczek. A libffi umożliwia wywoływanie funkcji, których podpis znany jest tylko w czasie wykonywania.
Inną możliwością jest osadzenie interpretera, takiego jak Lua lub Guile , w twoim programie (lub napisanie własnego, tak jak zrobił to Emacs). To silna decyzja dotycząca projektu architektonicznego. Możesz przeczytać Lisp In Small Pieces and Pragmatics Language Programming, aby uzyskać więcej.
Istnieją warianty lub mieszanki tych podejść. Możesz użyć biblioteki kompilacji JIT (np. Libgccjit lub
asmjit
). Możesz wygenerować w czasie wykonywania trochę kodu C i C ++ w pliku tymczasowym, skompilować go jako tymczasową wtyczkę i dynamicznie ładować tę wtyczkę (takie podejście zastosowałem w GCC MELT ).We wszystkich tych podejściach zarządzanie pamięcią stanowi poważny problem (jest to właściwość „całego programu”, a to, co faktycznie jest „kopertą” twojego programu, „się zmienia”). Będziesz potrzebował przynajmniej trochę kultury na temat wywozu śmieci . Przeczytaj podręcznik GC dotyczący terminologii. W wielu przypadkach (arbitralne odwołania cykliczne, w których słabych wskaźników nie można przewidzieć), schemat liczenia odniesień drogi dla inteligentnych wskaźników C ++ może być niewystarczający. Zobacz także to .
Przeczytaj także o dynamicznej aktualizacji oprogramowania .
Zauważ, że niektóre języki programowania, w szczególności Common Lisp (i Smalltalk ), są bardziej przyjazne dla idei funkcji importujących środowisko uruchomieniowe. SBCL jest darmową implementacją Common Lisp i kompiluje się do kodu maszynowego przy każdej interakcji REPL (a nawet jest w stanie wyrzucić śmieci do kodu maszynowego i może zapisać cały plik obrazu rdzenia, który można później łatwo zrestartować).
źródło
Najwyraźniej próbujesz stworzyć własny styl oprogramowania typu Simulink lub LabVIEW, ale z niecnym komponentem XML.
W najbardziej podstawowej formie szukasz struktury danych zorientowanej na wykres. Twoje modele fizyczne składają się z węzłów (nazywamy je komponentami) i krawędzi (złącza w nazwie).
Nie ma mechanizmu wymuszonego na język, aby to zrobić, nawet z refleksją, więc zamiast tego będziesz musiał stworzyć interfejs API, a każdy komponent, który chce grać, będzie musiał wdrożyć kilka funkcji i przestrzegać zasad określonych przez interfejs API.
Każdy komponent będzie musiał zaimplementować zestaw funkcji, aby wykonać takie czynności jak:
I to tylko do skonfigurowania wykresu. Potrzebne będą dodatkowe funkcje, aby uporządkować sposób wykonywania modelu. Każda funkcja będzie miała określoną nazwę, a wszystkie komponenty muszą mieć te funkcje. Wszystko specyficzne dla komponentu musi być osiągalne poprzez ten interfejs API, w identyczny sposób dla poszczególnych komponentów.
Twój program nie powinien próbować wywoływać tych „funkcji zdefiniowanych przez użytkownika”. Zamiast tego powinien wywoływać funkcję obliczeniową ogólnego przeznaczenia lub niektóre takie funkcje na każdym komponencie, a sam komponent dba o wywołanie tej funkcji i przekształcenie danych wejściowych na wynik. Połączenia wejściowe i wyjściowe są abstrakcjami tej funkcji, to jedyna rzecz, którą program powinien zobaczyć.
Krótko mówiąc, niewiele z tego jest specyficznych dla C ++, ale będziesz musiał zaimplementować rodzaj informacji w czasie wykonywania, dostosowanych do konkretnej domeny problemu. Z każdą funkcją zdefiniowaną przez API będziesz wiedzieć, jakie nazwy funkcji wywołać w czasie wykonywania, a także poznać typy danych każdego z tych wywołań, i po prostu używasz zwykłego starego dynamicznego ładowania bibliotek, aby to zrobić. Dostaniesz to z dużą ilością płyty kotłowej, ale to tylko część życia.
Jedynym aspektem specyficznym dla C ++, o którym należy pamiętać, jest to, aby interfejs API był interfejsem API C, aby można było używać różnych kompilatorów dla różnych modułów, jeśli użytkownicy udostępniają własne moduły.
DirectShow to interfejs API, który robi wszystko, co opisałem i może być dobrym przykładem do obejrzenia.
źródło
Możesz użyć biblioteki leadelf. Następnie sprawdź krzyżowo symbole względem nagłówka biblioteki. Oto skrypt powłoki PHP, który robi to https://github.com/comarius/elf_resolver
źródło