Otwartoźródłowe biblioteki niestandardowe do projektów w C++

Od lat prowadząc C++ zawsze zachęcam (lub jak mogę to wymagam:D), aby Państwo pod koniec semestru zmaterializowali swoje umiejętności w formie projekciku używającego biblioteki niestandardowego. Z mojego doświadczenia jest to najlepszy sposób nauki C++, aby po całym semestrze nauki, ćwiczeń, drobnych zadań coś w końcu zrobić. Mimo intensywnego rozwoju C++ wciąż jest ciężko podpiąć pierwszą bibliotekę niestandardową do projektu, stąd zdecydowałem się na ten wpis. Poza ogólnym wprowadzeniem dorzucę listę wybranych, ciekawych bibliotek, jakie znaleźli inni wraz z moim komentarzem.

Biblioteka niestandardowa jest pewnym dodatkiem do C++, którego nie ma w bibliotece standardowej (czyli coś, czego w standardzie lub na stronie cppreference nie znajdziemy). Standard z założenia jest dostępny wszędzie gdzie jest kompilator C++ (kod używający jedynie biblioteki standardowej jest przenośny między systemami, a nawet urządzeniami bez konieczności doinstalowywania czegokolwiem).

Bibliotekę może zrobić każdy, ale na uwagę zasługują biblioteki, których kod źródłowy jest publicznie dostępny, lista takich bibliotek znajduje się tutaj (lista ta jest utrzymywana przez społeczność, czegoś może brakować). Co prawda jak czegoś szukamy to najczęściej przez ulubioną wyszukiwarkę po tym co biblioteka ma robić, a nie przez tę listę. Na ogół wtedy pojawia się duża lista wyników, z których jest ciężko bez doświadczenia wybrać „tą najlepszą”.

Co powinna zawierać dobra biblioteka?

Biblioteka to w skrócie kod napisany przez kogoś innego. Może ją zrobić każdy, więc też jakość i stopień dojrzałości jest różny. Biblioteka składa się z tego, z czego składa się kod w C++, a dobra biblioteka powinna zawierać:

  1. Pliki nagłówkowe (*.h, *.hpp) – zawierają deklaracje funkcji, definicje klas, definicje szablonów itp.
  2. [opcjonalnie] Pliki źródłowe (*.cpp, *.c) – pliki zawierające definicje do deklaracji z plików nagłówkowych. Jeśli je biblioteka zawiera stanowi to pewną trudność w użyciu biblioteki w naszym projekcie (dlatego w prostych użyciach wygodne są biblioteki składające się tylko z nagłówków, tzw. header-only, niestety tylko niewielkie biblioteki mogą takie być ze względu na czas kompilacji).
  3. Dokumentacje – czyli opisana biblioteka co to jest, do czego służy, jak zainstalować itp. Jeśli tego nie ma to w zdecydowanej większości przypadków odrzucamy bibliotekę.
    • Tutorial/getting started/krok po kroku – jest to bardzo cenne w bibliotece aby w dokumentacji było opisane w krokach jak zacząć jej używać z dobrym opisem.
  4. Przykłady (często w podkatalogu samples, example itp) – na podstawie samej dokumentacji na ogół ciężko się połapać, dlatego kompletne przykłady bardzo ułatwiają.
  5. Skompilowane pliki – jeśli biblioteka nie jest header-only to na ogół budujemy ją sami, jednakże czasami twórcy udostępniają skompilowaną wersję, gotową do dołączenia do projektu (statyczną: *.lib, *.a; dynamiczną *.dll, *.so).
  6. Jeśli zdecydujemy się użyć już skompilowanej wersji musi się zgadzać jej wersja z naszym kompilatorem. Czyli jeśli np. na Windowsie używamy MinGW to nie zadziałają biblioteki zrobione pod VisualStudio, nawet jeśli używamy kompilatora VisualStudio, to może biblioteka nie działać gdy została skompilowana na innej wersji kompilatora – to irytujące, ale tak to wygląda.
    • Należy też pamiętać, że jeśli używamy bibliotek skompilowanych dynamicznie, to dystrybuując naszą binarkę powinniśmy dołączyć biblioteki dynamiczne (pliki *.dll na Windowsie lub *.so na Linuxie) – bez tego użytkownik może nie być w stanie użyć wszystkich funkcji naszego oprogramowania.
      • Ja jestem zwolennikiem aby używać bibliotek statycznie gdzie się da – aby wszystko było w jednej binarce. Niestety czasami prawa autorskie nie pozwalają na statyczne linkowanie bez płatnej licencji.
  7. Historię commitów – a dokładniej istotne są dwa aspekty – liczba commitów, oraz data ostatniej modyfikacji. Jak commitów jest poniżej stu jest to oznaka, że biblioteka może nie być dojrzała, oraz nie wiemy czy początkowy zapał twórców się szybko nie kończy, dlatego ja lubię kilka tysięcy commitów. Ważna jest też data ostatniej modyfikacji, jeśli jest to mniej niż rok temu to jest szansa, że twórcy się zniechęcili, albo może nawet nam nie zadziałać na nowszym kompilatorze.
  8. Społeczność, czyli czy jest biblioteka powszechnie używana (wtedy w razie problemów ktoś może pomoże). Można to sprawdzić patrząc na liczbę Issuesów, lub jak dużo jest pytań z odpowiedziami np. na StackOverflow, czy ogólnie w internecie na forach.
  9. [opcjonalnie, mniej ważne] Pliki CMake – jeśli bibliotekę trzeba zbudować wygodnie jest jeśli zawiera pliki CMake, które możemy odpowiednio włączyć do naszego projektu CMake.

Jeśli biblioteka spełnia wszystkie powyższe aspekty to wydaje się dobrym wyborem, ale niekoniecznie najlepszym.

Jak się kompiluje przy bibliotekę niestandardową?

  1. Jeśli biblioteka jest header-only to wystarczy ją przekopiować do projektu i ustawić odpowiednie ścieżki. Najprofesjonalniej jest w projekcie utworzyć katalog libs, oraz w ustawieniach projektu ustawić ścieżkę do includów na katalog tej biblioteki (aby ścieżki przy includowaniu były krótkie np. #include <biblioteka/naglowki.h> zamiast <libs/X.234.1/include/biblioteka/naglowki.h>
  2. Jeśli biblioteka ma jeszcze pliki skompilowane to trzeba dodać do projektu:
    • katalog, gdzie znajdują się biblioteki skompilowane
    • wymienić konkretne biblioteki, z którymi linkujemy
  3. [alternatywa 1] Powyższe kroki można uprościć jeśli biblioteka zawiera pliki CMake, niestety na ogół trzeba szukać instrukcji jak się robi, oraz używać we własnym projekcie CMake’a.
  4. [alternatywa 2] Można się pokusić o użycie managera pakietów, który wiele aspektów nam zautomatyzuje, wydaje mi się, że najwygodniejszy jest VCPKG (ale uwaga: na Windowskie opbługuje jedynie kompilator VisualStudio, o innych kompilatorach możemy zapomnieć).

Ciekawe biblioteki open source

  1. Biblioteki tworzące interfejs użytkownika do aplikacji (raczej nie do gier):
    • Qt – jest to biblioteka, która zawiera nie tylko interfejs użytkownika, ale też bardzo wiele innych rzeczy takich jak komunikacja sieciowa, czy komunikacja z bazami danych. Jest to biblioteka hiperwieloplatformowa (czyli nie tylko PCty, ale też prostsze urządzenia, WebAssembly, czy Android). Problemem jest jednak duży próg wejścia (przydatne: jak ją zainstalować, warsztaty z QT). Mimo iż Qt posiada bardzo dobrą dokumentacje.
    • wxWidgets – z moich doświadczeń – często studenci, którzy wybierają tę bibliotekę żałują że nie wybrali Qt
  2. Biblioteki do gier 2D:
    • SFML – gry, okienka, multimedia, grafika, sieć
  3. Biblioteki do komunikacji przez sieć:
    • libcurl – biblioteka napisana w C (a także narzędzie curl) do różnorakich żądań sieciowych, nie tylko POST i GET ale też utrzymanie sesji.
    • libcpr – jest to biblioteka w C++ obudowująca powyższą libcurl, zdecydowanie wygodniejsza w użyciu
    • POCO::NET – jest komponentem obszernej biblioteki POCO do obsługi sieci
  4. Grafika 3D – OpenGL, chociaż sugeruję nie operować na samym OpenGLu, tylko skorzystać z biblioteki, która go pod spodem używa np. SFML – tak jest wygodniej pod względem używania widgetów.

Biblioteki header-only && wieloplatformowe

  1. nlohmann/json – biblioteka do obsługi plików w formacie JSON (m.in. ich parsowania), bardzo wygodna w obsłudze
  2. lamarrr/chalk – do kolorowania składni w konsoli
  3. cpp-inquirer – do tworzenia interaktywnego menu w konsoli
  4. tabulate – do tworzenia różnorakich tabel w konsoli (również z kolorowaniem składni)
  5. RapidXML – jest to niezwykle prosta i popularna biblioteka, niestety od lat nierozwijana. Co prawda społeczność nie pozwoliła jej wymrzeć i pojawiła się wersja m.in. obslugująca CMake’a, lub obsługująca przestrzenie nazw. Swego czasu napisałem artykuł na jej temat.

Biblioteki warte uwagi

  1. boost – jest to niesamowicie obszerna biblioteka, zawierająca niezliczone funkcjonalności do C++, z których część wchodzi do kolejnych standardów C++. Ludzie określają ją „poczekalnią do standardu” mimo iż wszystko z niej nie wejdzie do standardu. Zawiera ona komponenty header-only, a także inne aspekty, których jest tak wiele, że nie sposób je wymienić. Duża część biblioteki to rozwiązania generyczne (bazujące na szablonach). Osobom zaawansowanym i znającym standard polecam aby się zapoznać z tym co biblioteka boost zawiera. Kiedyś opisałem jak zbudować bibliotekę boost statycznie.
  2. POCO – nazwa dla nas nie brzmi poważnie, ale wywodzi się od Portable Components. Jest to bardzo duża biblioteka, która zawiera pewne użytkowe funkcjonalności m.in. obsługa plików ZIP, wysyłanie i odbieranie maili, wrappery do baz danych, oraz wiele różnych modułów do pracy przez sieć.
  3. GTest & GMock – GTest jest biblioteką do testów automatycznych, w której testy się pisze w bardzo wygodny sposób. GMock jest biblioteką, którą się używa do tworzenia mocków (czyli w skrócie obiektów naśladujących inny obiekt, ale z zaprogramowanym zachowaniem, czy zliczaniem wywołań). Biblioteki te są razem dystrybuowane. Raczej w każdej firmie piszącej testy w C++ są one używane.
  4. OpenCV – jest to biblioteka do operowania na multimediach przy użyciu znanych algorytmów do przetwarzania m.in. obrazów, ale także i wykrywania obiektów.
  5. Lista wybranych bibliotek od mojego kolegi, który też prowadził zajęcia z C++, a samego C++ rozkminia bardziej niż ja.

Pozostałe aspekty

Potrzebujemy małej funkcjonalności z bibliotek zewnętrznej – czy użyć pod modułu powszechnie szanowanej olbrzymiej biblioteki, czy małej biblioteki dostarczającej tylko to co potrzebujemy. Konkretnie – potrzebujemy parser XML i mamy dylemat czy zaciągać całą dużą bibliotekę np. boost lub poco, czy użyć małej, mniej znanej biblioteki, która robi tylko to. Mimo pozorów nie jest łatwo odpowiedzieć na to pytanie. Spotkałem się ze zdaniem, że wszystkie dobre praktyki programistyczne sprowadzają się do ograniczenia zależności. Wg tej zasady nie chcemy dodawać do projektu bibliotek, których nie potrzebujemy. A jak dodawać to tylko to co potrzebujemy (na ile się da). Należy też wiedzieć jakie biblioteki już są w projekcie, oraz co umożliwiają, aby docelowo nie było dwóch bibliotek robiących to samo.

Kolejna kwestia – mamy w projekcie dwie biblioteki – standardową i zewnętrzną, które dostarczają podobną funkcjonalność, którą chcemy użyć – bez naprawdę wyraźnego powodu używamy zawsze standardowe (przykład std:shared_ptr vs boost::shared_ptr).

Mamy jeszcze aspekt do uwzględnienia – użycie bibliotek systemowych (czyli tych co działają tylko na danym systemie, lub rodzinie systemów operacyjnych). Ja osobiście staram się ich unikać gdyż nie są przenośne, ale czasami musimy ich użyć. Na Windowsy jest WinAPI, które ma słabą dokumentacje, ale umożliwia robienie prawie wszystkiego (nawet zapewne wirusy można tam robić). Na Linuxy jest standard POSIX.

Na zakończenie – natrafiwszy na ciekawe biblioteki opisałem ich mały podzbiór w formie artykułów, może kogoś zainteresuje.

Prośba do czytelników

Jeśli masz fajne pomysły jak zmienić ten artykuł – proszę o komentarze:).

4 uwagi do wpisu “Otwartoźródłowe biblioteki niestandardowe do projektów w C++

  1. Pingback: Zestawienie materiałów na zajęcia organizacyjne | programowaniec

  2. Pingback: Łatwe dodawanie bibliotek zewnętrznych do projektu niezależnie od systemu operacyjnego – manager pakietów VCPKG + CMake | programowaniec

Dodaj komentarz