blog.michalt.pl

Biblioteka zakresów w C++20

25.10.2020 0:35 Tech

Czas odkurzyć kolejne tematy z moich zakolejkowanych i zalegających wpisów. Niedługo rok 2021, a ja nadal tkwię w standardzie 20. Niedługo nadejdzie czas, aby rozejrzeć się za C++23. Dziś jednak nie wybiegam w przyszłość, pozostanę we współczesności, czyli standardzie 20 i opowiem pokrótce o bibliotece zakresów.

Do testów użyłem niedawno skompilowanego GCC 11 – wydanie z 18 października. W tej wersji, aby móc zbudować kod z przykładów, należy dodać flagę -std=c++20. Niby kompilator na 2021 rok, a mimo to dwudziestka nadal nie jest domyślna – smuteczek.

Do czego służy biblioteka zakresów? Jak można się domyślić, do generowania kolekcji i pracy z istniejącymi, które z jej pomocą można transformować, filtrować, scalać, wyodrębniać fragmenty – to tak mniej więcej. Do pracy na kolekcjach wykorzystywany jest operator alternatywy bitowej |.

Przejrzałem trochę literatury i zauważyłem, że wszyscy korzystają z wektorów. Otóż można również korzystać z arrayów, a także zwykłych tablic. Jeszcze jedna kwestia: będę używał pełnych przestrzeni nazw – taki mam zwyczaj – dzięki temu lepiej widać co z czego pochodzi.

Przykład 1

Wyobraźmy sobie, że mamy funkcję, która ma wyświetlać na ekranie tylko zdania zaczynające się od litery, koniecznie wielkiej, jednak dane te są np. zbierane z jakiegoś zewnętrznego formularza, który wypełnia użytkownik. Jak wiadomo, użytkownicy często są niechlujni. Jeśli zdanie będzie zaczynać się od symbolu, ma zostać pominięte, jeśli od małej litery, ma ona zostać zamieniona na wielką.

kod - przykład 1

Funkcja filter pozwala wyeliminować niepasujące do naszych wymagań zdania, a transform przerobić je w odpowiadający nam sposób. Oczywiście ów funkcje mają zastosowanie również do innych struktur danych. Obie przyjmują jako argument inne funkcje, które dokonują koniecznych operacji. W przypadku filter z pomocą napisanej przeze mnie isFirstCharLetter dokonuję sprawdzenia, czy zdanie zaczyna się od litery, jeśli nie, będzie ono odrzucone. Następne z pomocą transform i napisanej przeze mnie funkcji UpperFirstChar dokonuję zamiany pierwszej literki z wielkiej na małą, jeśli oczywiście mamy do czynienia z małą literą.

Bublem w tej bibliotece może się wydawać to, że wiele jej funkcji zwraca wyniki jako std::ranges::transform_view. Aby dokonać konwersji do wektora, należy skorzystać albo z wariantu pętli zakresowej for, tj. std::ranges::for_each znajdującej się w bibliotece algorithm, albo z jednej ze standardowych pętli C++ i wypełnić nowy wektor.

Przykład 2

Tu nieco bardziej widać wcześniej wspomniany problem. Wyobraźmy sobie, że mamy pewien tekst i każdą jego linijkę chcemy umieścić jako oddzielny element w tablicy lub wektorze.

kod - przykład 2

Jak możemy zauważyć, nie wygląda to najlepiej i wymaga sporo kodu, który na szczęście jest łatwy do napisania. Tu funkcja split odpowiada za rozdzielenie elementów na podstawie podanego w argumencie wzorca. Co ciekawe, wartością przez nią zwracaną nie jest kolekcja napisów, a wartości typu std::ranges::split_view<std::ranges::ref_view<std::__cxx11::basic_string<char> >, std::ranges::single_view<char> >::_OuterIter<true>::value_type – tu musiałem posłużyć się dodatkowo funkcją transform, a także lambdą w której dokonałem rzutowania na napis.

Przykład 3

Na deser generowanie ciągów liczbowych przy użyciu iota_view:

kod - przykład 3

W tym wypadku wygenerowana zostanie kolekcja liczb od 5 do 7 – mamy tu do czynienia z czymś na zasadzie przedziału otwartego z prawej strony, czyli zakres od 5 do 8 z wykluczeniem 8.

Wynik działania kodu:
5
6
7

W tym przykładzie generujemy przedział liczbowy <1,11) z którego przy użyciu funkcji take pobieramy 7 pierwszych elementów:

kod - przykład 4

Wynik działania kodu:
1
2
3
4
5
6
7


O bibliotece ranges można by jeszcze troszkę się porozwodzić, myślę jednak, że przytoczone tu przykłady dobrze obrazują z czym mamy do czynienia. Brakuje mi możliwości rzutowania z transform_view do wektora. Myślę, że w przyszłości zakresy w C++ będą dalej się rozwijać i kto wie, może za 3 lata opowiem tu o kolejnych nowościach, które się w nich pojawią?

To wszystko w tej notce! :) Do przeczytania!