Obsah
1. Programovací jazyk C3: reakce na chyby, odložení vykonání příkazů
2. Jak reprezentovat chybějící hodnotu nebo informaci o chybě?
3. Rozšíření datových typů: Optional
4. Přístup k hodnotě typu Optional s detekcí chyby
5. Uživatelsky definovaný typ s informací o chybě
6. Vynucení přístupu k hodnotě operátorem !!
7. Chování operátoru !! v případě, že je namísto hodnoty uložena informace o chybě
8. Praktické použití: výpočet faktoriálu bez vracení informace o chybě
9. Výpočet faktoriálu s detekcí nekorektního vstupu
10. Pokus o přímý přístup k vrácené hodnotě operátorem !!
12. Základní porovnání konstrukce defer v jazycích Go a C3
13. Proč byla konstrukce defer do jazyka C3 přidána?
14. Pořadí volání příkazů zaregistrovaných s využitím příkazu defer
15. Sémantika defer v jazyce Go
16. Sémantika defer v jazyce C3
18. Funkce volané na začátku a konci procesu
19. Repositář s demonstračními příklady
1. Programovací jazyk C3: reakce na chyby, odložení vykonání příkazů
V již páté části seriálu o programovacím jazyce C3 se budeme zabývat velmi důležitým tématem, který byl do jisté míry v původním jazyce C poněkud odsunut do pozadí. Jedná se o způsoby, jakými může aplikace reagovat na nějaké chybové hodnoty resp. na chybějící hodnoty atd. S touto problematikou souvisí i rozšíření typového systému programovacího jazyka C3 o typ Optional. Ve skutečnosti se nejedná o žádnou žhavou novinku, protože typ Optional vznikl jako varianta typů Option a Result známých z dalších programovacích jazyků.
Popíšeme si taktéž programovou konstrukci, kterou lze použít pro odložené vykonání nějakých příkazů. Tato konstrukce se zapisuje klíčovým slovem defer, jehož sémantika je však v jazyce C3 odlišná, než například v programovacím jazyce Go (stejný zápis povede k odlišnému způsobu vykonání bloků defer).
2. Jak reprezentovat chybějící hodnotu nebo informaci o chybě?
V prakticky dennodenní programátorské praxi se setkáme s problematikou reprezentace chybějících hodnot nebo informací o chybě. Příkladem může být funkce, která buď vrací výsledek nějakého výpočtu nebo chybu. V různých jazycích se setkáme s několika způsoby, jak toho dosáhnout:
- Namísto vypočtené hodnoty se vrací „magická“ hodnota stejného typu
- Vrací se buď ukazatel nebo Null/None
- Vyhazuje se výjimka (odlišné řízení toku programu)
- Vrací se dvojice (hodnota, nil) nebo (cokoli, chyba)
- Používají se specializované typy Option, Maybe, Result atd.
V jazyku C3 se setkáme s posledním popsaným způsobem. Jedná se o způsob, který je převzatý z Haskellu, OCamlu či jazyka F#. Příkladem může být jazyk F# s typem Option:
type Option<'a> = | Some of 'a | None
Vidíme, že se ve skutečnosti vlastně jedná o výčtový typ s pouhými dvěma explicitně zapsanými (a překladači známými) hodnotami None a Some, přičemž Some „obaluje“ vlastní hodnotu typu 'a (alfa), se kterou chceme pracovat. Současně se tedy – zcela podle očekávání – jedná o generický datový typ, což v praxi znamená, že volitelná hodnota může být jakéhokoli typu (ovšem samozřejmě hlídaného překladačem již v době překladu zdrojového kódu).
Jen pro zajímavost se podívejme, jak je typ Option definovaný v Rustu:
enum Option<T> {
None,
Some(T),
}
V mnoha případech však nemusí být použití datového typu Option tím nejlepším řešením. Pro příklad nemusíme chodit daleko – předpokládejme, že budeme chtít, aby námi definovaná funkce pro dělení celých čísel vracela v případě pokusu o dělení nulou chybové hlášení a nikoli nicneříkající hodnotu None. K tomuto účelu se v programovacím jazyku F# používá datový typ nazvaný příhodně Result. Tento datový typ se podobá již popsanému typu Option, ovšem s tím rozdílem, že obaluje buď výsledek (třeba návratovou hodnotu volané funkce) nebo informaci o chybě. Deklarace struktury Result vypadá následovně:
type Result<'T,'TError> =
| Ok of ResultValue:'T
| Error of ErrorValue:'TError
Jen pro zajímavost a doplnění se podívejme, jak je tomu v Rustu:
enum Result<T, E> {
Ok(T),
Err(E),
}
V programovacím jazyku C3 nalezneme právě obdobu typu Result, která se zde však označuje termínem Optional (což může být zpočátku maličko matoucí).
3. Rozšíření datových typů: Optional
Nejprve si ukažme triviální příklad, ve kterém je definována běžná lokální proměnná typu int, je provedena inicializace této proměnné a následně je hodnota proměnné vypsána na standardní výstup:
module error_handling;
import std::io;
fn void main()
{
int i = 42;
io::printf("i=%d\n", i);
}
Výsledek bude pochopitelně následující:
i=42
V případě, že budeme chtít pracovat s potenciálně neuvedenými nebo neznámými hodnotami (tedy Optional) postačuje definici datového typu doplnit o otazník – viz podtrženou část kódu:
module error_handling;
import std::io;
fn void main()
{
int? i = 42;
io::printf("i=%d\n", i);
}
S takto definovanou proměnnou i se ovšem pracuje odlišným způsobem, na což nás upozorní překladač:
4: fn void main()
5: {
6: int? i = 42;
7: io::printf("i=%d\n", i);
^^^^^^^^^^^^^^^^^^^^^^^
(/home/ptisnovs/src/c3-examples/c3-errors-handling/02_optional_value.c3:7:5)
Error: The result of this call is optional due to its argument(s). This
optional result may not be implicitly discarded. Please assign it to a
variable, ignore it with '(void)', rethrow with '!' or panic with '!!'.
Z chybového hlášení překladače je patrné, že se k proměnné i musíme chovat jinak, než k běžné proměnné typu int. Jak bude zpracování její hodnoty vypadat, je ukázáno v navazující kapitole.
4. Přístup k hodnotě typu Optional s detekcí chyby
Korektní způsob přístupu (čtení) hodnoty, která je typu Optional, vyžaduje, aby se otestovaly obě možnosti, které mohou nastat – tj. jak situace, kdy proměnná obsahuje korektní hodnotu, tak i situace, kdy hodnotu neobsahuje popř. je namísto běžné hodnoty v proměnné uložena informace o chybě. Typicky se v této situaci setkáme s využitím rozvětvení typu if-else, které může vypadat následovně:
module error_handling;
import std::io;
fn void main()
{
int? i = 42;
if (catch excuse = i)
{
io::printfn("Fault: %s", excuse);
} else {
io::printf("i=%d\n", i);
}
}
Překladač zjistí, že se korektně testují oba možné stavy a dovolí zdrojový kód přeložit (a spustit):
i=42
V případě, že se v první větvi vyskakuje z celé funkce nebo bloku (break, continue, return), není nutné zapisovat větev else, neboť překladač opět dokáže zjistit, že v podmínce testujeme potenciální chybový stav a navíc se z větve if řízení programu vrací zpět do volající funkce. To znamená, že i následující zápis je z pohledu překladače plně korektní a tudíž i přeložitelný:
module error_handling;
import std::io;
fn void main()
{
int? i = 42;
if (catch excuse = i)
{
io::printfn("Fault: %s", excuse);
return;
}
io::printf("i=%d\n", i);
}
5. Uživatelsky definovaný typ s informací o chybě
Připomeňme si, že proměnná typu Optional může obsahovat buď přímo hodnotu (například 42) nebo informaci o chybě. Jak však taková informace o chybě vypadá? Jedná se o samostatnou hodnotu, kterou je možné definovat s využitím specializovaného klíčového slova faultdef. Definice může v tom nejjednodušším případě vypadat následovně:
faultdef MY_ERROR;
Tuto hodnotu můžeme přiřadit do proměnné typu Optional. Povšimněte si otazníku na konci přiřazení:
module error_handling;
import std::io;
faultdef MY_ERROR;
fn void main()
{
int? i = MY_ERROR?;
if (catch excuse = i)
{
io::printfn("Fault: %s", excuse);
} else {
io::printf("i=%d\n", i);
}
}
Výsledek získaný po překladu a spuštění tohoto příkladu:
Fault: error_handling::MY_ERROR
Samozřejmě lze použít i alternativní zápis bez větve else:
module error_handling;
import std::io;
faultdef MY_ERROR;
fn void main()
{
int? i = MY_ERROR?;
if (catch excuse = i)
{
io::printfn("Fault: %s", excuse);
return;
}
io::printf("i=%d\n", i);
}
Výsledky:
Fault: error_handling::MY_ERROR
6. Vynucení přístupu k hodnotě operátorem !!
Programovací jazyk C3 vývojářům nabízí speciální operátor reprezentovaný dvojicí znaků !!, který je zapisovaný za jméno proměnné nebo výrazu typu Optional. Tento operátor se pokouší provádět „unwrapping“ hodnoty, která je do Optional zabalena – počítá tedy optimisticky s tím, že proměnná skutečně hodnotu obsahuje (a nikoli chybu). Pokud proměnná/výraz skutečně obsahuje běžnou hodnotu, bude výsledkem tato hodnota, což si ostatně velmi snadno ověříme v dalším demonstračním příkladu:
module error_handling;
import std::io;
faultdef MY_ERROR;
fn void main()
{
int? i = 42;
io::printf("i=%d\n", i!!);
}
Vzhledem k tomu, že proměnná i (typu Optional) obsahuje běžnou hodnotu 42, je tato hodnota výrazem i!! vyhodnocena a vrácena:
i=42
7. Chování operátoru !! v případě, že je namísto hodnoty uložena informace o chybě
Pokud naopak do proměnné i namísto běžné hodnoty uložíme informaci o chybě a budeme chtít provést operaci typu unwrap nad touto proměnnou, musí runtime programovacího jazyka C3 na tuto skutečnost nějakým vhodným způsobem zareagovat – zde konkrétně tak, že dojde k běhové chybě a program zhavaruje:
module error_handling;
import std::io;
faultdef MY_ERROR;
fn void main()
{
int? i = MY_ERROR?;
io::printf("i=%d\n", i!!);
}
Z výpisu, který se zobrazí po pádu programu, lze dohledat, k jaké chybě došlo i v jakém místě programu musíme hledat příslušný programový řádek:
ERROR: 'Unexpected fault 'error_handling::MY_ERROR' was unwrapped!' in std.core.builtin.default_panic (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:175) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling] in std.core.builtin.panicf (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:231) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling] in error_handling.main (/home/ptisnovs/src/c3-examples/c3-errors-handling/08_force_unwrap.c3:10) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling] in @main_to_void_main (/home/ptisnovs/xy/c3c/build/lib/std/core/private/main_stub.c3:18) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling] [inline] in main (/home/ptisnovs/src/c3-examples/c3-errors-handling/08_force_unwrap.c3:6) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling] in __libc_start_call_main (source unavailable) [/lib64/libc.so.6] in __libc_start_main_alias_2 (source unavailable) [/lib64/libc.so.6] in _start (source unavailable) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling]
8. Praktické použití: výpočet faktoriálu bez vracení informace o chybě
Podívejme se nyní na praktické použití datového typu, který může nést informaci o hodnotě nebo naopak informaci o chybě. Typickým příkladem je výpočet faktoriálu, přičemž samotný faktoriál je definován pouze pro kladné hodnoty. Otázkou tedy je, „co“ se má vrátit v případě, že budeme chtít počítat faktoriál pro zápornou hodnotu (to by ještě bylo možné obejít vhodným typem parametru) nebo pro příliš vysokou hodnotu. První řešení spočívá v tom, že budeme nekorektní parametr do jisté míry ignorovat a vrátíme nějakou „magickou“ hodnotu (nebo test na chybný vstup vůbec neprovedeme):
module error_handling;
import std::io;
fn int factorial(int n)
{
int result = 1;
for (int i = 1; i <= n; ++i)
{
result *= i;
}
return result;
}
fn void main()
{
for (int n = 10; n >= -10; n--)
{
io::printf("%d! = %d\n", n, factorial(n));
}
}
Výpočet faktoriálu pro vstupní hodnoty v rozmezí 10..-10 dopadne v tomto případě následovně:
10! = 3628800 9! = 362880 8! = 40320 7! = 5040 6! = 720 5! = 120 4! = 24 3! = 6 2! = 2 1! = 1 0! = 1 -1! = 1 -2! = 1 -3! = 1 -4! = 1 -5! = 1 -6! = 1 -7! = 1 -8! = 1 -9! = 1 -10! = 1
9. Výpočet faktoriálu s detekcí nekorektního vstupu
Výpočet faktoriálu ovšem můžeme upravit tak, aby se detekovaly záporné parametry a pokud je opravdu záporný parametr předán, vrátí se chybová hodnota NEGATIVE_INPUT. Povšimněte si, že návratovou hodnotou funkce pro výpočet faktoriálu, není typ int ale int?:
module error_handling;
import std::io;
faultdef NEGATIVE_INPUT;
fn int? factorial(int n)
{
if (n < 0) {
return NEGATIVE_INPUT?;
}
int result = 1;
for (int i = 1; i <= n; ++i)
{
result *= i;
}
return result;
}
fn void main()
{
for (int n = 10; n >= -10; n--)
{
int? result = factorial(n);
if (catch excuse = result)
{
io::printfn("Fault: %s: %d", excuse, n);
} else {
io::printf("%d!=%d\n", n, result);
}
}
}
V programu (main) korektně reagujeme na chybové hodnoty vrácené z funkce factorial:
10!=3628800 9!=362880 8!=40320 7!=5040 6!=720 5!=120 4!=24 3!=6 2!=2 1!=1 0!=1 Fault: error_handling::NEGATIVE_INPUT: -1 Fault: error_handling::NEGATIVE_INPUT: -2 Fault: error_handling::NEGATIVE_INPUT: -3 Fault: error_handling::NEGATIVE_INPUT: -4 Fault: error_handling::NEGATIVE_INPUT: -5 Fault: error_handling::NEGATIVE_INPUT: -6 Fault: error_handling::NEGATIVE_INPUT: -7 Fault: error_handling::NEGATIVE_INPUT: -8 Fault: error_handling::NEGATIVE_INPUT: -9 Fault: error_handling::NEGATIVE_INPUT: -10
10. Pokus o přímý přístup k vrácené hodnotě operátorem !!
Ještě si pro úplnost ukažme, jak dopadne pokus o unwrapping vrácené hodnoty bez testování, zda nebyla vrácena chyba:
module error_handling;
import std::io;
faultdef NEGATIVE_INPUT;
fn int? factorial(int n)
{
if (n < 0) {
return NEGATIVE_INPUT?;
}
int result = 1;
for (int i = 1; i <= n; ++i)
{
result *= i;
}
return result;
}
fn void main()
{
for (int n = 10; n >= -10; n--)
{
int result = factorial(n)!!;
io::printf("%d!=%d\n", n, result);
}
}
Takto upravený program po svém překladu a spuštění podle očekávání zhavaruje:
ERROR: 'Unexpected fault 'error_handling::NEGATIVE_INPUT' was unwrapped!' in std.core.builtin.default_panic (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:175) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling] in std.core.builtin.panicf (/home/ptisnovs/xy/c3c/build/lib/std/core/builtin.c3:231) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling] in error_handling.main (/home/ptisnovs/src/c3-examples/c3-errors-handling/11_factorial.c3:23) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling] in @main_to_void_main (/home/ptisnovs/xy/c3c/build/lib/std/core/private/main_stub.c3:18) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling] [inline] in main (/home/ptisnovs/src/c3-examples/c3-errors-handling/11_factorial.c3:19) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling] in __libc_start_call_main (source unavailable) [/lib64/libc.so.6] in __libc_start_main_alias_2 (source unavailable) [/lib64/libc.so.6] in _start (source unavailable) [/home/ptisnovs/src/c3-examples/c3-errors-handling/error_handling] Program interrupted by signal 4.
11. Konstrukce defer
Další jazykovou konstrukcí, která slouží k ovlivnění toku programu (control flow), je příkaz (resp. přesněji řečeno programová konstrukce) reprezentovaná klíčovým slovem defer. Touto konstrukcí je možné zaregistrovat požadavek na volání nějakého uceleného programového kódu (tj. typicky běžné funkce či anonymní funkce) do zásobníku, jehož obsah se vykoná při odchodu z toho bloku, ve kterém je příkaz defer použit. Z tohoto důvodu se tato technika nazývá odložené volání funkce nebo anonymní funkce. Předchozí dvě věty sice mohou znít poměrně složitě, zvláště pokud jste se s tímto konceptem dříve nesetkali, ale samotné použití konstrukce defer je ve skutečnosti poměrně přímočaré, jak si to ostatně ukážeme v demonstračních příkladech.
12. Základní porovnání konstrukce defer v jazycích Go a C3
Konstrukce defer je velmi užitečnou součástí programovacího jazyka Go zmíněného v předchozí kapitole. Ukažme si tedy alespoň ve stručnosti, jak se tato konstrukce v Go používá:
package main
import "fmt"
func main() {
defer fmt.Println("Too late")
fmt.Println("Hello")
}
Z výše zobrazeného zdrojového kódu tohoto příkladu je patrné, že ve funkci main je použit příkaz defer, kterým požadujeme, aby se při ukončování funkce main zavolala jiná funkce, konkrétně funkce nazvaná fmt.Println (které se v tomto případě předá jediný parametr). Tím, že je před volání funkce fmt.Println zapsáno klíčové slovo defer, dojde k onomu odložení volání – runtime systém si jen zapamatuje, že má tuto funkci zavolat později.
Z chování programu je parné, že došlo k odloženému volání funkce:
Hello Too late
V programovacím jazyku C3 se konstrukce defer zapisuje prakticky stejným způsobem, jako tomu bylo v Go. Ostatně neuškodí krátké porovnání:
module error_handling;
import std::io;
fn void main()
{
defer io::printn("Too late");
io::printn("Hello");
}
Opět se podívejme, jaké zprávy (a v jakém pořadí) se vypíšou po překladu a spuštění tohoto demonstračního příkladu:
Hello Too late
13. Proč byla konstrukce defer do jazyka C3 přidána?
Nyní tedy víme (prozatím alespoň ve stručnosti), jakým způsobem se konstrukce defer zapisuje a používá, ale musíme si samozřejmě vysvětlit, proč vůbec v jazyku C3 existuje. V mnoha programech je totiž nutné nějakým způsobem a za jakýchkoli okolností uzavírat určité prostředky (resources). Může se jednat o připojení k databázi, otevřené připojení ke klientovi, otevřený soubor atd. A právě toto uzavírání prostředků lze poměrně elegantním způsobem realizovat v bloku či v několika blocích defer. Jedná se vlastně o zobecnění programového bloku typu finally používaného v programové konstrukci try-catch-finally v některých jiných programovacích jazycích.
14. Pořadí volání příkazů zaregistrovaných s využitím příkazu defer
Nic nám pochopitelně nebrání použít v rámci jedné funkce (nebo jakéhokoli jiného bloku) hned několik příkazů defer. Zajímavé bude ovšem zjistit, jak se bude tato funkce chovat při spuštění programu, tj. v runtime. Můžeme si to snadno otestovat, protože již víme, že v bloku defer je možné funkci (či obecně kódu zde zapsaném) předat libovolné argumenty. To mj. znamená, že můžeme defer použít například v programové smyčce atd. Ovšem my se obejdeme bez smyčky – pouze zaregistrujeme tři odložené příkazy:
module error_handling;
import std::io;
fn void main()
{
defer io::printn("Defer #1");
defer io::printn("Defer #2");
defer io::printn("Defer #3");
io::printn("Hello");
}
Ze zobrazených výsledků je patrné, že se odložené příkazy volají v opačném pořadí, než v jakém byly zaregistrovány. Je tomu tak z toho důvodu, že tyto příkazy jsou uloženy do zásobníku, tedy do struktury typu LIFO (Last In First Out):
Hello Defer #3 Defer #2 Defer #1
15. Sémantika defer v jazyce Go
V programovacím jazyce Go se odložené příkazy vykonají (v opačném pořadí) vždy při opouštění funkce nebo metody, a to nezávisle na tom, zda byly zapsány v nějakém (zanořeném) bloku kódu. Ukažme si to na jednoduchém demonstračním příkladu se třemi bloky defer, které jsou definovány na úrovni funkce main, v jednom zanořeném bloku a nakonec v bloku zanořeném do nějakého jiného bloku:
package main
import "fmt"
func main() {
fmt.Println("Hello")
defer fmt.Println("Defer #1")
{
defer fmt.Println("Defer #2")
{
defer fmt.Println("Defer #3")
}
}
fmt.Println("Bye")
}
Pořadí zobrazení zpráv po spuštění tohoto příkladu:
Hello Bye Defer #3 Defer #2 Defer #1
Z tohoto výpisu je patrné, že skutečně nezáleží na tom, zda je defer použit přímo v dané funkci nebo v nějakém vloženém bloku.
16. Sémantika defer v jazyce C3
Programovací jazyk C3 se chová odlišně od jazyka Go, protože odložené příkazy (defer) jsou zavolány nikoli na konci funkce, ale na konci každého bloku. To tedy znamená, že i když je syntaxe zápisu bloků defer v obou jazycích totožná, sémantika (chování) v runtime může být zcela odlišné. Pokusme se přepsat příklad z předchozí kapitoly z Go do C3:
module error_handling;
import std::io;
fn void main()
{
io::printn("Hello");
defer io::printn("Defer #1");
{
defer io::printn("Defer #2");
{
defer io::printn("Defer #3");
}
}
io::printn("Bye");
}
Nyní je ze zobrazených zpráv patrné, že třetí a druhý odložený příkaz se vykonají ještě před výpisem zprávy Bye a pouze první odložený příkaz je skutečně zavolán při ukončování funkce (tělo funkce je samo o sobě blokem):
Hello Defer #3 Defer #2 Bye Defer #1
17. Konstrukce defer a return
Ještě si ovšem musíme ukázat jednu kombinaci jazykových konstrukcí a to konkrétně kombinaci defer a return. Konstrukce return ukončuje běh funkce a tudíž se vykonají i odložené příkazy, ovšem dokážeme tímto způsobem například modifikovat návratovou hodnotu funkce? Vyzkoušejme si to:
module error_handling;
import std::io;
fn int function()
{
int a = 10;
io::printn("Hello");
defer a++;
io::printn("Bye");
return a;
}
fn void main()
{
io::printn(function());
}
Z výpisu je patrné, že takto jednoduché to nebude:
Hello Bye 10
18. Funkce volané na začátku a konci procesu
Konstrukce defer umožňuje modifikovat pořadí provádění kódu na úrovni jednotlivých příkazů (statement). Programovací jazyk C3 kromě toho podporuje i označení vybraných funkcí pomocí @init a @finalizer. Funkce označená jako @init bude provedena na začátku procesu a pochopitelně funkce označená @finalizer na konci procesu. Pokusme se tedy jednotlivé způsoby zkombinovat do jednoho (umělého) příkladu:
import std::io;
fn void main()
{
defer io::printf("world");
io::printf(", ");
}
fn void hello() @init
{
io::printf("Hello");
}
fn void world() @finalizer
{
io::printn("!");
}
Tento demonstrační příklad by měl po svém spuštění vypsat klasickou zprávu:
Hello, world!
19. Repositář s demonstračními příklady
Demonstrační příklady vytvořené pro nejnovější verzi programovacího jazyka C3 byly uloženy do repositáře dostupného na adrese https://github.com/tisnik/c3-examples. Následují odkazy na jednotlivé příklady (či jejich nedokončené části).
Demonstrační příklady z prvního článku o programovacím jazyku C3:
| # | Příklad | Stručný popis | Adresa |
|---|---|---|---|
| 1 | factorial.c3 | realizace výpočtu faktoriálu | https://github.com/tisnik/c3-examples/blob/master/introduction/factorial.c3 |
| 2 | factorial_macro.c3 | výpočet faktoriálu konkrétní hodnoty implementovaný formou makra | https://github.com/tisnik/c3-examples/blob/master/introduction/factorial_macro.c3 |
| 3 | swap_macro.c3 | makro realizující prohození dvou hodnot | https://github.com/tisnik/c3-examples/blob/master/introduction/swap_macro.c3 |
| 4 | renderer.c | výpočet a vykreslení Juliovy množiny implementovaný v jazyku C | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer.c |
| 5 | renderer_v1.c3 | definice datové struktury s rozměry rastrového obrázku a skeleton všech funkcí | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v1.c3 |
| 6 | renderer_v2.c3 | anotace parametrů funkcí typu ukazatel (pointer) | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v2.c3 |
| 7 | renderer_v3.c3 | statická kontrola, zda se nepředávají neinicializované ukazatele | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v3.c3 |
| 8 | renderer_v4.c3 | runtime kontrola, zda se nepředávají neinicializované ukazatele | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v4.c3 |
| 9 | renderer_v5.c3 | první (nekorektní) varianta funkce pro inicializaci barvové palety | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v5.c3 |
| 10 | renderer_v6.c3 | druhá (korektní) varianta funkce pro inicializaci barvové palety | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v6.c3 |
| 11 | renderer_v7.c3 | volání knihovní I/O funkce a volání nativní céčkovské funkce | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v7.c3 |
| 12 | renderer_v8.c3 | plně funkční program pro výpočet a vykreslení Juliovy množiny | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v8.c3 |
Demonstrační příklady ze druhého článku o jazyku C3:
| # | Příklad | Stručný popis | Adresa |
|---|---|---|---|
| 13 | 01_just_main.c3 | struktura nejjednoduššího programu obsahujícího pouze prázdnou funkci main | https://github.com/tisnik/c3-examples/blob/master/c3-basics/01_just_main.c3 |
| 14 | 02_module_name.c3 | struktura programu s uvedeným plným jménem modulu | https://github.com/tisnik/c3-examples/blob/master/c3-basics/02_module_name.c3 |
| 15 | 03_hello_world.c3 | klasický program typu „Hello, world!“ napsaný v jazyku C3 | https://github.com/tisnik/c3-examples/blob/master/c3-basics/03_hello_world.c3 |
| 16 | 04_exit_value.c3 | ukončení procesu s předáním návratového kódu zpět volajícímu programu | https://github.com/tisnik/c3-examples/blob/master/c3-basics/04_exit_value.c3 |
| 17 | 05_c_function.c3 | zavolání funkce definované v knihovně programovacího jazyka C | https://github.com/tisnik/c3-examples/blob/master/c3-basics/05_c_function.c3 |
| 18 | 06_bool_type.c3 | definice proměnných typu pravdivostní hodnota (bool) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/06_bool_type.c3 |
| 19 | 07_int_to_bool.c3 | implicitní převod hodnoty typu int na pravdivostní hodnotu (nekorektní řešení) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/07_int_to_bool.c3 |
| 20 | 08_int_to_bool.c3 | explicitní převod hodnoty typu int na pravdivostní hodnotu (korektní řešení) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/08_int_to_bool.c3 |
| 21 | 09_int_to_bool.c3 | explicitní převod hodnoty typu int na pravdivostní hodnotu (nekorektní řešení) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/09_int_to_bool.c3 |
| 22 | 10_bool_sizeof.c3 | zjištění velikosti paměti obsazené hodnotou typu bool | https://github.com/tisnik/c3-examples/blob/master/c3-basics/10_bool_sizeof.c3 |
| 23 | 11_int_types.c3 | definice proměnných typu celé číslo se znaménkem s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/11_int_types.c3 |
| 24 | 12_uint_types.c3 | definice proměnných typu celé číslo bez znaménka s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/12_uint_types.c3 |
| 25 | 13_no_suffixes.c3 | celočíselné konstanty bez uvedení suffixu (bitové šířky) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/13_no_suffixes.c3 |
| 26 | 14_suffixes.c3 | celočíselné konstanty s uvedením sufficu (bitové šířky) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/14_suffixes.c3 |
| 27 | 15_int_sizeof.c3 | zjištění velikosti paměti obsazené celočíselnými hodnotami se znaménkem | https://github.com/tisnik/c3-examples/blob/master/c3-basics/15_int_sizeof.c3 |
| 28 | 16_uint_sizeof.c3 | zjištění velikosti paměti obsazené celočíselnými hodnotami bez znaménka | https://github.com/tisnik/c3-examples/blob/master/c3-basics/16_uint_sizeof.c3 |
| 29 | 17_int_conversions.c3 | korektní převody mezi celočíselnými hodnotami s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/17_int_conversions.c3 |
| 30 | 18_int_conversions.c3 | nekorektní převody mezi celočíselnými hodnotami s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/18_int_conversions.c3 |
| 31 | 19_int_conversions.c3 | explicitní převody a přetečení hodnot | https://github.com/tisnik/c3-examples/blob/master/c3-basics/19_int_conversions.c3 |
| 32 | 20_float_types.c3 | definice proměnných typu numerická hodnota s plovoucí řádovou čárkou (tečkou) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/20_float_types.c3 |
| 33 | 21_vector_type.c3 | definice vektoru obsahujícího celočíselné hodnoty | https://github.com/tisnik/c3-examples/blob/master/c3-basics/21_vector_type.c3 |
| 34 | 22_vector_operations.c3 | základní operace s celými vektory | https://github.com/tisnik/c3-examples/blob/master/c3-basics/22_vector_operations.c3 |
| 35 | 23_vector_sizes.c3 | zjištění a tisk velikosti vektorů (různé datové typy prvků vektorů, shodná délka) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/23_vector_sizes.c3 |
| 36 | 24_vector_sizes.c3 | zjištění a tisk velikosti vektorů (stejné datové typy prvků vektorů, odlišná délka) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/24_vector_sizes.c3 |
Demonstrační příklady použité ve třetím článku o jazyku C3:
Demonstrační příklady z předchozího článku o jazyku C3:
| # | Příklad | Stručný popis | Adresa |
|---|---|---|---|
| 57 | 01_program_stub.c3 | struktura programu s uvedeným plným jménem modulu | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/01_program_stub.c3 |
| 58 | 02_if.c3 | nejjednodušší forma rozvětvení založené na konstrukci if | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/02_if.c3 |
| 59 | 03_if_else.c3 | plné rozvětvení realizované konstrukcí if-else | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/03_if_else.c3 |
| 60 | 04_improper_if.c3 | nekorektní způsob zápisu programové konstrukce if-else (porovnání s jazykem C) | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/04_improper_if.c3 |
| 61 | 05_improper_if.c3 | nekorektní způsob zápisu programové konstrukce if-else (porovnání s jazykem C) | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/05_improper_if.c3 |
| 62 | 06_if_else_if.c3 | složitější rozvětvení založené na programové konstrukci if-else if-else | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/06_if_else_if.c3 |
| 63 | 07_switch_basic.c3 | základní forma vícenásobného rozvětvení založeného na konstrukci switch-case | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/07_switch_basic.c3 |
| 64 | 08_switch_basic.c3 | větší množství podmínek a programová konstrukce switch-case | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/08_switch_basic.c3 |
| 65 | 09_switch_condition.c3 | podmínky zapsané ve větvích programové konstrukci switch-case vyhodnocované v čase běhu procesu | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/09_switch_condition.c3 |
| 66 | 10_switch_true.c3 | konstrukce switch-case bez uvedeného výrazu za klíčovým slovem switch | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/10_switch_true.c3 |
| 67 | 11_switch_break.c3 | zápis prázdné větve default v programové konstrukci switch-case | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/11_switch_break.c3 |
| 68 | 12_switch_nextcase.c3 | pokračování ve vykonávání konstrukce switch-case vynucené klíčovým slovem nextcase | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/12_switch_nextcase.c3 |
| 69 | 13_for_loop.c3 | základní forma programové smyčky realizované klíčovým slovem for | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/13_for_loop.c3 |
| 70 | 14_foreach_loop.c3 | základní forma programové smyčky typu for-each | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/14_foreach_loop.c3 |
| 71 | 15_foreach_loop.c3 | programová smyčka for-each vracející index prvku i hodnotu prvku | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/15_foreach_loop.c3 |
| 72 | 16_foreach_loop.c3 | modifikace obsahu pole v programové smyčce for-each | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/16_foreach_loop.c3 |
| 73 | 17_foreach_loop.c3 | pokus o modifikaci obsahu procházeného pole | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/17_foreach_loop.c3 |
| 74 | 18_foreach_loop.c3 | modifikace procházeného pole přes ukazatel na prvek | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/18_foreach_loop.c3 |
| 75 | 19_foreach_r_loop.c3 | programová smyčka for-each, ve které se sekvencí prochází v opačném pořadí | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/19_foreach_r_loop.c3 |
| 76 | 20_while_loop.c3 | základní forma programové smyčky typu while | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/20_while_loop.c3 |
| 77 | 21_while_loop2.c3 | programová smyčka typu while s konstrukcí break | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/21_while_loop2.c3 |
| 78 | 22_nested_loops.c3 | realizace vnořených programových smyček | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/22_nested_loops.c3 |
| 79 | 23_break.c3 | vnořené programové smyčky a příkaz break: ukončení vnitřní smyčky | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/23_break.c3 |
| 80 | 24_break.c3 | vnořené programové smyčky a příkaz break: ukončení vnější smyčky | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/24_break.c3 |
| 81 | 25_break.c3 | vnořené programové smyčky a příkaz break, varianta se smyčkami typu while | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/25_break.c3 |
A konečně následují odkazy na demonstrační příklady z článku dnešního:
20. Odkazy na Internetu
- Programovací jazyk C3: evoluce, nikoli revoluce
https://www.root.cz/clanky/programovaci-jazyk-c3-evoluce-nikoli-revoluce/ - Programovací jazyk C3: datové typy pro moderní architektury
https://www.root.cz/clanky/programovaci-jazyk-c3-datove-typy-pro-moderni-architektury/ - Programovací jazyk C3: složené datové typy a kontejnery
https://www.root.cz/clanky/programovaci-jazyk-c3-slozene-datove-typy-a-kontejnery/ - The C3 Programming Language
https://c3-lang.org/ - C3 For C Programmers
https://c3-lang.org/language-overview/primer/ - C3 is a C-like language trying to be an incremental improvement over C rather than a whole new language
https://www.reddit.com/r/ProgrammingLanguages/comments/oohij6/c3_is_a_clike_language_trying_to_be_an/ - Tiobe index
https://www.tiobe.com/tiobe-index/ - PYPL PopularitY of Programming Language
https://pypl.github.io/PYPL.html - C3 Tutorial
https://learn-c3.org/ - History of programming languages
https://devskiller.com/history-of-programming-languages/ - History of programming languages (Wikipedia)
https://en.wikipedia.org/wiki/History_of_programming_languages - D language
https://dlang.org/ - Zig programming language
https://ziglang.org/ - V language
https://vlang.io/ - D programming language
https://en.wikipedia.org/wiki/D_(programming_language) - Zig programming language (Wikipedia)
https://en.wikipedia.org/wiki/Zig_(programming_language) - V programming language (Wikipedia)
https://en.wikipedia.org/wiki/V_(programming_language) - Syntax highlighting for C3's programming language
https://github.com/Airbus5717/c3.vim - Go factorial
https://gist.github.com/esimov/9622710 - Generational list of programming languages
https://en.wikipedia.org/wiki/Generational_list_of_programming_languages - The Language Tree: Almost Every Programming Language Ever Made
https://github.com/Phileosopher/langmap - List of C-family programming languages
https://en.wikipedia.org/wiki/List_of_C-family_programming_languages - Compatibility of C and C++
https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B - C++23: compatibility with C
https://www.sandordargo.com/blog/2023/08/23/cpp23-c-compatibility - Can C++ Run C Code? Understanding Language Compatibility
https://www.codewithc.com/can-c-run-c-code-understanding-language-compatibility/ - C3: Comparisons With Other Languages
https://c3-lang.org/faq/compare-languages/ - C3 Programming Language Gains Traction as Modern C Alternative
https://biggo.com/news/202504040125_C3_Programming_Language_Alternative_to_C - The case against a C alternative
https://c3.handmade.network/blog/p/8486-the_case_against_a_c_alternative - C (programming language) Alternatives
https://alternativeto.net/software/c-programming-language-/ - Seriál Programovací jazyk Go
https://www.root.cz/serialy/programovaci-jazyk-go/ - Is C3 the Underdog That Will Overtake Zig and Odin?
https://bitshifters.cc/2025/05/22/c3-c-tradition.html - „Hello, World!“ program
https://en.wikipedia.org/wiki/%22Hello%2C_World!%22_program - The C Programming Language
https://en.wikipedia.org/wiki/The_C_Programming_Language - Kontejner (abstraktní datový typ)
https://cs.wikipedia.org/wiki/Kontejner_(abstraktn%C3%AD_datov%C3%BD_typ) - Are arrays not considered containers because they are not based off of a class?
https://stackoverflow.com/questions/37710975/are-arrays-not-considered-containers-because-they-are-not-based-off-of-a-class - Array declaration (C, C++)
https://en.cppreference.com/w/cpp/language/array.html - Understanding the Apple ‘goto fail;’ vulnerability
https://www.blackduck.com/blog/understanding-apple-goto-fail-vulnerability-2.html - Branch (computer science)
https://en.wikipedia.org/wiki/Branch_(computer_science) - Conditional (computer programming)
https://en.wikipedia.org/wiki/Conditional_(computer_programming) - Dangling else
https://en.wikipedia.org/wiki/Dangling_else - Switch statement
https://en.wikipedia.org/wiki/Switch_statement