Portál AbcLinuxu, 2. května 2025 20:24
Chtěl by jsem vám představit jeden ze svých projektů, na kterém jsem začal pracovat už v rámci diplomové práce (8 let zpět). Od té doby jsem jej párkrát využil při implementaci úkolů se kterými jsem se setkal v praxi. V případě, že nikoho nebude zajímat, nevadí, alespoň jsem pročistil starý kód a umístil jej na veřejné úložiště. Jedná se o program yapgen, který používám pro generování překladačů.
Program yapgen, jehož jméno vychází z tradice pojmenování lexikálních a syntaktických analyzátorů, slouží k rychlému prototypování, testování a generování lexikálního a syntaktického analyzátoru.
Překladač je definován souborem obsahujícím popis lexikálního a syntaktického analyzátoru. Tento soubor může obsahovat i sémantická pravidla generovaného jazyka, která jsou popsána skripty v programovacím jazyce LUA. Z popisu jazyka je programem yapgen vygenerován překladač, který je možné otestovat nad vstupním řetězcem, nebo uložit jeho zdrojový kód v jazyce C/C++.
Standardní postup při tvorbě nového překladače je následující: Autor vygeneruje triviální překladač, a k němu odpovídající vstupní řetězce. Prostřednictvím programu yapgen testuje funkci překladače na vstupních řetězcích a postupně rozšiřuje popis jazyka o nové požadované funkce. V okamžiku, kdy se autor dostane do bodu, kdy je s popisem svého překladače spokojen vygeneruje C/C++ zdrojový kód, který použije ve svém projektu.
Program yapgen akceptuje následující tři argumenty. Argument --parser_descr <file>
odkazuje na soubor obsahující popis překladače k vygenerování. Tento soubor obsahuje popis regulárních výrazů terminálních symbolů a pravidel gramatiky jazyka. Parametr --parser_save_cc <file>
odkazuje na cílový soubor, do kterého je vygenerován kód překladače v jazyce C++. Výsledný soubor obsahuje implementaci lexikálního a syntaktického analyzátoru přijímaného jazyka. Poslední argument --source <file>
odkazuje na soubor obsahující řetězec, který je použit jako vstupní řetězec vygenerovaného překladače.
Vygenerování překladače:
./yapgen --parser_descr demo_exp.rules
Vygenerování překladače a jeho spuštění nad vstupním řetězcem:
./yapgen --parser_descr demo_exp.rules --source demo_exp.src
Vygenerování překladače a následné vygenerování C++ kódu překladače:
./yapgen --parser_descr demo_exp.rules --parser_save_cc demo_exp.cc
Terminální symboly gramatiky přijímaného jazyka jsou popsány množinou regulárních výrazů. Na základě těchto regulárních výrazů se generuje lexikální analyzátor rozpoznávající symboly ve vstupním řetězci.
Jednotlivé terminální symboly jsou zapisovány v následujícím formátu:
<id> {<regexp>}
Kde <id>
zastupuje název (identifikátor) regulárního výrazu, a <regexp>
zastupuje kód obsahující regulární výraz. Terminální symboly mohou být pojmenovány standardními identifikátory, známými například z jazyka C.
oct_i32_const {'0'.<07>*}
dec_i32_const {<19>.d*}
hex_i32_const {'0'.[xX].(<09>+<af>+<AF>).(<09>+<af>+<AF>)*}
_SKIP_ {w.w*}
_SKIP__ {'#'.!'\n'*.'\n'}
_SKIP___ {"//".!'\n'*.'\n'}
_SKIP____ {"/*".(!'*'+('*'.!'/'))*."*/"}
_END_ {'\0'}
Terminální symboly, jejichž jméno obsahuje podřetězec _SKIP_
jsou tímto způsobem označeny jako symboly ignorované syntaktickou analýzou generovaného překladače. Tímto způsobem se často ve zdrojovém kódu jazyka označují bíle znaky a komentáře.
Terminální symboly, jejichž jméno obsahuje podřetězec _END_
jsou označeny jako tzv. ukončovací terminály, při jejichž přijetí ze zdrojového řetězce akcí shift dochází k ukončení rozkladu vstupního řetězce. Jako hodnota ukončovacího terminálu se často používá znak \0
identifikující konec vstupního řetězce.
Regulární výrazy popisující terminální symboly jazyka se skládají z následujících komponent, které je možné kombinovat prostřednictvím níže popsaných operátorů.
Zástupný symbol
Zastupuje jeden znak z předdefinované množiny znaků.
e
- epsilon, zastupuje řetězec s nulovou délkou.w
- white, jeden z bílých znaků (mezera, tabulátor, nebo nový řádek).d
- digit, jedna číslice 0-9
.l
- letter, jedno písmeno a-z
, A-Z
.Znak
Zastupuje jeden znak identifikovaný zápisem v apostrofech '<char>'
.
'a'
- zastupuje znak uvedený v apostrofech.'\60'
- zastupuje znak s ordinální hodnotou odpovídající číslu v osmičkové soustavě.'\x30'
- zastupuje znak s ordinální hodnotou odpovídající číslu v šestnáctkové soustavě.'\n'
- zastupuje znak se zvláštním významem identifikovaným symbolem za zpětným lomítkem. Symbol za lomítkem může být jeden z následujících: abfnrtv\?'">}]|
.Negace znaku
Zastupuje libovolný znak lišící se od znaku uvedeného v uvozovkách. Pro znak uvedený v uvozovkách může být použita stejná notace jako v případě komponenty Znak: !'<char>'
.
!'a'
'"'.(!'"'+'\\'.'"')*.'"'
Řetězec znaků
Zastupuje řetězec znaků, které jsou popsány znaky uvedenými na jednotlivých pozicích řetězce. Pro znaky na jednotlivých pozicích řetězce je možné použít stejnou notaci jako v případě komponenty Znak: "<char><char>..."
.
"if"
"else"
"multi\nline"
"/*".(!'*'+('*'.!'/'))*."*/"
Rozsah znaků
Zastupuje znak, jehož ordinární hodnota se nachází v rozmezí od ordinární hodnoty prvního znaku do ordinární hodnoty druhého znaku včetně krajních hodnot. Pro identifikaci prvního a posledního znaku je možné použít stejnou notaci jako v případě komponenty Znak: <<char><char>>
.
<19>.d*
'0'.[xX].(<09>+<af>+<AF>).(<09>+<af>+<AF>)*
Množina znaků
Zastupuje jeden znak z uvedené množiny znaků. Pro identifikaci jednotlivých znaků v množině je možné použít stejnou notaci jako v případě komponenty Znak: [<char><char>...]
.
'\''.'\\'.[abfnrtv\\?\'"].'\''
'0'.[xX].(<09>+<af>+<AF>).(<09>+<af>+<AF>)*.[lL]
Znaky s výjimkou
Zastupuje jeden znak, který se nenachází v uvedené množině znaků. Pro identifikaci jednotlivých znaků v množině je možné použít stejnou notaci jako v případě komponenty Znak: |<char><char>...|
.
'"'.(|"|+'\\'.'"')*.'"'
Nad výše uvedenými základními komponentami regulárních výrazů je možné používat následující operátory.
Konkatenace
Binární operátor .
. Pro přijetí regulárního výrazu je nutné přijmout levou a následně pravou stranu operátoru.
"Hello".' '."world"
<19>.d*
'x'.(<09>+<af>+<AF>).(<09>+<af>+<AF>+e)
Alternativa
Binární operátor +
. Pro přijetí regulárního výrazu je nutné přijmout levou nebo pravou stranu operátoru.
"i8"+"i16"+"i32"+"i64"+"u8"+"u16"+"u32"+"u64"+"f32"+"f64"
('_'+l).('_'+l+d)*
'x'.(<09>+<af>+<AF>).(<09>+<af>+<AF>+e)
Iterace
Unární operátor *
. Pro přijetí regulárního výrazu je nutné přijmout operand 0 až n-krát. Operand je umístěn před symbolem operátoru *
.
<19>.d*
('_'+l).('_'+l+d)*
Uvedené operátory jsou seřazeny podle priority (sestupně) následovně: iterace, konkatenace a alternativa.
Kulaté závorky ()
zvyšují prioritu uzavřeného regulárního podvýrazu obdobně jako závorky v aritmetických výrazech.
Samotný jazyk je popsán SLR(1) gramatikou, která je dostatečně silná pro popis obecného programovacího jazyka. Gramatika jazyka je definována výše uvedeným popisem terminálních symbolu, výčtem neterminálních symbolů a pravidly gramatiky skládajících se z terminálních a neterminálních symbolů.
Neterminální symboly gramatiky se zapisují do ostrých (angle) závorek.
<start>
<program>
Pravidla gramatiky jsou zapisovány v následujícím formátu:
<head> -> <item> <item> ... ->> {<code>}
Kde, <head>
je neterminál reprezentující hlavičku pravidla. Hlavička pravidla je od jeho těla oddělena znaky ->
. Tělo pravidla obsahuje posloupnost terminálních a neterminálních symbolů (<item>
), které jsou od sémantického kódu pravidla odděleny znaky ->>
. Sémantický kód reprezentovaný zdrojovým kódem v jazyce LUA je uzavřen ve složených závorkách {}
. V případě, že je potřeba v sémantickém kódu použít symbol }
je nutné před něj vložit znak zpětného lomítka \}
.
<command> -> <if_condition> <if_body> ->> {}
<if_condition> -> if <condition> ->> {print('if condition')}
<if_body> -> <command> ->> {print('if statement')}
<if_body> -> <if_else> <command> ->> {print('if else statement')}
<if_else> -> <command> else ->> {print('if else')}
Sémantická pravidla jazyka jsou při prototypování překladače zapsána prostřednictvím skriptů v jazyce LUA.
Voláním funkce rule_body
s jedním parametrem n
reprezentujícím celé číslo je možné získat řetězec odpovídající podřetězci zdrojového řetězce, který je reprezentován n
-tou položkou těla pravidla, podle kterého proběhla redukce.
Vygenerovaný analyzátor je jednoprůchodový. To znamená, že terminální symboly rozpoznané lexikálním analyzátorem jsou okamžitě zpracovány na vstupu syntaktické analýzy, bez nutnosti jejich zaznamenávání.
V této části uvádím příklad inkrementální tvorby překladače vyhodnocujícího jednoduché aritmetické výrazy.
První soubor pravidel vytvoří analyzátor, který je schopný na vstupu přijmout řetězec obsahující pouze bílé znaky (mezera, tabulátor nový řádek).
init_code: {}
terminals:
_SKIP_ {w.w*}
_END_ {'\0'}
nonterminals:
<start>
<end_check>
rules:
<start> -> <end_check> ->> {}
<end_check> -> _END_ ->> {}
Analyzátor rozpoznává dva terminální symboly, kde první symbol zastupuje posloupnost bílých znaků, a druhý zastupuje znak označující konec řetězce. Volání lexikálního analyzátoru akceptují ukazatel na znaky řetězce a jeho délku, a při překročení délky vstupního řetězce automaticky generují symbol '\0'
na vstupu. Hodnota ukončujícího terminálu není omezena na symbol \0
, ale může nabývat libovolné hodnoty, kterou je možné popsat regulárním výrazem.
Jak již bylo uvedeno v rámci popisu regulárních výrazů všechny terminály, které ve svém názvu obsahují podřetězec _SKIP_
jsou syntaktickou analýzou ignorovány. V případě, že terminální symbol obsahující ve svém názvu podřetězec _END_
je přesunut na LALR
rozkladový zásobník syntaktické analýzy (což znamená že neterminál nacházející se nalevo od tohoto symbolu byl kompletně přijat) je rozklad vstupního řetězce ukončen.
Z výše uvedeného vyplývá, že analyzátor vygenerovaný na základě prvního souboru pravidel v podstatě čeká na přijetí znaku '\0'
.
Druhý soubor pravidel popisuje analyzátor, který na vstupu rozeznává terminální symboly reprezentující celočíselné konstanty.
init_code: {print('init_code')}
terminals:
oct_int {'0'.<07>*}
dec_int {<19>.d*}
hex_int {'0'.[xX].(<09>+<af>+<AF>).(<09>+<af>+<AF>)*}
_SKIP_ {w.w*}
_END_ {'\0'}
nonterminals:
<start>
<end_check>
<program>
<item_list>
<item>
<A>
rules:
<start> -> <end_check> ->> {}
<end_check> -> <program> _END_ ->> {}
<end_check> -> _END_ ->> {}
<program> -> <item_list> ->> {}
<item_list> -> <item> <item_list> ->> {}
<item_list> -> <item> ->> {}
<item> -> <A> ->> {}
<A> -> oct_int ->> {print('constant: '..rule_body(0))}
<A> -> dec_int ->> {print('constant: '..rule_body(0))}
<A> -> hex_int ->> {print('constant: '..rule_body(0))}
Příklad řetězce akceptovaného vygenerovaným překladačem:
10
0x10 125 02
07
Výstup generovaný při spuštění překladače nad uvedeným vstupním řetězcem.
init_code
constant: 10
constant: 0x10
constant: 125
constant: 02
constant: 07
Množina terminálních symbolů byla rozšířena o reprezentace celočíselných konstant. Gramatika popisující jazyk byla rozšířena o pravidla generující seznam celočíselných konstant.
K posledním třem pravidlům byly přiřazeny sémantické kódy, které se interpretují v případě redukce podle pravidla, ke kterému jsou přiřazeny. Sémantické kódy uvedené v druhém souboru pravidel vypisují na standardní výstup informace o rozpoznaném terminálním symbolu.
V souboru je uveden i inicializační sémantický kód, který je zapsán za klíčovým slovem init_code
. Tento sémantický kód je interpretován vždy na začátku rozkladu vstupního řetězce.
Třetí soubor pravidel popisuje překladač, který v rámci svých sémantických pravidel vyhodnocuje jednoduché výrazy nad celočíselnými konstantami.
init_code: {s = {\}}
terminals:
oct_int {'0'.<07>*}
dec_int {<19>.d*}
hex_int {'0'.[xX].(<09>+<af>+<AF>).(<09>+<af>+<AF>)*}
lr_br {'('}
rr_br {')'}
plus {'+'}
minus {'-'}
asterisk {'*'}
slash {'/'}
percent {'%'}
_SKIP_ {w.w*}
_END_ {'\0'}
nonterminals:
<start>
<end_check>
<program>
<item_list>
<item>
<C>
<B>
<A>
rules:
<start> -> <end_check> ->> {}
<end_check> -> <program> _END_ ->> {}
<end_check> -> _END_ ->> {}
<program> -> <item_list> ->> {}
<item_list> -> <item> <item_list> ->> {}
<item_list> -> <item> ->> {}
<item> -> <C> ->> {print("result: "..s[#s])}
<C> -> <C> plus <B> ->> {s[#s-1] = s[#s-1] + table.remove(s)}
<C> -> <C> minus <B> ->> {s[#s-1] = s[#s-1] - table.remove(s)}
<C> -> <B> ->> {}
<B> -> <B> asterisk <A> ->> {s[#s-1] = s[#s-1] * table.remove(s)}
<B> -> <B> slash <A> ->> {s[#s-1] = s[#s-1] / table.remove(s)}
<B> -> <B> percent <A> ->> {s[#s-1] = s[#s-1] % table.remove(s)}
<B> -> <A> ->> {}
<A> -> lr_br <C> rr_br ->> {}
<A> -> oct_int ->> {table.insert(s,tonumber(rule_body(0),8))}
<A> -> dec_int ->> {table.insert(s,tonumber(rule_body(0),10))}
<A> -> hex_int ->> {table.insert(s,tonumber(rule_body(0)))}
Příklad řetězce akceptovaného vygenerovaným překladačem:
10 - 010
5*(10 + 5) - 0x10
10*3 - 10*2
Výstup generovaný při spuštění překladače nad uvedeným vstupním řetězcem.
result: 2
result: 59
result: 10
Seznam terminálních symbolů byl rozšířen o kulaté závorky a pět základních aritmetických operátorů. Gramatika popisující přijímaný jazyk byla rozšířena o pravidla popisující jednoduché aritmetické výrazy. Vyhodnocování výrazů v rámci sémantických kódů se provádí pomocí zásobníku:
s
, která reprezentuje zásobník vyhodnocovaného výrazu. Zásobník je zde zastoupen LUA tabulkou.s
.Priorita operátorů v aritmetických výrazech je určena posloupností neterminálních symbolu <A>
,<B>
a <C>
.
V posledním příkladu popíši překladač kódu odvozeného z jazyka C, který používám pro překlad C zápisu do JIT instrukcí knihovny libjit. Řetězce zmiňovaného jazyka popisují funkce, jejichž kód se generuje prostřednictvím knihovny libjit. Následují příklady dvou vstupních řetězců:
i32 fact(i32 n)
{
i32 res = 1;
while (n > 1) res *= n--;
return res;
}
i32 test(i8 *data,u32 count)
{
i64 *ptr = (i64 *)data;
i64 *ptr_end = ptr + count;
do {
*ptr = fact(*ptr);
} while(++ptr < ptr_end);
return 0;
}
Soubor popisující překladač přijímající podmnožinu jazyka C je k nalezení zde. Příklad překladače pro JIT kompilace zde uvádím spíše jen proto, aby jsem ukázal, že je možné yapgen použít pro generování skutečného překladače, a ne pouze výše uvedených hraček.
Generátor používám pro generování překladačů, které jsou aktuálně nasazené v reálných aplikacích. Zdrojové kódy je možné najít na yapgen.
V této kapitole, která nebyla součástí původního příspěvku je uvedeno pár příkladů použití programu yapgen k vygenerování překladače používaného v praxi.
Vyvinuli jsme aktivní elektroměr, jehož komunikace je popsána v normě ČSN EN 62056-21. Komunikace je rozdělená na pomyslné pakety, které mohou být částečně binární, protože kontrolní BCC znak může nabývat libovolné hodnoty.
Protože komunikace po sériové lince není rozdělena na zprávy, musí komunikující program rozpoznat jednotlivé pakety v bloku dat načtených prostřednictvím sériového rozhraní. Úloha více-méně odpovídá identifikaci terminálních symbolů ve zdrojovém řetězci.
Yapgen byl využit jako rozpoznávač těchto terminálních symbolů, a z vygenerovaného C/C++ souboru byla použita jeho část popisující lexikální analyzátor. Vygenerovaný kód se používá jako součást firmware vyvinutého elektroměru. Soubor popisující pravidla pro rozpoznání jednotlivých paketů v rámci komunikace je k nalezení zde.
Protože jsem byl mladý a zvědavý jal jsem se prostřednictvím programu yapgen vytvořit skriptovací jazyk. Tento jazyk v průběhu let evolvoval do použitelného stavu. K dnešnímu dni je využíván v reálných aplikacích nasazených na embedded zařízeních a v implementaci serverového řešení pro sběr dat a jejich vyhodnocování.
Jsem si samozřejmě vědom argumentů typu: není potřeba další skriptovací jazyk, vše je již vyřešeno a děláš zbytečnou práci. Jelikož vývoj jazyka začal ze zvědavosti, tak některé z výše uvedených argumentů postrádají smysl, ale protože se jazyk vyvinul v něco co je možné použít při řešení skutečných úloh pokusím se v některém z následujících blogů obhájit jeho místo na slunci.
Jazyk uclang je používán k řešení úloh v praxi, a v rámci jeho implementace byl použit program yapgen. Následující překladače a nástroje byly při implementaci jazyka uclang vygenerovány pomocí programu yapgen.
Uclang parser
Překladač jazyka uclang: uclang_parser.
JIT parser
Překladač funkcí zapsaných v pseudo C jazyce: jit_parser. Rozpoznávač datového typu z jeho textového popisu jit_types.
Generátor dokumentace z komentářů
Nalezení komentáře ve zdrojovém souboru: docu_extract. Zpracování jednoho komentáře: docu_comment.
JSON parser
Překladač JSON zápisů pro jejich přímý import do objektů jazyka uclang: json.
Code/decode parser
Parser pro řetězec popisující binární data, do kterých budou převedeny objekty v rámci metod Pack.code#2
a Pack.decode#1
: pack_code.
Regexp parser
Překladač pro regulární výrazy používané v programu yapgen, a také v modulu parser
jazyka uclang: regexp.
String format parser
Překladač pro rozklad formátovacího řetězce používaného při formátování řetězců: string_format.
To by měly být všechny výskyty překladačů generovaných pomocí programu yapgen vyskytující se v jazyce uclang.
Tiskni
Sdílej:
Samotný jazyk je popsán SLR(1) gramatikou, která je dostatečně silná pro popis obecného programovacího jazyka.To znamená odstranit konflikty z gramatiky? To není příliš pohodlné, ne?
Ano to je pravda je nutné vyřešit SLR(1)
konflikty vznikající v rozkladové tabulce. SLR(1)
gramatika byla zvolena jako dostačující pro řešení původní úlohy v rámci které program yapgen vznikl.
Od té doby jsem jej párkrát využil při implementaci úkolů se kterými jsem se setkal v praxi.
BTW: když píšeš „C/C++“, co to znamená? Umí to generovat zdroják v obou jazycích nebo to generuje C, které se dá přeložit i kompilátorem pro C++?
Třetí možnost je správně: generuje to zdrojový soubor pro kompilátor C++, kde z C++ jsou používány následující funkce, které nebyly součástí jazyka C: metody ve strukturách (třídách), reference, datový typ bool, výčtový typ, a možná některé další.
Ale to není tak důležité, protože v případě potřeby je možné vygenerovat výsledný zdrojový soubor v libovolném jazyce (dostatečně silném). Tvrdá práce: generování DFA (Deterministický Konečný Automat) a LALR tabulky je již hotová a generování C/C++ souboru je v podstatě jen export algoritmů na těmito dvěma strukturami.
Příklady vygenerovaných .cc
souborů odpovídajících překladačům popsaným v kapitolce "Příklad tvorby překladače" je uveden na: 1, 2, 3.
Uvedené soubory obsahují dvě základní funkce recognize_terminal
a parser_parse_source_string
, které dělají všechnu práci.
To je mi dost divné. Sice nebyl nijak moc kvalitní, v podstatě o ničem, ale zápisky Miriam tu taky zůstávají (byť třeba ne na titulní stránce). A protože pod ním byly komentáře, nemohl ho běžný uživatel (jako autor) smazat. Leoš asi není běžný uživatel…
Ad patička:
Výhodná nabídka; Romantická kvelbená kóje v Kyjově (se záchodem!)
To je včetně toho pozemku nad tím nebo to je cena jen za díru v zemi a ty cihly? Kupuješ? Podle kabelů to vypadá, že elektřina tam je. Šlo by tam udělat datacentrum a menší kancelář. Na zahradě by se postavil stožár a bylo by
Kupuješ?Spíš jsem chtěl morálně podpořit majitele v jeho originálně snaze prodat kvelbenou kóji.
Nepochybně máš pravdu, bohužel jsem si zvykl používat základní datové typy tak, že počítám s platností následujících tvrzení:
sizeof(void *) == sizeof(long unsigned)
sizeof(int) == 4
sizeof(unsigned) == 4
sizeof(char) == 1
sizeof(long long int) == 8
sizeof(float) == 4
sizeof(double) == 8
A až narazím na platformu, na které to nebude platit, tak si budu trhat vlasy a vzpomenu si na tebe.
sizeof(void *) == sizeof(long unsigned)Tohle třeba neplatí na Windows, teda né že mi Windows není ukradenej, ale stejně. Pro nějaké takové prasárničky typu pointer na číslo existuje (u)intptr_t.
sizeof(unsigned) == 4S tímhle nepochodíš na různých mikroprocesorech. Na těch "hloupých" typu atmega, to je např. 2.
sizeof(char) == 1Tohle platí vždycky. Ať už má char 8 nebo 256 bitů, pořád to 1 byte, protože byte je totéž co char.
Je neco spatnyho na tom, ze vubec nepouzivam size_t a vsechny velikosti mam pevne omezene napr. uint8_t nebo int64_t a naprosto ignoruju architekturu? Mam porad trochu problem s endianness, ale jinak se muzu spolehnout, ze bude kod fungovat vsude stejne (hlavne na 8bit AVR).
Uplne jsem ale prestal pouzivat packed struktury, to je zlo.
A co kdyz nemam vubec problem s minimalni velikosti, ale potrebuju presne urcite nasobky 8bitu? Napr. kdyz zpracovavam HW udaje a potrebuju 1:1 bitovou reprezentaci. Nebo treba kdyz rozebiram a skladam sitove pakety.
Taky moc se mi nelibi, ze bych vsude psal uint_least32_t misto uint32_t, je to delsi zapis a neprinasi funkcionalitu ani prehlednost.
A co kdyz nemam vubec problem s minimalni velikosti, ale potrebuju presne urcite nasobky 8bitu? Napr. kdyz zpracovavam HW udaje a potrebuju 1:1 bitovou reprezentaci. Nebo treba kdyz rozebiram a skladam sitove pakety.Na platformě, kde neexistuje 8bit typ toho nedocílíte a musíte použít typ větší (a třeba z něho použít jen 8 bitů, to je na vás) a na platformě, kde existuje, tak ho použijete. A když ho vyžadujete, tak není problém otestovat, jestli je nadefinováno nějaké
UINTN_MAX
makro a kydnout tam #error
s vysvětlením proč a co, než nechat kompilaci selhat na nějaké chybě, která třeba nic neřekne potenciálnímu "kompilovači" vašeho kódu. A jestli se vám zdají jména dlouhé, tak použijte kratší, jazyk k tomu poskytuje prostředky (typedef, using).
Na platformě, kde neexistuje 8bit typ toho nedocílíte a musíte použít typ větší (a třeba z něho použít jen 8 bitů, to je na vás) a na platformě, kde existuje, tak ho použijete.Toto je naprosto absurdni situace, ktera by mohla nastat v drevnich dobach pocitacu typu PDP-XY, kdy jeste nebyl byte obecne prijatou jednotkou pro ulozeni informace a velikost slova byla definovana moznostmi hardwaru. Napr. architektura SPARC nebo treba JVM pracuji interne s 32bitovyma hodnotama a jen pri praci s pameti vyuzivaji typy s mensim rozsahem... a nikomu to vetsi problemy necini, protoze prekladac se o to postara sam.
kdybych měl čas
Čemu se teď vlastně věnuješ? Nechceš se někdy rozepsat v blogu?
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.