Eksport funkcji jako API.

0

Witam wszystkich!

Zastanawiam się w jaki sposób najlepiej wyeksportować funkcje z mojej aplikacji tak by były one dostępne dla ewentualnych pluginów ładowanych dynamicznie. Chciałbym by pluginy miały możliwość wywoływania funkcji z samego pliku wykonywalnego, który je ładuje. Jaki sposób byłby najlepszy? Jak to się robi w dużych projektach?

2

Ja to robię w taki sposób:

  1. API (i ew. główne ciało aplikacji) eksportuję do osobnej biblioteki, nagłówki rozdzielam na publiczne (API) i prywatne (widoczne tylko dla aplikacji)
  2. Główną binarkę linkuję do dllki z implementacją
  3. pluginy linkuję do głównej dllki i dodaję publiczne nagłówki

Nie jest to idealne, ale działa. Jeśli można lepiej to chętnie się dowiem.

0

W dużych projektach robi się to tak, że zupełnie osobno daje się całe API. W postaci libów lub dllek. Z tych libów i dllek korzysta również główna aplikacja. Plugin teraz musi podłączyć te dllki i liby do swojego projektu i może hulać.

0

A co w przypadku gdy potrzebujemy w tych funkcjach w DLL mieć dostęp do np. jakiejś zmiennej, która jest w pliku wykonywalnym?

1

Do wykonywalnego się nie podlinkujesz, dlatego częstym rozwiązaniem jest plik wykonywalny którego jedynym zadaniem jest odpalenie głównej funkcji w dll/.so.

0

Możesz zrobić strumień i prosty interface np. będziesz dawał jakiś zakres wartości np. 0-15, a aplikacja będzie zwracać strumieniem danych, w zależność od przekazanej wartości.

Jeśli druga aplikacja dziedziczy po procesie matki, to można użyć mmap albo semaforów do współdzielenia zarezerwowanych bloków pamięci.

Ewentualnie możesz podpiąć się pod process i sprawdzić wartość w danym miejscu pamięci, ale będziesz musiał albo przeszukać pamięć, albo przekazać adress miejsca w pamięci.

0

@up Rozwiązanie ma być od początku do końca multiplatformowe

1

Jeśli to ma być zwykły system pluginów, wystarczy zdefiniować dwa interfejsy - IPlugin i IHost. Pierwszy definiuje API plugina, drugi - metody jakie plugin może wywołać na rzecz aplikacji, np. pobrać wartość wspomnianej wcześniej zmiennej z wnętrza aplikacji. Metoda prosta, przenośna i bez zbędnych komplikacji.

0

A jak wygląda tutaj kwestia używania różnych kompilatorów do tworzenia pluginów? Nie posypie się to wszystko ze względu na brak kompatybilności ABI?

2

Posypie. Jeśli chcesz używać różnych kompilatorów to lepiej ogranicz się do interfejsu w C z opaque pointerami i funkcjami do alokacji/dealokacji w głównej aplikacji.

0

Są jeszcze jakieś inne pomysły jak poradzić sobie tutaj z kwestią używania różnych kompilatorów? Rozumiem, że "duże" aplikacje używają metody z "surowymi" wskaźnikami.

1

@Wazka260196, nie posypie się, jeśli wszystkie metody publiczne obu interfejsów będą metodami wirtualnymi - chodzi o zbudowanie odpowiedniego vtable, które jest "przenośne" między kompilatorami. Argumentami metod nie mogą być inne klasy, które nie zostały zdefiniowane przez system pluginów, zatem std::string czy std::vector odpadają. Tylko inne interfejsy wchodzą w grę. Następna rzecz to zarządzanie pamięcią. Jeśli plugin przydzieli jakąś pamięć przy pomocy new/malloc/itd., to taką pamięć możne zwolnić tylko kod po stronie plugina. Dlatego nie możesz po stronie aplikacji zrobić tak:

IPlugin* plugin = pluginCreateInstance(IHostptr);

delete plugin;

bo wystrzeli ci asercja lub inny wyjątek. IPlugin powinien mieć metodę, która usuwa obiekt:

IPlugin* plugin = pluginCreateInstance(IHostptr);

plugin->Destroy();

Destroy, jak i pluginCreateInstance, są zaimplementowane po stronie plugina, więc wszystko jest ok.

To tak pokrótce...

0

Z dyskusji w komentarzach wnioskuję, że jeżeli chcę osiągnąć to co chcę, to muszę znaleźć jakiś sposób na wygenerowanie tablicy metod w taki sam sposób dla każdej platformy i kompilatora. Czy kombinuję w dobrą stronę?

0

Wpływu na sposób generowania vtable przez kompilator nie masz. Ale możesz przyjąć pewne założenia. Na Windowsie wszystkie znaczące kompilatory są COM compatible, zatem sposób generowania vtable musi być taki sam, niezależnie od wersji czy producenta kompilatora. W przypadku g++ sprawa wydaje się jasna - istnieje coś takiego jak Itanium C++ ABI, które definiuje ABI dla C++, w tym sposób generowania vtable.

Firma Steinberg wypuściła jakiś czas temu nową wersję systemu pluginów VST3, który bazuje na interfejsach. Jej oprogramowanie wypuszczane jest na Windowsy i OS X. Nie sądzę, żeby firma, która ma jakąś renomę pakowała się w coś, co jest niepewne.

@kq, faktycznie informacja o typie może być trzymana w vtable.

0

Wpływu na sposób generowania vtable przez kompilator nie masz. Ale możesz przyjąć pewne założenia. Na Windowsie wszystkie znaczące kompilatory są COM compatible, zatem sposób generowania vtable musi być taki sam, niezależnie od wersji czy producenta kompilatora.

Ale COM wcale nie wymaga, by to vtable które mu dajesz to było vtable wygenerowane przez kompilator.
Zmienna COM-owa (typu ICośTam*) to wskaźnik na COM-owe vtable, w którym pierwsze trzy pola to wskaźniki na metody Add, Remove i QueryInterface, a potem dalsze metody we właściwej kolejności.

COM-a nie obchodzi co jest przed ani za vtable. Często są tam inne vtable (przy wielokrotnym dziedziczeniu), i oczywiście pola klasy też gdzieś muszą być.

Takie vtable można nawet dynamicznie generować wewnątrz QueryInterface - do tego zresztą ta metoda służy.
Oczywiście jest to rzadko potrzebne, bo w większości przypadków kompilator C++ potrafi utworzyć vtable w takim samym formacie automatycznie, a w przypadku wielokrotnego dziedziczenia wybiera się właściwe vtable za pomocą rzutowania wskaźnika, które się robi w tymże QueryInterface.

Z dyskusji w komentarzach wnioskuję, że jeżeli chcę osiągnąć to co chcę, to muszę znaleźć jakiś sposób na wygenerowanie tablicy metod w taki sam sposób dla każdej platformy i kompilatora.

Pod Windowsem COM działa tak samo pod każdym kompilatorem (obsługującym COM).
Pod uniksami można go zasymulować na tej samej zasadzie, ale tu już musisz się dowiedzieć jak wygląda ABI C++, choć zgaduję że vtable jest budowane w ten sam sposób, bo to najsensowniejszy sposób.

0
Wazka260196 napisał(a):

A co w przypadku gdy potrzebujemy w tych funkcjach w DLL mieć dostęp do np. jakiejś zmiennej, która jest w pliku wykonywalnym?

Może to oznaczać, że źle zaprojektowałeś system. Ale nie musi. Musisz mieć Interfejs do aplikacji głównej i tym interfejsem się posługiwać podczas komunikacji: dll -> app. Ale o tym już chyba ktoś pisał.

1

@Juhas Nie bardzo rozumiem dlaczego źle miałem zaprojektować taki system. Wydaje mi się to dosyć naturalne, że plugin w pewien sposób może potrzebować dostępu do danych głównej aplikacji (gimp np.?)

@Azarien - czyli generalnie nic nie stoi na przeszkodzie żeby symulować na innych platformach i kompilatorach taką "architekturę". Twoją wypowiedź rozumiem w ten sposób, że vtable generowane przez kompilator to jedna sprawa (choć może być zgodna z COM), a sama tablica metod dla COM to oddzielna już rzecz. Zgadza się?

0

Dobrze rozumiesz. Faktem jest, że większość/wszystkie znaczące kompilatory starają się być zgodne z ABI systemowym, ale różnice na windowsie są chociażby w używanych wyjątkach i name manglingu. Jeśli chcesz mieć interfejs w C++ to w/g mnie dobrą zasadą jest trzymać się tego samego kompilatora - lub przynajmniej rodziny kompilatorów.

0

Kwestia tutaj jest taka, że rozważam to wszystko w kategoriach wieloplatformowości i braku pewności co do używanego kompilatora. Nie mogę przez to (tak mi się wydaje!) założyć że cokolwiek będzie ze sobą zgodne. W tym momencie wydaje mi się, że najlepszym wyjściem w tym wypadku byłoby napisanie czegoś na kształt właśnie tego COMa.

0

Jak już pisałem, w przypadku Windowsa COM jest tym, co daje gwarancje zgodności. Z kolei gcc, czyli dominujący kompilator na systemy uniksopodobne, ma ujednolicone ABI (od wersji 4).

PS. czy przypadkiem javowe JNI nie korzysta z interfejsów?

1 użytkowników online, w tym zalogowanych: 0, gości: 1