Pokud píšete kód v C++ a používáte lambda výrazy, resp. bloky, mohou se vám hodit makra, která vás odstíní od rozdílů v syntaxi C++0× (nový standard C++) a LLVM (implementace Applu). Zatímco lambda výraz „x → x + 1“ se v C++0× píše
[](int x){ return x + 1; }
implementace Applu vypadá takto:
^(int x) { return x + 1; }
Ve svém kódu používám makro preprocesoru, které mě od rozdílu v syntaxi odstíní, a zápis pak vypadá takto:
BLOCK()(int x) { return x + 1; }
Pokud potřebujete deklarovat proměnnou pro lambda výraz, resp. blok, lze také použít makro (následně uvádím zápis v C++0×, LLVM a s makrem):
function<int(int)> block
int(^block)(int)
BLOCKVAR(block, int, int)
První argument je jméno proměnné, druhý argument je typ návratové hodnoty a dále následují typy argumentů bloku.
Makro BLOCK dostává jako parametr seznam proměnných použitých v bloku (nutné pro C++0×, v LLVM se tento seznam ignoruje). Příklad použití:
int x = 1;
BLOCK(x)(int n) { return x + n; }
Pokud bychom proměnnou x neuvedli, oznámil by kompilátor GCC chybu „‚x‘ is not captured“ (pokud použijete verzi GCC od Applu, budou se používat bloky Applu, jejichž syntax a implementace se od té v LLVM téměř neliší).
Zde je deklarace použítých maker:
#ifdef __BLOCKS__
Vzhledem k rozdílné sémantice je třeba pamatovat na proměnné používané v bloku referencí. Ve výčtu pro C++0× je nutné použít znak &, např. &x. Pro LLVM se při deklaraci takové proměnné používá __block, např. __block int x. Tohoto rozdílu se lze zbavit makrem, které pro C++0× definuje __block jako nic (ignoruje se).
Další rozdíl spočívá ve faktu, že bloky v LLVM se vytváří na zásobníku (a jsou tedy např. po návratu z funkce zrušeny). Pokud potřebujete delší životnost bloku, musíte použít Block_copy, abyste zkopírovali blok na haldu (a až už blok nepotřebujete, tak Block_release). V C++0× se o to starat nemusíte, takže kvůli přenositelnosti musíte Block_copy a Block_release používat, ale pro C++0× si můžete tyto funkce nadefinovat tak, že první vrací svůj argument (předaný blok) a druhá nedělá nic.
Úlohy na pozadí
Bloky se hodí kromě jiného pro spouštění kódu na pozadí (v jiném vlákně). Bohužel ani zde neexistuje (zatím) žádná univerzálně použitelná metoda. S využitím výše uvedeného makra používám často ještě DETACHTHREAD:
DETACHTHREAD(BLOCK() { cout << "OK" << endl; });
Kód není v obou prostředích zcela ekvivalentní, neboť zatímco thread prostě vytvoří a spustí nové vlákno, NSOperationQueue spravuje vlastní pool vláken a kód pouští podle vlastního uvážení (např. podle počtu procesorů, resp. jader). Bylo by možné použít nízkoúrovňovou třídu NSThread, ale v naprosté většině případů není tento rozdíl podstatný.
NB: Výše uvedený kód byl testován s GCC 4.5 a LLVM 1.7.
Autor se zabývá vývojem kompilátorů a knihoven pro objektově-orientované programovací jazyky.
Přečteno 37 774×
Přečteno 26 420×
Přečteno 24 934×
Přečteno 21 278×
Přečteno 18 932×