Software Development

Jak wprowadzałem zmiany do C++20

Czerwiec 11, 2019 0
Podziel się:

Językiem C i C++ interesowałem się od czasów liceum – wtedy właśnie napisałem pierwszy program komputerowy. Pisząc kod, lubię mieć pełną kontrolę nad każdym aspektem i właśnie to mogę osiągnąć pisząc w C++. Czasem doskwierały mi jednak pewne małe braki, których z kolei nie odczuwam  pracując z innymi językami. C++11 zmniejszył tę przepaść, jednak pewne niedogodności pozostały:
– czas kompilacji –  w większości spowodowany przez archaiczny system includów, którego nie widzimy w przypadku Javy, C# lub Go,
– brak Korutyn, w których można jasno i łatwo tworzyć asynchroniczny kod (dostępne chociażby w Pythonie).

W 2011 roku zacząłem przyglądać się pracy Komitetu Standaryzacyjnego C++ w oczekiwaniu na dalszy rozwój funkcjonalności. Zastanawiało mnie, dlaczego tak wyczekiwane aspekty języka jak Moduły czy Korutyny nie wchodzą do kolejnych standardów. Przecież za każdym razem dochodziły głosy, że te funkcjonalności są gotowe, często zaimplementowane i przetestowane! Wyszedł C++14, potem C++17 i dalej nie udało się dostarczyć naszych upragnionych modułów do standardu.

W 2018 roku nadarzyła się okazja, żeby zrozumieć powody tych opóźnień. Podczas after-party po pewnej konferencji, poświęconej w głównej mierze C++, wśród polskojęzycznych prelegentów zrodził się pomysł utworzenia polskiej reprezentacji, która miałaby prawo głosu na posiedzeniach międzynarodowego komitetu standaryzacyjnego. Normy ISO są tworzone przez powołane do tego Grupy Robocze (w przypadku standardu C++ jest to WG21). Aby zostać oficjalnym członkiem takiej grupy, trzeba należeć do określonego Organu Narodowego (eng. National Body), który jest zainteresowany daną normą. Do tej pory żaden z zespołów należących do Polskiego Komitetu Normalizacyjnego nie wyrażał zainteresowania pracą nad standardem C++.

Nigdy wcześniej nie spodziewałem się, że będę miał możliwość uczestniczenia w pracach tego  zespołu. Nawet po tamtej imprezie nie podejrzewałem, że coś z tego wyjdzie, ale nie mógłbym spojrzeć w lustro, gdybym nie spróbował dołączyć do tego zespołu! W Polskim Komitecie Normalizacyjnym, w ramach którego grupa miała być zawiązana, uczestniczą instytucje, a nie osoby prywatne. W związku z tym udałem się do swojego Line Managera w Sii, aby przedstawić mu propozycję. Ku mojemu zdziwieniu, pomysł został przyjęty z dużym entuzjazmem. Dzięki olbrzymiemu wsparciu firmy w ramach Sponsoringu Pasji (bo Sii wspiera nie tylko pasje sportowe), Sii stało się członkiem Komitetu Technicznego ds. Języków Programowania, a ja już 2 lutego 2019 r. siedziałem w samolocie do Kony (Hawaje). Leciałem na swoje pierwsze w życiu posiedzenie Komitetu Standaryzacyjnego C++.

To właśnie na posiedzeniu w Konie zaakceptowano – a w zasadzie zaakceptowaliśmy! – największe zmiany w języku od czasów C++11. Zarówno Moduły, jak i Korutyny zostały zatwierdzone jako część szkicu roboczego C++20. Dla osób, które nie śledzą rozwoju C++ zbyt dokładnie, może wydać się to błahostką, a tak naprawę jest to ukoronowanie ponad dekady prac całego Komitetu! Jestem dumny, że Sii i ja osobiście braliśmy udział w tych głosowaniach!

Atmosfera

Pierwszego dnia na spotkaniu byłem przerażony. Nagle wylądowałem w pokoju wypełnionym ludźmi, których uważam za najlepszych spośród najlepszych. Do tej pory znałem ich jako prelegentów i autorów książek. W Konie siedziałem w jednej sali, na równych prawach z jednymi z najlepszych programistów C++ na świecie. Można się czuć onieśmielonym…

Pomimo pierwszego zakłopotania muszę przyznać, że Komitet jest bardzo otwarty na nowych ludzi. Poziom dyskusji cały czas jest bardzo wysoki. Odniosłem wrażenie, że przy każdej kwestii spornej wszystkie strony są gotowe zmienić zdanie i przyznać rację przeciwnikowi. Widać było chęć znalezienia najlepszego rozwiązania, niezależnie od tego, kto jest jego autorem. W IT wielokrotnie byłem świadkiem przywiązywania się autorów do własnych pomysłów. W takiej sytuacji konstruktywna krytyka często bywa błędnie odbierana przez autora jako atak. Na posiedzeniu Komitetu nie zauważyłem tego zjawiska, mimo że często mieliśmy dwie przeciwstawne propozycje rozwiązania tego samego problemu (i obie wymagały olbrzymiej ilości czasu do przygotowania).

Co prawda nie powinienem oceniać całego Komitetu po jednym spotkaniu, ale na razie zapowiada się bardzo dobrze! 😉

Poziom wyzwań

Nigdy nie sądziłem, że projektowanie i rozwijanie języka programowania jest proste, ale muszę przyznać, że ogrom pracy i złożoność problemów, z jakimi Komitet musi się mierzyć, przerósł moje oczekiwania o kilka rzędów.

Żeby nakreślić sytuację, postaram się posłużyć przykładem. C++11 wprowadziło słowo kluczowe constexpr. Pozwala ono na przeprowadzanie obliczeń w czasie kompilacji, ale ze względu na poziom skomplikowania w implementacji kompilatorów, dopuszczone zostały tylko bardzo proste funkcje. W latach 2014 i 2017 ograniczenia te zostały rozluźnione. Możemy już używać pętli, tworzyć nowe zmienne itp, jednak wciąż nie możemy alokować pamięci. Nie ma mowy o stworzeniu obiektu klasy std::string lub std::vector w funkcji oznaczonej jako constexpr.

Rozważmy, co musi się zadziać w języku, aby funkcja wykonywana w czasie kompilacji mogła tę pamięć alokować.

Kiedy jesteśmy w trakcie wykonania tej funkcji (to jest w czasie kompilacji), to możemy o takiej naszej alokowanej pamięci myśleć normalnie: możemy ją modyfikować i możemy ją zwolnić. Nic trudnego ani skomplikowanego. Dopóki alokujemy i zwalniamy tę pamięć w czasie kompilacji, wszystko jest w porządku. Problem pojawia się, kiedy nie chcemy jej zwalniać w trakcie kompilacji. Istnieje całkiem spora lista przykładów, w których warto by było potraktować taki std::vector stworzony w czasie kompilacji jako stałą w czasie wykonania. Np. wczytanie XML lub JSON na temat wszystkich zmian stref czasowych w historii, aby dostarczyć moduł pracy z datami.

W tym przypadku mamy całkiem sporą motywację, aby pamięć zaalokowaną w czasie kompilacji zostawić do dyspozycji programu w czasie wykonania. I tu pojawia się problem. W czasie wykonania programu ta pamięć jest stała. Próba wprowadzenia zmiany spowoduje niezdefiniowane zachowania: może zakończyć program lub, co gorsza, objawi się w mniej oczywisty i bardziej szkodliwy sposób. Co więcej, typ zwrócony, który utrzymuje taką pamięć (np. rzeczony std::vector) nie może tej pamięci dealokować w destruktorze, bo allokator ma możliwość wprowadzać w takiej pamięci zmiany.

Trzeba też pamiętać, że tego problemu (ani żadnego innego, z jakim boryka się Komitet) nie można rozwiązać w pierwszy lepszy sposób. Rozwiązanie musi być koherentne i brać pod uwagę wszystkie inne aspekty języka, aby być możliwie jak najspójniejsze z intuicją użytkowników języka. A użytkowników C++ jest naprawdę wiele. Nie można też zepsuć istniejącego kodu. A tego kodu jest jeszcze więcej.

Co dalej?

Sama obecność na posiedzeniach Komitetu to tylko wisienka na torcie. Pomiędzy spotkaniami większość pracy odbywa się na listach mailingowych, za pośrednictwem których zgłaszane są pierwsze wersje dokumentów i omawiane bieżące problemy. Regularnie (średnio 1-2 w miesiącu) odbywają się też telekonferencje, które są miniaturową wersją posiedzeń Komitetu. Wynik tych prac jest punktem wyjścia dla posiedzeń.

Przeżyłem swoje pierwsze posiedzenie Komitetu i muszę przyznać, że było to niesamowite doświadczenie. Otrzymałem szansę, aby przyczynić się do rozwoju technologii, której używam na co dzień w pracy. Jednocześnie mogę współpracować z ludźmi, których uważam za mentorów. To świetne uczucie i wyjątkowa okazja do nauki. Zacząłem też pracę nad swoją pierwszą propozycją do biblioteki standardowej, ale chwilę zajmie zanim będzie ona gotowa do publikacji.

Wciąż jeszcze oswajam się z dynamiką i metodami pracy Komitetu, ale wierzę, że mogę mieć swój wkład w jego pracę. Już nie mogę się doczekać 15 lipca i posiedzenia w Kolonii (Niemcy)!

 

5 / 5
Marcin Grzebieluch
Autor: Marcin Grzebieluch
Starszy Inżynier ds. oprogramowania

Imię i nazwisko (wymagane)

Adres email (wymagane)

Temat

Treść wiadomości

Zostaw komentarz