Techniki zapewniające zgodność między platformami (C ++)?

13

Kończę jeden z moich najwcześniejszych projektów w C ++, który (zgodnie z ramami) powinien być wieloplatformowy. W pełni rozwinąłem projekt w Windows i Visual Studio, myśląc, że ponieważ wszystkie biblioteki są wieloplatformowe, wykonanie kompilacji OSX „później” byłoby trywialne. Okazało się, że tak nie jest, ale raczej „kod systemu Windows” nie działa poprawnie i trzeba było naprawić niektóre błędy kompilacji.

Jakie techniki istnieją wcześniej, aby zapewnić zgodność kodu ze wszystkimi platformami? Równoczesne rozwijanie wszystkich platform, a tym samym testowanie kodu na każdej platformie w tym samym czasie, gdy dodawane są nowe funkcje, zamiast tworzyć kolejne wersje platform jedna po drugiej? (*)

Poszukując konkretnej porady, która nie zależy od narzędzi, ale raczej „procesów programistycznych”, które pomagają kompatybilność między platformami, niezależnie od używanych narzędzi. Jak ten (*) powyżej.


W szczególności tworzę wtyczkę VST z WDL-OL ( https://github.com/olilarkin/wdl-ol ) i kilkoma wieloplatformowymi bibliotekami DSP. Projekty WDL-OL mają skonfigurowane projekty VS i Xcode, ale wydaje mi się, że problemy wynikają z bibliotek, a następnie z różnic w kompilatorach.

mavavilj
źródło
1
nie ma sposobu na zapewnienie przenośności, tylko sposoby na poprawę / maksymalizację przenośności
phuclv
i dlaczego nie jest to duplikat? Myślę, że było wiele podobnych pytań
phuclv
Co robi twoja aplikacja? Jak byś tego użył? W wierszu poleceń lub w interfejsie graficznym? Proszę edytować swoje pytanie , aby ją poprawić.
Basile Starynkevitch,
A jakich ram używasz?
Basile Starynkevitch

Odpowiedzi:

15

Tworzenie przenośnego kodu może być bardzo trudne.

Najpierw kilka oczywistych porad związanych z językiem:

  • używaj standardowego C ++ i ostrożnie unikaj nieokreślonego zachowania
  • polegaj przede wszystkim na bibliotece standardowej (i bibliotekach przenośnych, takich jak boost )
  • zawsze uwzględniaj wszystkie oczekiwane nagłówki. Nie zakładaj, że nie potrzebujesz nagłówka, ponieważ jest on zawarty w innym (tj. W jednej konkretnej implementacji !): Może to powodować błędy kompilacji
  • unikaj konstrukcji obsługiwanych przez kompilator, ale nie gwarantowanych przez standard C ++ (np. anonimowa struktura lub tablica o zmiennej długości ): może powodować błędy kompilacji.
  • użyj opcji kompilatora, aby pomóc w egzekwowaniu zgodności (wyłączając rozszerzenia specyficzne dla kompilatora, maksymalizując poziom zwracanych komunikatów ostrzegawczych)
  • pamiętaj, że nie wystarczy, aby kod działał: istnieje wiele pułapek przenośności zależnych od implementacji: rozmiar (nawet elementarnych) typów danych, znaki, które mogą być domyślnie podpisane lub niepodpisane , założenia dotyczące endianowości , kolejność oceny w wyrażeniach, gdy te używaj efektów ubocznych itp.

Następnie kilka zaleceń projektowych:

  • Preferuj standardową bibliotekę zamiast równoważnej funkcjonalności systemu operacyjnego
  • Izoluj użycie zależności systemu operacyjnego w jak największym stopniu (na przykład w funkcji opakowania kontrolowanej kompilacją warunkową)

Wreszcie delikatne punkty:

  • kodowanie znaków: w wielu systemach możesz polegać na utf8 . Ale w przypadku systemu Windows jest bardziej delikatny, ponieważ system albo oczekuje ansi, albo utf-16. Możesz oczywiście polegać na typedef (jak TCHAR), ale może to być trudne w połączeniu ze standardową biblioteką (np. coutVs wcoutw zależności od tego, czy używasz charlub wchar_t)
  • Jeśli dla GUI / grafiki / zaawansowanych I / O nie możesz znaleźć przenośnej biblioteki, która spełniałaby twoje oczekiwania / wymagania, zaprojektuj ogólną architekturę, aby odizolować komponenty specyficzne dla systemu operacyjnego. Opakowania mogą tutaj nie być wystarczające z powodu wielu różnych interakcji i pojęć.
  • Skorzystaj z kilku interesujących wzorców projektowych, takich jak na przykład fabryka abstrakcyjna (idealna do projektowania rodzin powiązanych obiektów, takich jak interfejs użytkownika w systemie operacyjnym) lub mediator (idealny do implementacji współpracy między rodzinami powiązanych obiektów) i używaj ich razem z kompilacją warunkową .

Ale to tylko porady. W tej dziedzinie nie można uzyskać pewności.

Christophe
źródło
-Wall -Wextra -Werror
rura
1
@pipe Ty też powinieneś być -pedantic.
5gon12eder
@ 5gon12eder Bardzo dobry punkt w kontekście tej odpowiedzi.
rura
11

Nic nie może zagwarantować, że kod jest zgodny z platformą inną niż budowanie, uruchamianie i testowanie go. Dlatego podejście wszystkich rozsądnych ludzi polega na budowaniu, uruchamianiu i testowaniu aplikacji na każdej platformie, na której ich zdaniem trzeba będzie zbudować, uruchomić i przetestować.

Ciągła integracja (CI) może nieco zmniejszyć to obciążenie w przypadku mniejszych projektów, ponieważ można uzyskać tanie lub bezpłatne agenty kompilacji dla niektórych platform (głównie Linux), rozwijać program w systemie Windows i po prostu wrócić do systemu Linux, gdy wystąpi problem.

OSX CI jest jednak dość skomplikowany.

DeadMG
źródło
Nie ma wzmianki, co to jest CI?
mavavilj
5
Continuous Integration - w zasadzie kilka serwerów, na których uruchamiane są kompilacje i testy.
DeadMG
@DeadMG Masz na myśli jak Mozilla Tinderbox?
Damian Yerrick
1
Travis CI zapewnia bezpłatne hosty kompilacji OSX
Daenyth
Wystarczy użyć Cmake.
raaj
9

Jeśli pytasz o „procesy programistyczne”, a podstawową platformą programistyczną jest Windows z Visual Studio, proponuję spróbować zbudować projekt bez uwzględnienia „windows.h”. Otrzymasz wiele błędów kompilacji, które wskażą Ci wiele miejsc, w których będziesz musiał zmienić kod. Na przykład „DWORD” nie zostanie # zdefiniowany i trzeba go zastąpić uint32_twszędzie (google for stdint.hi znajdziesz przydatne informacje o typach liczb całkowitych i ich definicjach międzyplatformowych). Następnie musisz zastąpić wszystkie wywołania API Win32, takie jak,Sleep()z ich odpowiednikiem na wiele platform (ponownie, Google jest twoim najlepszym przyjacielem, który pokaże odpowiednie pytania i odpowiedzi na stronach stosu * .com). Prawdopodobnie nie uda ci się znaleźć wszystkich odpowiednich zamienników dla wielu platform dla twojego kodu i będziesz musiał zwrócić include "windows."dyrektywę, ale ją zawrzeć #ifdef _WIN32- więcej szczegółów tutaj

Zadawaj coraz bardziej konkretne pytania i otrzymuj odpowiedzi - to ogólna sugestia dotycząca „jaki powinien być proces rozwoju”

EDYCJA 1 Kolejną moją propozycją jest użycie gcc i / lub clang na maszynie programistycznej Windows (wraz z Visual Studio)

mvidelgauz
źródło
3

To zależy od wspomnianych „niektórych błędów kompilacji”. Nie wiedząc, co to jest, nie można być konkretnym.

Mam wieloplatformowy kod dla Windows / Linux / iOS / Android / Mac. Każda nowa platforma przyniosła dodatkowe błędy i ostrzeżenia przy pierwszym dodaniu. Szybko dowiesz się, które konstrukcje powodują problemy. Unikaj ich lub wyodrębnij różnice w nagłówku za pomocą #ifdefs. Staraj się nigdy nie przechodzić #ifdefmiędzy platformami w obrębie samego kodu.

Jeden przykład:

void myfunction(MyClass &);
myfunction(MyClass());

tworzy tymczasową instancję, MyClassktóra jest usuwana po myfunctionzwróceniu. W przypadku niektórych moich kompilatorów C ++ instancja ta jest do odczytu / zapisu (a fakt, że jest tymczasowy i wkrótce zostanie zniszczony, nie martwi kompilatora). W przypadku innych myfunctionkonieczne jest ponowne zdefiniowanie, aby wziąć const MyClass &, lub kompilator narzeka. Nie ma znaczenia, co mówi standard C ++ lub który kompilator jest właściwy, a który zły. Po napotkaniu błędu kilka razy, co wiem (a) albo zadeklarować zmienną tymczasową typu MyClassi przekazać, że do myfunctionlub (b) stwierdzenie, że odwołanie constw myfunctioni wykorzystać mutabletu i tam deconstify.

Podsumowując: zdobywaj doświadczenie i rozwijaj własne standardy kodowania.

Martin Kochański
źródło
2
To błędna funkcja MSVC. Jeśli chodzi ci o to, że rozwijanie na 1 systemie pozwala im się wkraść, punkt wzięty. Ale ta konkretna rzecz nie jest czymś, co powinieneś robić.
JDługosz
1
Jedną dobrą rzeczą przy stosowaniu wielu łańcuchów narzędzi jest to, że wspólny podzbiór, który akceptują, jest bardziej prawdopodobne, że będzie poprawny, zgodny ze standardami C ++, niż to, co każdy z nich akceptuje indywidualnie. W twoim przypadku pokazany kod jest ewidentnie niepoprawny i obie wymienione poprawki są prawidłowymi alternatywami. Powiedziałbym, że ma znaczenie to , co mówi standard.
5gon12eder
1
Albo powiedziałbym, że wieloplatformowy C ++ jest bardziej restrykcyjny niż to, co mówi standard. Innymi słowy, nawet jeśli standard tak mówi, nadal podlegasz najniższym wspólnym mianownikom (lub co najmniej możliwości dostawcy) dla platform, które musisz obsługiwać. Istnieje wiele bibliotek typu open source, które musiały wykluczać powiedzmy C ++ 11, ponieważ część ich składników (jak w przypadku obywateli) nie jest zdolna do obsługi C ++ 11.
rwong
3

Możliwym sposobem na ułatwienie przenoszenia może być poleganie tylko na deklaracjach i funkcjach dostarczonych przez standard C ++ 11 oraz przy użyciu bibliotek i platform międzyplatformowych, takich jak POCO i Qt .

Ale nawet to nie jest niezawodne. Pamiętaj o aforyzmie

nie ma czegoś takiego jak program przenośny, są tylko programy, które zostały pomyślnie przeniesione (na określoną platformę)

Dzięki praktyce, dyscyplinie i dużemu doświadczeniu, przeniesienie programu na inną platformę można zwykle zrobić szybko. Ale doświadczenie i wiedza są bardzo ważne.

Basile Starynkevitch
źródło
1
Inną radą jest to, że gdy przenoszenie odbywa się przez różne osoby i zespół, zmiany kodu muszą zostać ponownie zintegrowane z linią główną. W przeciwnym razie różnice między platformami stają się wiedzą gromadzoną przez zespół, który to zrobił.
rwong