C++-standardin historiaa
Alusta asti ratkaiseva seikka C++:n kehityksessä on ollut tehokkuuden painottaminen sekä mahdollisuus tarvittaessa päästä lähelle laitteistoa. Vaikka Bjarne Stroustrup kehitti C++:n C-kielestä jo 1982, sen ensimmäinen virallinen standardi julkaistiin vuonna 1998. Tuolloin kieleen virallisesti tulivat mm. vieläkin käytössä olevat peruskirjastot kuten STL (vector, map, …) ja IO-virrat (cin, cout ja cerr).
Ensimmäisen standardiversion jälkeen C++ pysyi samanlaisena verraten pitkään (poislukien pieni “korjauspainos” 2003, C++03), kunnes vuonna 2011 julkaistu C++11 laajensi kieltä olennaisesti, mukaan tulivat mm. säiepohjainen rinnakkaisuus (thread, future, mutex), älykkäät osoittimet (shared_ptr), tyyppipäättely (auto i = f();), lambda-funktiot ([](int i){ return 2*i;}), mahdollisuus käännösaikaiseen evaluointiin (constexpr, static_assert) sekä “variadiset templatet” (template <typename… Types> f(Types… args);)
Vuonna 2012 standardoinnissa otettiin käyttöön malli, jossa kielen uusia mahdollisia ominaisuuksia tarvittaessa kehitetään ensin virallisesti standardista erillään “TS:nä” (Technical Specification), joita voidaan sitten kokemuksen karttuessa sisällyttää itse standardiin. Samalla päätetiin mallista, jossa uusi versio standardista julkaistaan aina 3 vuoden välein (eikä “sitten kun se on valmis”, mikä aiheutti paljon kritisoidun 8 vuoden viiveen C++03:n jälkeen). Tässä aikatulussa pysyttiin, ja seuraava korjausversio C++14 ilmestyi 3 vuotta myöhemmin, ominaisuuksina lähinnä C++11:n uusien ominaisuuksien laajentamista, kuten paluutyypin päättely (auto f() { return 3; }), geneeriset lambdat ([](auto i){ return 2*i; } ja muuttuja-templatet (template <typename T> constexpr T PI = T(3.14159);).
Seuraava suurempi standardiversio oli vuonna 2017 C++17, jossa mukaan tulivat mm. käännösaikainen if-lause (if constexpr, korvaa C:n #ifdef:n), koosteen purkaminen eli “structured binding” (auto [x, y] = f();), fold-lausekkeet (template <typename… T> summaa(T… args) { return args + …; }) ja kirjastotuki mahdollisille ja heterogeenisille arvoille (optional<T>, variant<X,Y>, jälkimmäinen korvaa C:n unionit).
Mielenkiintoinen kuriositeetti kielen standardoinnissa on, että vaikka seuraavan julkaisun tavoitevuosi tiedetään etukäteen, ei siihen haluta sitoutua ennen kuin versio on valmis. Niinpä kehittämisen aikana lyödään lukkoon vain vuosikymmen ja jätetään vuosi auki. Niinpä C++14 tunnettiin ennen julkaisuaan nimellä C++1y ja C++17 oli C++1z (C++11:n oli alunperin tarkoitus tulla ulos ennen vuotta 2010, joten se kulki nimellä C++0x. Tätä hävetään vieläkin.) Seuraavien standardiversioiden työnimet ovat C++2a, C++2b ja C++2c.
Tämän vuoden helmikuussa lyöntiin lukkoon C++20:n ominaisuudet ja standardi siirtyi lopulliseen äänestykseen. Nyt mukana on todella suuria uusia ominaisuuksia, mm. conceptit (template <typename T> requires sortable<T>), moduulit (import <iostream>, korvaa #includen (vihdoin!)), korutiinit (coroutines, vrt. Pythonin generaattorit), spaceship operator <=> (oman tyypin kaikki vertailuoperaatiot yhdellä funktiolla) sekä välit (ranges, yleistää iteraattoreiden käsitettä ja mahdollistaa mm. algoritmien putkituksen, alkioiden laiskan suodattamisen yms.)
Standardointityö käytännössä
Virallisesti C++-standardointikomitean nimi on “ISO/IEC JTC1 (Joint Technical Committee 1) / SC22 (Subcommittee 22) / WG21 (Working Group 21)”. Käytännössä “komitea” on joukko ihmisiä, joilla on halu kehittää kieltä, aikaa kirjoittaa standardointiehdotuksia ja osallistua komitean tapaamisiin. Tyypillisesti komitean jäsenet ovat jonkin kansallisen standardointijärjestön jäseniä (kuten minä Suomen standardisoimisliiton asiantuntijajäsen), kääntäjävalmistajia (Microsoft, Intel jne.), suuria C++:n käyttäjiä (Facebook, Google, Nvidia, Apple, Adobe, …) tai yksittäisiä kirjastojen yms. sovelluskehittäjiä. Mutta mukaan voi tulla kuka vain, esim. marraskuun kokouksessa erään paperin yksi kirjoittajista oli suomalainen luokiolainen. Kuriositeettina mainittakoon, että Suomen virallinen edustaja Ville Voutilainen on ollut standardoinnssa viime vuosina erittäin keskeisessä roolissa. Ajan kuluessa osallistujamäärä on kasvanut parista kymmenestä yli kahteen sataan, mikä tekee standardointitapaamisten käytännön järjestelyistä varsin haastavia, varsinkin kun standardointityö saa suuren osan rahoituksestaan lahjoituksina (käytännössä suurilta firmoilta).
Paras tapa kuvata standardointitapaamisen etenemistä käytännössä on koordinoitu kaaos. Nykyisin osallistujia on niin paljon ja C++ niin laaja, että standardointityö on jaettu useaan eri ryhmään kuten Core language, Library Evolution, Concurrency, Unicode, Machine learning, Reflection, Numerics, jne. Pari vuotta sitten käynnistyi myös C++:n opetukseen keskittyvä ryhmä. Oma osuuteni tapahtuu lähinnä ryhmissä Concurrency (jossa olen tehnyt pari paperia rinnakkaisesta poikkeus/virhekäsittelystä) ja juuri tuo Education-ryhmä (jossa olen ollut lähinnä kuuntelijana).
Standardointitapaamisen alussa on “virallinen” ISO:n (International Standardization Organization) protokollan vaatima avaussessio, jossa käydään läpi virallinen ohjelma ja tavoitteet. Sen jälkeen osallistujat jakaantuvat omien intressiensä mukaan ryhmiin, joissa etukäteen toimitettuja standardointiehdotuksia käydään läpi. Ainakin itselläni oman aikataulun hallitseminen on ollut haastavaa ne kaksi kertaa, kun olen paikalle päässyt. Yllättävän suuri osa käytännön tiedonkulusta tapahtuu epävirallisia henkilökohtaisia kanavia pitkin, ja eri ryhmillä on eri käytäntöjä siitä, pidetäänkö käsiteltävien ehdotusten aikataulut virallisessa Wikissä, Githubin issuetrackerissä tai jossain muualla. Eräs konkari hyvin kuvasikin, että standardointitapaamiseen ensimmäisen kerran osallistuminen tarkoittaa aika lailla liikkuvaan junaan täydessä vauhdissa hyppäämistä, eikä kenelläkään ole aikaa erikseen paapoa uusia tulokkaita, vaan asiat täytyy vain itse yrittää selvittää ja hyväksyä se, että kaikkeen ei ehdi, jaksa eikä ymmärrä osallistua.
Ryhmissä keskeskustelut ovat periaatteessa hyvin demokraattisia, eli kuka tahansa paikallaolija saa asioista äänestettäessä antaa äänensä. Keskustelun ja äänestysten on tarkoitus “lisätä konsensusta”, eli tuottaa ratkaisuja, joiden kanssa kaikki pystyvät elämään. Käytännössä ymmärrettävistä syistä jotkut tahot ovat standardoinnissa tasa-arvoisempia kuin toiset, joten jos esim. jokin suurista kääntäjävalmistajista ilmoittaa, että jotain ominaisuutta ei pysty toteuttamaan, niin muiden äänillä ei juuri ole merkitystä (elleivät muut sitten pysty osoittamaan, että järkevä toteutustapa on olemassa). Muutenkin puheenvuorojen osalta huomaa nopeasti, että ryhmät jakautuvat “konkareihin”, jotka kuuntelevat toisiaan tarkalla korvalla ja “tulokkaisiin”, joiden puheenvuorot ja kommentit hyväksytään, mutta niihin ei oletuksena käytetä paljoa aikaa. Osallistuminen keskusteluihin on myös henkisesti varsin nöyräksi pistävä kokemus: Hyvin pian tajuaa olevansa joukossa, joissa suurin osa osallistujista tietää jostain ohjelmointikielten osa-alueesta paljon enemmän kuin itse. Ja henkilökohtaisena kokemuksena on myös jossain määrin hermostuttavaa keskustella oman osaamisen äärirajoilla olevasta asiasta sellaisen ihmisen kanssa, joka keskustelun aikana ajankuluksi ratkaisee puhuessaan 8x8x8 Rubikin kuutiota käsittämättömällä nopeudella…
Kaikki nuo standardointiin tuodut ehdotukset ovat julkisia, joten niihiin voi halutessaan tutustua osoitteessa http://www.open-std.org/jtc1/sc22/wg21/docs/papers/. Sen sijaan itse tapaamisen keskustelut tapahtuvat siinä mielessä suljettujen ovien takana, että osallistujien toivotaan olevan tiedottamatta mistään tuloksista ulkomaailmaan ennen kuin koko tapaaminen on ohi (väärien huhujen leviämisen estämiseksi). Paljon tietoa C++-standardoinnista löytyy myös osoitteesta https://isocpp.org/.
C++2b ja tulevaisuus
C++ on tällä hetkellä varsin aktiivisessa kehitysvaiheessa. C++20 aloitti monia suuria uudistuksia (kuten moduulit ja conceptit), joiden täysi käyttöönotto C++:n standardikirjastossa C++23:n myötä tulee muuttamaan käytännön ohjelmointia paljon. Moduulien myötä #include ja sen ongelmat ovat toivottavasti vihdoin historiaa. Conceptien myötä kääntäjän virheilmoituksen esim. STL:n käytössä selvenevät huomattavasti ja koko standardikirjaston käytöstä saadaan sujuvampaa. Toivottavasti myös C++20:sta pois tippuneet sopimussuunnittelun (contract programming) mahdollistavat esi- ja jälkiehdot sekä invariantit saadaan C++23:een. C++:n yksi päätavoite on aina ollut tehokkuus, ja nyt tehokkuutta pyritään laajentamaan aktiivisesti rinnakkaisuuden suuntaan. Välien (ranges), koruutinien (coroutines) ja putkituksen (pipeline operator |>) saaminen kieleen muuttavat radikaalisti sitä, miten STL:n algoritmeja kannattaa käyttää ja kuinka paljon kääntäjä pystyy niitä optimoimaan, samalla myös rinnakkaisuuden hyödyntäminen STL:ssä helpottuu huomattavasti. Tulevaisuuden maalina on myös heterogeenisen laskennen tukeminen, jolloin osa laskennasta voi tapahtua (joko automaattisesti tai ohjatusti) CPU:illa, osa GPU:illa ja osa vaikka esiohjelmoidusti FPGA:lla jne, ja geneeriset algoritmit osaisivat itse adaptoitua erityyppiseen tarjolla olevaan rinnakkaisuuteen.
Kaiken kaikkiaan on mielenkiintoista nähdä, kuinka vanhan valtavirtakielen evoluutio ja kehitys tapahtuu. Keskusteluja kuunnellessa alkaa tajuta, kuinka paljon vanhoista ominaisuuksista kiinni pitäminen ja uusien ominaisuuksien lisääminen johtuu käytännön tarpeista. Vaikka yhdistelmä väistämättä johtaa kielen paisumiseen, niin sille ei oikein mahda mitään. Samoin standardointiin osallistumisen myötä ainakin itselleni on kirkastunut kuva siitä, että tehokkuus on tällä hetkellä asia, joka ohjaa entistä enemmän C++:n kehitystä. Tähän ajavat niin virtapihit sulautetut järjestelmät, itseajavat autot, laskentatehoa vaativat tekoälyalgoritmit, matkapuhelinten akunkesto kuin suurten pilvipalvelinkeskusten sähkönkulutus ja jäähdytys.
Kirjoittanut Matti “Bitti” Rintala