2016-12-19 33 views
7

Piszę interfejs FFI dla istniejącej biblioteki (napisanej w C).FFI in Squeak: Singleton ExternalLibrary vs. metody klas a metody w ExternalStructures?

Biblioteka korzysta z dużej liczby nieprzezroczystych struktur, dlatego zdefiniowałem kilka ExternalStructures (bez pól) do użycia jako void*.

Teraz widziałem dwa sposoby (lub cztery?) O relacje z biblioteką:

mającą ExternalLibrary z jednej metody funkcji eksportowanej: To może mieć metodę w klasie instancji, a następnie użyć wzór singleton ma jedną instancję. Lub wdrożenie metod w klasie z boku „bardziej złożonych” składni tym moduleName w Pragma FFI jak w:

ffiTestFloats: f1 with: f2 
    "FFITestLibrary ffiTestFloats: $A with: 65.0" 
    <cdecl: float 'ffiTestFloats' (float float) module:'SqueakFFIPrims'> 
    ^self externalCallFailed 

Co jest lepsze?

Dodatkowo widziałem inny sposób robienia tego, nie mając w ogóle ExternalLibrary i implementując metody bezpośrednio w ExternalStructure. Ta druga część jest lepsza, jednak cała definicja interfejsu FFI jest rozłożona na kilka klas, a obsługa i przenoszenie na inne platformy, dialekty Smalltalk lub wersje bibliotek mogą być bardziej złożone.

Co to jest "właściwy" sposób na zrobienie tego?

+0

Nie wiem, która jest metoda skrzypienia, ale podoba mi się podejście biblioteki zewnętrznej za pomocą jednej metody dla każdej wyeksportowanej funkcji. Również posiadanie metod w ExternalStructure może być trudne, ponieważ może istnieć kilka struktur lub nikt w ogóle dla niektórych funkcji. –

Odpowiedz

1

Trzymałbym się tradycyjnego podejścia do modelowania rzeczy takimi, jakie są. Mamy tutaj bibliotekę zewnętrzną, następnie stwórzmy dla niej klasę i replikujmy jej interfejs API w naszym obiekcie, oczywiście, używając metod z poziomu strony, które wykonują wymagane wywołania FFI.

Stosujemy to podejście od dwóch dekad, a doświadczenie pokazuje, że wzorzec singletonu działa bardzo dobrze w tym przypadku, ponieważ ułatwia życie klienta. Oczywiście ten obiekt musi być traktowany z ostrożnością, aby nie odwoływać się do instancji biblioteki z nieodpowiednich miejsc. Zauważ jednak, że nie jest to podstawowa decyzja, ale będziesz musiał w jakiś sposób zachować unikalne instancje biblioteki gdzieś przechowywane.

Wdrażanie wywołań FFI w strukturach zewnętrznych nie jest naturalne, ponieważ niektóre połączenia mogą obejmować więcej niż jedną strukturę lub wcale. Więc gdzie byś je umieścił?

Wspomniano również o możliwości zastosowania metod po stronie klasy. W końcu wszyscy zgadzamy się, że powinno być tylko jedno wystąpienie każdej biblioteki, czyż nie? Jednym z powodów odrzucenia tej możliwości jest to, że metody po stronie klasy zapewnią mniej elastyczne wdrożenie. Czemu? Jedną rzeczą jest użycie jakiegoś mechanizmu, aby mieć tylko jedną instancję klasy, a drugą - uniemożliwić jej posiadanie. Jeśli Twój obiekt jest instancją (a nie klasą), nadal będziesz miał możliwość uniknięcia jawnie zachęcanego ograniczenia pojedynczej instancji i być w stanie stworzyć kolejną. Naruszałoby to twoje własne zasady, ale zawsze lepiej jest to robić. Jednym prostym przypadkiem do zrobienia tego jest testowanie. Możesz utworzyć drugą instancję, która łączy się, powiedzmy, inną wersję biblioteki i przetestuj ją bez konieczności modyfikowania klasy.Drugi powód nieuzyskania metod klasowych jest bardziej subtelny: klasy reprezentują pojęcie rzeczy, a nie rzeczy. W konsekwencji ich naturalny protokół różni się od protokołu ich instancji. Oddzielenie obu interfejsów doda wyrazistości projektowi.

+0

Gera właśnie pyta, gdzie umieścić deklaracje funkcji - jak rozumiem, nie chce/musi zajrzeć do wnętrza struktur. –

+0

@BertFreudenberg dobry punkt. Usunąłem nieistotne akapity i dodałem kilka uwag, które pozostają w związku z tym pytaniem. Dzięki! –

1

Ja lubię o wiele lepiej pierwszą opcję, obiekt reprezentujący bibliotekę zewnętrzną z odwzorowaniem jeden do jednego między funkcjami C i metodami instancji. Oczywiście, to jest niższy poziom abstrakcji, nad nim powinny powstawać lepsze abstrakcje. Nie użyłbym "singleton", nie ma potrzeby "jeden przypadek klasy" myślę. Potrzebny jest dobrze znany obiekt, a do tego obiektu nie powinno się odnosić bezpośrednio od wewnątrz, aby uniknąć sprzężenia, w razie potrzeby należy go przekazać jako parametr.

3

Podejdę z podejściem ExternalLibrary, ponieważ pozwala dostosować nazwę biblioteki zamiast kodowania go w każdej metodzie.