Hybridní jazyky

29. 7. 2011 13:14 zboj

V tomto článku porovnávám Objective-C++ a C++/CLI, dva hybridní jazyky vzniknuvší „sloučením“ dvou zavedených jazyků. V prvním případě jde o Smalltalk a C++, ve druhém případě o C++ a C# (zjednodušeně řečeno, viz níže).

Objective-C++

Brad Cox a Tom Love vytvořili jazyk Objective-C jako objektově-orientované rozšíření (inspirované Smalltalkem) jazyka C. Původní kompilátor byl jen jakýmsi preprocesorem, jenž kód v Objective-C převedl do jazyka C (podobně jako tomu bylo v počátcích u C++). Deklarovaným cílem bylo vytvořit moderní jazyk (OO bylo tehdy podobnou módou jako dnes např. HTML5), ve kterém by ovšem bylo možné používat knihovny napsané v jazyce C. Objective-C++ je založené na stejném principu, ale je nadstavbou jazyka C++.

S jazykem Objective-C(++) se dnes setkáte nejspíše při programování pro Mac OS X a iOS. Pro Objective-C je k dispozici rozsáhlá knihovna, ale občas potřebujete použít v kódu cizí knihovnu napsanou v C nebo C++. Jako příklad mějme třídu Translator, která implementuje strojový překlad z angličtiny do češtiny (ne pomocí Google Translate, ale nativně; takový kód opravdu existuje v rámci projektu Apertium). Použití je následující:

Translator t = new Translator("en", "cs"); std::string s = t.translate("Hello, world!"); // vrátí "Ahoj, světe!"

Nyní si představme, že chceme kód z Apertia použít v aplikaci pro Mac OS X nebo iOS. Standardním postupem je implementovat tzv. wrapper, tj. třídu v Objective-C, která vývojáře odstíní od nízkoúrovňového kódu v C nebo C++. V našem případě se nabízí vytvořit třídu APTranslator (prefix AP znamená Apertium), jež poskytne rozhraní pro použití knihovny. Toto rozhraní by mohlo vypada nějak takto:

@interface APTranslator : NSObject { Translator* translator; } - (NSString*)translateString:(NSString*)text; @end

Je zřejmé, že kromě vytvoření instance třídy Translator (což je triviální) musíme zajistit převod mezi NSString* a std::string. K tomu naštěstí můžeme využít metod, které nabízí třída NSString. Metoda translateString: by pak vypadala takto:

- (NSString*)translateString:(NSString*)text { std::string s = [text UTF8String]; s = translator->translate(s); return [NSString stringWithUTF8String: s.c_str()]; }

Pochopitelně je nutné implementovat také metody init a dealloc pro vytvoření a uvolnění instance třídy Translator. Tím jsme hotovi a můžeme třídu ATTranslator používat:

ATTranslator* translator = [[ATTranslator alloc] init]; NSLog(@"%@", [translator translateText: @"Hello, world!"]);

C++/CLI

Když Microsoft vytvořil platformu .NET, zveřejnil také zcela nový jazyk: C#. Pro .NET je ovšem možné programovat i v jiných jazycích, a tak kromě Basicu (ve verzi Visual Basic .NET) můžeme psát pro .NET také v C++, resp. jeho rozšíření, které se nazývá C++/CLI. Podobně jako u Objective-C(++) je C++/CLI nadstavbou jazyka C++ a účelem tohoto jazyka je umožnit použití knihoven napsaných v C a C++ v aplikacích pro .NET (typicky se taková nativni knihovna „zabalí“ do dynamické knihovny, tedy souboru s příponou .dll, kterou lze pak přímo použít v C#).

Na stejném příkladu jako výše pro Objective-C++ si ukážeme, jak by vypadal stejný „wrapper“ v C++/CLI. Jediným větším problémem je zajistit převod mezi nativním řetězcem (std::string) a řetězcem v .NET (String^). K tomu použijeme funkci marshal_as ze jmenného prostoru msclr::interop.

public ref class APTranslator { private: Translator* translator; public: APTranslator() { translator = new Translator("en", "cs"); } String^ translate(String^ text) { std::string s = marshal_as<std::string>(text); s = translator->translate(s); return marshal_as<String^>(s); } };

Použití je pak už jednoduché:

(gcnew APTranslator("en", "cs"))->translate("Hello, world!")

Pochopitelně ještě musíme uvolnit instanci třídy Translator při zániku „wrapperu“.

Poznámku si zaslouží překlad kódu v C++/CLI. K dispozici jsou tři varianty: /clr:safe, /clr:pure a /clr. První z nich generuje tzv. bezpečný kód, který je ekvivalentní výsledku překladu kódu v C#. Problém je, že v takovém kódu nelze použít nativní kod v C/C++, čímž tato volba ztrácí smysl, protože pro čistý kód pro .NET je pak už stejně lepší psát v C#. Volba /clr umožňuje míchat téměř libovolně nativní kód s knihovnami .NET. Nativní kód je ale překládán přímo do kódu procesoru, čímž se připravujeme o cennou kompatibilitu výsledného spustitelného souboru. Nejlepší volbou je proto pro většinu aplikací volba /clr:pure, která také umožňuje použít nativní kód (včetně např. knihovny STL), ale kompiluje do mezikódu CLR, takže přeložený program lze spustit všude, kde je k dispozici .NET framework. I tato volba má jistá omezení vzhledem k nativnímu kódu, ale většinu knihoven takto přeložit lze.

Sdílet