Zatiaľ čo programovacie jazyky Visual Basic a C# sa tešia večne neutíchajúcemu záujmu zo strany vývojárov pracujúcich na platforme Microsoft .NET Framework 2.0, 3.0 a 3.5, len zlomok programátorov vie, že okrem „bejziku“ a „síšárpu“ existuje taktiež niečo, čomu sa vraví C++/CLI.
C++/CLI alebo riadené C++ je .NET-kompatibilný programovací jazyk, ktorý sa objavil spoločne s uvedením Visual Studia 2005, teda pred viac ako dvoma rokmi. Na technických seminároch a konferenciách zameraných na vývoj počítačového softvéru a demonštráciu programátorských techník však s nevôľou zisťujem, že formulka C++/CLI je takmer neznáma. V skutočnosti o existencii a použití tohto programovacieho jazyka vie len maličký segment vývojárov. Z objektívneho pohľadu vznikol tento neutešený stav ako dôsledok mnohých rôznych faktorov. Keď sa spýtate programátorov píšucich svoj softvér pre platformu .NET, čo ich ako prvé napadne po vyslovení zaklínadla „dotnet“, tak drvivá väčšina vám povie, že v ich mysliach panuje silná asociácia s jazykmi C# a Visual Basic. Nech už sa pozriete kamkoľvek, spomenuté programovacie jazyky sú bez výnimky propagované ako prostriedky „prvej voľby“, ktoré sú k dispozícii vývojárom plánujúcim stavať svoje riadené aplikácie v štýle .NET.
Áno, máte pravdu, C# a Visual Basic sú bez ošemetného glorifikovania skvelé jazyky. Sám ich už mnoho rokov používam, pričom tu a tam medzi nimi prepínam, čo mi umožňuje využívať pozitíva jedného alebo druhého jazyka. Na riešenie istej problémovej oblasti sa lepšie hodí C#, zatiaľ čo v iných partiách zase sebavedomo boduje Visual Basic. A prosím, pustite z hlavy slová nekvalifikovaných programátorov a zlomyseľníkov, ktorí hlásajú, že Visual Basic je iba hračkou a nie skutočným programovacím jazykom na budovanie počítačového softvéru. Visual Basic sa vo verzii 2008 predvádza vo vskutku skvelej forme – veď len miera zapracovaných syntakticko-sémantických inovácií svedčí o životaschopnosti tohto programovacieho prostriedku. Nechajme však Basic a C# na moment stranou, pretože mojou snahou, ktorá sa bude preplietať celým týmto článkom, je vyzdvihnutie pozície jazyka C++/CLI.
Zrodenie riadeného C++
Aby sme sa nedopustili omylu, rád by som uviedol, že s riadeným C++ sa mohli programátori po prvýkrát stretnúť už v roku 2002, presne vtedy, keď bola s veľkou pompou uvedená nová generácia programovacích nástrojov v súprave .NET. V týchto časoch sa v produkte Visual C++ .NET objavil jazyk v origináli pomenovaný ako Managed Extensions for C++. Syntaktické rozšírenia predstavovali doplnenie natívneho C++ (navrhnutého podľa štandardu ISO/IEC 14882:1998) o programové rysy, ktoré by dovoľovali použiť C++ čoby prostriedok na písanie aplikácií bežiacich v prostredí .NET. Pravdu povediac, začlenené rozšírenia mali tak hlboký dosah na jazyk C++, že sa o projekte Managed Extensions for C++ začalo hovoriť ako o zbrusu novom programovacom jazyku. Nuž, a tak sme získali prvé riadené C++, kde adjektívum „riadené“ bolo s dôvtipom zvolené tak, aby ostro kontrastovalo s existujúcim „natívnym“ C++. Skvelé je, že tento terminologický dodatok nenecháva žiadny priestor na nedorozumenie, lebo všetko, čo sa javí ako riadené, je určené pre virtuálny exekučný systém platformy .NET Framework.
Rozšírenia uvedené v Managed Extensions for C++ sa dotýkali lexikálnej, syntaktickej aj sémantickej stránky programovacieho jazyka, a je preto jasné, že tieto modifikácie museli byť reflektované aj v kompilátore produktu Visual C++ .NET. Microsoft pre riadené C++ pripravil nový prekladač, ktorý dokázal generovať zostavenia (assembly) s MSIL kódom, typovými metadátami a ďalšími potrebnými súčasťami.
Tým pádom získal produkt Visual C++ .NET citeľne na sile, pretože sa jednou ranou stal široko ďaleko jediným nástrojom, ktorý umožňoval vytvárať softvér podľa nasledujúcich scenárov:
-
Písanie programov v jazyku C s pomocou knižnice CRT.
-
Písanie programov v jazyku C v spolupráci s Win32 API.
-
Písanie programov v jazyku C++ s podporou štandardnej knižnice a STL.
-
Písanie programov v jazyku C++ spoločne s knižnicami MFC a ATL.
-
Písanie riadených (.NET) programov pre systém CLR s využitím bázovej knižnice tried (BCL) v jazyku Managed Extensions for C++.
Pre úplnosť dodajme, že Visual C++ .NET združoval projektové šablóny, vďaka ktorým mohli vývojári zakladať funkčné skelety svojich budúcich riadených aplikácií. K dispozícii bola dokonca tiež šablóna na stavbu formulárových aplikácií, ktorých základy boli položené na obľúbenej objektovej knižnici Windows Forms (alebo WinForms skrátene, ak chcete). Nanešťastie, dobrý nápad nebol dotiahnutý do konca, pretože Visual C++ .NET chýbal prívetivý vizuálny návrhár formulárov, čo bol pomerne tvrdý oriešok, s ktorým si nemálo vývojárov nevedelo rady. Zmienený oriešok sa nakoniec pekne rozluskol sám, keď v prvej polovici roka 2003 uzrel svetlo sveta Visual C++ .NET 2003 s plnou podporou vizuálneho programovania, čím sa v tejto disciplíne vyrovnal tandemu tvorenému Visual Basicom .NET 2003 a Visual C# .NET 2003. Avšak, pokiaľ by sme zvažovali celkový počet prítomných projektových šablón na vývoj aplikácií .NET, tak Visual C++ .NET nebol ani vo verzii 2003 rovnocenným partnerom spomenutej „veľkej dvojky“.
V odborných kruhoch sa názory na jazyk C++ s Managed Extensions dosť často rozchádzali. Ak si dovolíme to zjednodušenie, a rozdelíme všetkých vývojárov na „komerčných“ a „akademických“, tak najmä z prvej skupiny sa ozývali námietky voči snáď až príliš prekombinovanej syntaxi a ťažkopádnej sémantike riadeného C++. Čo dodať, potreby komerčných programátorov sú iné ako požiadavky teoretických informatikov, počítačových vedcov a ďalších odborných persón pôsobiacich na vysokých školách informatického zamerania. Bežný vývojár chce jazyk, pomocou ktorého bude môcť dosiahnuť maximálnu možnú pracovnú produktivitu. Jeho cieľom je vytvorenie softvéru presne podľa prianí zákazníka, za ktorý dostane patričnú odmenu (pragmaticky zmýšľajúci programátor nevidí v použití jazyka žiadnu mágiu). Na druhej strane, ľudia pôsobiaci v akademickej sfére majú tendenciu dlho-predlho skúmať produkt, pričom to ozajstné vzrušenie prežívajú, keď sa môžu dosýta pohrať so všetkými programovými rysmi, voľbami prekladača a ďalšími, rýdzo špecializovanými a výsostne jemnými atribútmi analyzovaného produktu.
Napriek tomu, že jazyk C++ s Managed Extensions bol z pohľadu akademikov skvostným dielom umožňujúcim písať programy pre platformu .NET, jeho praktické rozšírenie a obľuba medzi vývojármi zostali na tristnej úrovni. Bola to skutočne škoda, pretože riadené C++ malo na viac, než sa v tej dobe javilo. Popri vývoji aplikácií .NET spomeňme trebárs možnosť „miešať“ natívny a riadený zdrojový kód, alebo veľmi rýchlo portovať existujúce natívne programy do riadeného prostredia. Nezriedka stačilo len vziať aplikačný kód a skompilovať ho s prepínačom /clr kompilátora.
C++/CLI: Riadené C++ v novom vydaní
Hoci sa jazyku C++ s Managed Extensions nepodarilo naplno preraziť, bolo jasné, že myšlienka urobiť z jazyka C++ komfortný nástroj na písanie aplikácií .NET vytyčovala cestu správnym smerom. Zlomom v ďalšej evolúcii sa stal november roku 2005, keď sa medzi vývojárov dostalo nové riadené C++. Vari zo všetkých strán inovovaný jazyk bol pomenovaný ako C++/CLI, pričom CLI predstavuje akronym pre spoločnú jazykovú infraštruktúru platformy .NET (Common Language Infrastructure). V C++/CLI sa odohralo skutočné zemetrasenie, ktoré nenechalo kameň na kameni, našťastie v pozitívnom zmysle slova. S najväčším potešením bolo posudzované razantné prečistenie syntaxe, vypustenie krkolomných kľúčových slov a zavedenie moderných syntaktických konštrukcií, ktoré konečne dávali na známosť, že nové riadené C++ sa stáva prvotriednym obyvateľom univerza .NET.
Stále platí, že základom na projektovanie jazyka C++/CLI je natívne C++, ktoré odpovedá štandardu ISO/IEC 14882:2003 (tento štandard z roku 2003 bol minoritnou aktualizáciou predchádzajúceho štandardu z roku 1998). Jazyk C++/CLI podstúpil štandardizačný proces organizácie ECMA, výsledkom ktorého bolo prijatie štandardu ECMA – 372 C++/CLI Language Specification. Zatiaľ nie je pre jazyk C++/CLI k dispozícii štandard ISO, a ako sa tak zdá, je vo hviezdach, či takýto štandard bude v dohľadnej dobe vôbec prijatý (technickej komisii nie je akosi pochuti už samotný názov jazyka, ktorý je vraj veľmi ľahko zameniteľný s pôvodným, a teda natívnym C++).
Pri projektovaní jazyka C++/CLI boli stanovené tieto ciele:
- Vytvoriť elegantnú syntaktickú a sémantickú bázu, ktorá bude prirodzená a ľahko pochopiteľná pre programátorov v C++.
- Začlenenie prvotriednej podpory pre programové rysy CLI (ide napríklad o prácu s hodnotovými a odkazovými dátovými typmi, podporu generického programovania a automatickú správu pamäte zrodených objektov).
- Priniesť všetko dobré z natívneho C++ (napríklad možnosť pre deterministické uvoľnenie zdrojov asociovaných s objektmi).
- Eliminovať výskyt zložitých a spletitých syntakticko-sémantických konštrukcií tak neslávne známych z jazyka C++ s Managed Extensions.
Dobrou správou je, že všetky predostreté ciele sa tvorcom jazyka podarilo naplniť. Niekedy to síce bolo za cenu hlbokých zásahov do internej štruktúry jazyka, avšak vynaložené úsilie stálo bezpochyby za to. Jazyk C++/CLI kompletne nahrádza staršie riadené rozšírenia zhmotnené v C++ s Managed Extensions. C++/CLI je implementovaný v produktoch Visual C++ 2005, Visual C++ 2005 Express, Visual C++ 2008 a Visual C++ 2008 Express. Verzia 2005 je teda prvou edíciou Visual C++, v ktorej sa s jazykom C++/CLI stretávame. Ďalšia potešujúca správa je, že jazyk C++/CLI je v identickej podobe prítomný aj v najnovšej verzii 2008 produktu Visual C++. Pre vývojárov to znamená, že nadobudnuté znalosti a praktické skúsenosti môžu okamžite uplatniť aj v najnovšom prostredí.
Osobitnú pozornosť si zaslúži kompilátor jazyka C++/CLI, ktorý prichádza s novými prepínačmi. Tieto precíznejšie upravujú štruktúru a štýl správania sa vygenerovaných zostavení aplikácií .NET. Bližšiu charakteristiku prepínačov kompilátora môžete nájsť v tab. 1.
| Tab. 1: Prehľad prepínačov kompilátora jazyka C++/CLI |
| Prepínač |
Textový opis prepínača |
Charakteristika |
| /clr |
Common Language Runtime Support |
Podpora jazyka C++/CLI so zmiešaným kódom. |
| /clr:pure |
Pure MSIL Common Language Runtime Support |
Podpora jazyka C++/CLI s riadeným kódom. |
| /clr:safe |
Safe MSIL Common Language Runtime Support |
Podpora jazyka C++/CLI s riadeným a verifikovateľným kódom. |
| /clr:oldSyntax |
Common Language Runtime Support, Old Syntax |
Podpora jazyka C++ s Managed Extensions. |
Základným prepínačom aj naďalej zostáva /clr, ktorého aktivácia spôsobí preklad zdrojového kódu do inštrukcií jazyka MSIL. Prepínač /clr sa kamaráti so zmiešaným kódom, čo znamená, že v kóde sa môžu bez akýchkoľvek problémov vyskytovať fragmenty riadeného C++/CLI i natívneho C++. Tento prepínač je vhodnou voľbou vtedy, keď pracujeme na aplikácii, ktorej jadro spočíva na rozhraní oboch svetov.
Vyšší stupeň konformity so zásadami CLI reprezentuje prepínač /clr:pure – jeho pričinením môžu vznikať rýdze zostavenia s riadeným kódom (natívny kód nie je povolený). Ešte viac, a pravdu povediac, úplne najviac ako to len ide, sa k princípom spoločnej jazykovej infraštruktúry hlási prepínač /clr:safe, ktorý dokáže vyprodukovať bezpečné zostavenia. Pre bezpečné zostavenie platia dve zásady: Po prvé, bezpečné zostavenie obsahuje verifikovateľný MSIL kód – to je kód, ktorý spĺňa kritéria dané verifikačným algoritmom JIT kompilátora. No a po druhé, bezpečné zostavenie obsahuje validné typové metadáta. Nemôže sa preto stať, že by bezpečné zostavenie obsahovalo poškodené metadáta, alebo že by v ňom bol uložený potenciálne nebezpečný (neverifikovateľný) MSIL kód. Bezpečné zostavenia zostrojené pomocou prepínača /clr:safe kompilátora sú úplne zhodné so zostaveniami, ktoré vytvárajú prekladače jazykov Visual Basic a C#.
Na samý koniec sme si nechali prepínač /clr:oldSyntax, ktorý odsúva jazyk C++/CLI do ústrania a na jeho pozíciu umiestňuje staršiu verziu riadeného C++ s Managed Extensions. Nie je nutné dodávať, že užitie tohto prepínača nie je pre nové projekty vôbec vhodné a uplatňuje sa len pri potrebe úprav existujúcich programov napísaných ešte v jazyku C++ s Managed Extensions.
Čo je nové v jazyku C++/CLI
V tejto podkapitole by som sa rád zameral na niektoré z najvýznamnejších inovácií, s ktorými jazyk C++/CLI prichádza. Začneme pozvoľna, a síce preťaženým aritmetickým operátorom +, ktorý nám dovoľuje zreťazovať Unicode textové reťazce (konečne tak môžeme zabudnúť na neustále volanie statickej metódy Concat triedy System::String). Ďalej je na rade mechanizmus zjednotenia typov a jeho spätný chod, čiže programové operácie boxing a unboxing. Základnou myšlienkou uvedeného mechanizmu je schopnosť manipulovať s inštanciou hodnotového typu ako s objektom. Hoci v C++ s Managed Extensions bol boxing realizovaný explicitne príkazom __box, v jazyku C++/CLI je tento proces uskutočňovaný implicitne, teda kedykoľvek kompilátor usúdi, že je to potrebné (v tomto smere sa jazyk C++/CLI správa rovnako ako C# a Visual Basic). Spätný chod mechanizmu zjednotenia typov bol skôr zahájený spravidla operátorom dynamic_cast<>, čo je možnosť, ktorú máme aj v C++/CLI. Okrem toho však môžeme hodnotu z objektu získať tiež pomocou operátora safe_cast<>, či explicitnou typovou konverziou v štýle jazyka C.
Prejdime na hodnotové triedy a štruktúry a enumeračné (vymenované) typy. Tieto sa deklarujú ako value class, value struct a enum class. Odkazové triedy (skôr s prívlastkom __gc class) sú teraz deklarované ako ref class a rozhrania sa vytvárajú pomocou kontextovo spojených kľúčových slov interface class. Rozhrania môžu implementovať nielen odkazové triedy, ale tiež ich hodnotové náprotivky. Objekty odkazových tried sú zakladané operátorom gcnew, ktorý garantuje ich alokáciu na riadenej halde. Kedykoľvek je objekt odkazovej triedy skonštruovaný, operátor gcnew vráti riadený manipulátor, prostredníctvom ktorého je objekt situovaný na riadenej halde dosiahnuteľný. Riadený manipulátor je syntakticky stvárnený symbolom striešky (^). Manipulátor ^ je nástupcom riadeného smerníka (__gc*) z jazyka C++ s Managed Extensions. Je dobré mať na pamäti, že uvedený manipulátor smeruje vždy na „celý“ objekt. Ak budete potrebovať pristupovať k podobjektom, môžete s výhodou využiť schopnosti interných smerníkov interior_ptr. Manipulátor ^ smie byť podrobený dereferencii rovnako ako natívny smerník.
V súvislosti s kreáciou objektov odkazových tried má jazyk C++/CLI v rukách ešte jedno eso. Tým je zásobníková sémantika, vďaka ktorej možno ref triedu inštanciovať syntakticky takisto ako hodnotovú (value) triedu. Uveďme malú ukážku. Povedzme, že máme odkazovú triedu T. Potom príkaz zakladajúci objekt tejto triedy pomocou zásobníkovej sémantiky vyzerá takto: T obj; kde obj je názov premennej, prostredníctvom ktorej je zostrojený objekt dosiahnuteľný. Ak máte skúsenosti s jazykom C++, mohli by ste dospieť k pozoruhodnému záveru: dobre, no objekt bude alokovaný na zásobníku programového vlákna a nie na riadenej halde, všakže? Nie, vážení priatelia, nie je to tak – to sú len čary a troška mágie jazyka C++/CLI. Vždy totiž platí jednoduché pravidlo: Nech už je objekt založený akýmkoľvek syntaktickým spôsobom, ak ide o objekt odkazovej triedy, je vždy uložený na riadenú haldu (presnejšie do nultej objektovej generácie riadenej haldy).
Veľmi rozsiahle zmeny poznačili finalizáciu objektov odkazových tried. Bohužiaľ na podrobnú rozpravu nie je v tejto chvíli priestor, no najdôležitejšou zmenou je skutočnosť, že deštruktory sú interne prevádzané na metódy Dispose rozhrania System::IDisposable. Na objekt, resp. riadený manipulátor smie byť aplikovaný operátor delete, ktorý iniciuje aktiváciu deštruktora (teda metódy Dispose). Deštruktor uskutoční explicitné uvoľnenie nepotrebných zdrojov, ktoré sú s objektom riadenej triedy spojené (ale pozor, tým sa nekončí životný cyklus samotného objektu). Novým adeptom na scéne je finalizér, čo je metóda s názvom !T (T je identifikátor odkazovej triedy). Finalizér je spúšťaný automatickým správcom pamäte v procese implicitnej finalizácie objektu odkazovej triedy. Je samozrejme na programátorovi, či bude chcieť triedu navrhnúť s finalizérom – potom budú objekty zrodené z tejto triedy vyžadovať svoju finalizáciu (a preto bude k ich likvidácii potrebných viac cyklov kolekcie správcu pamäte).
Odkazové triedy deklarované v jazyku C++/CLI môžu mať práve jedného priameho predka, nakoľko viacnásobná dedičnosť nie je v prostredí .NET povolená. Aj v tomto smere ale poteší prívetivosť jazyka C++/CLI, ktorý uvádza implicitnú verejnú jednoduchú dedičnosť. Programátori sú tak zbavení povinnosti vypisovať kľúčové slovo public pri každom odvodzovaní podtried. Kompletne prepracovanou syntaxou sa môžu pochváliť skalárne vlastnosti, ktorých vetvy na čítanie a modifikáciu privátnych dátových členov tried sú odteraz zoskupené do jedného syntaktického bloku. Skalárne vlastnosti tak vyzerajú podobne ako tie, ktoré poznáte z jazyka C#. Minoritnými zmenami prešli aj abstraktné a zapečatené triedy, v deklaráciách ktorých sa modifikátory abstract a sealed nachádzajú až za identifikátormi týchto tried.
C++/CLI má budúcnosť
Programovací jazyk C++/CLI je moderným prostriedkom na vytváranie výkonných aplikácií bežiacich na vývojovo-exekučnej platforme Microsoft .NET Framework 2.0, 3.0 a 3.5. Pôvabná syntax, logickejšie programové konštrukcie a možnosť tesnej spolupráce s BCL z neho robia v očiach C++ vývojárov ideálneho kandidáta na tvorbu riadených aplikácií. Ak máte chuť sa s jazykom C++/CLI bližšie zoznámiť, prevezmite si z webu bezplatne inštalačný balíček produktu Visual C++ 2008 Express a vydajte sa za novými dobrodružstvami. Som si istý, že objavíte mnohé malebné zákutia jazyka, s ktorým je konečne radosť pracovať.
Ján Hanák