A Darwinu na macu, který je také kombinací c++ již pěkně dlouho.
Jinak mě na tom fascinuje jiná věc, mailing list v téhle podobě je asi jediná věc, co znám, kde je možné držet přehledně vlákna i po tak dlouhou dobu. Komerční společnosti přesedlali na různé uber/super/great služby na diskuze/trackování/messages s jediným výsledkem, nelze přehledně držet žádné informace dlouhodobě, rozumně se odkazovat a reagovat. Tohle je fantastické, po tolika letech lze navázat, tam kde se skončilo.
12. 1. 2024, 15:06 editováno autorem komentáře
Zalezi na tom, co z C++ chteji vyuzivat. Pokud by zustali vicemene u programu v C a za C++ vyuzili pouze nektere konstrukce, ktere by zjednodusily zapis struktur, nic bych proti tomu nemel.
Ovsem pokud by se pak kernel dostal do stavu, kdy by se vyzivaly classe s konstruktorama a destruktorama a co hur, funkce, co maji stale stejne jmeno, ale lisi se podle toho, jake jim predas parametry, to by vedlo k silenym zmatkum. Nevim jak vy, ale s C++ mamnekdy potize s tim, ze neni vubec jasne ktera verze funkce se zavola, protoze nekdy neni jasne jak se nektere parametry samy zkonvertuji treba z char na int a podobne. Nebo co hur, pridas dalsi verzi funkce s jinou kombinaci parametru a cast programu, na kterou jsi nesahnul, prestane fungovat, protoze se z nej zacne volat jina verze funkce, protoze parametry se zkonvertuji jinak. Stejne tak byva zmatek s destruktorama - nikdy nevis, v jakem poradi se zavolaji.
Proste podle me je C++ krasne, pise se v nem dobre, ale je znacne nedeterministicke.
Přesně.
Toto je jedna z věcí, kterou Rust udělal fakt dobře - žádné exceptions, žádný problém. Exceptions jsou feature, která přináší víc škody než užitku a dodnes je celá C++ komunita rozdělená na 2 tábory... C++ bez exceptions a s exceptions to jsou v podstatě 2 světy, které nejdou k sobě a dnes už se s tím nedá nic dělat.
Vyjimky se mi celkem libi. Silne to zjednodusuje program. Ale je pravda, ze pak staci neosetrit jednu vyjimku a jsou z toho problemy. Ja pisu spis drivery, web services a podobne, takze tam se to pouziva celkem dobre. Proste vznikne vyjimka, na nejvyssi urovni se odchytne a vrati se packet s errorem. Pak nemusi byt program prospikovany testem za kazdou funkci, aby clovek zjistil, ze nekde nastala chyba. Predtim jsem psal programy stylem, ze kazdy radek vypadal plus minus takhle:
if (!e) e=operace(parametry);
Pripadne
e=e||operace(parametry);
Ale takhle se neda psat program.
Podívej se na Rust, jak se dá psát program - tam ten error handling je celkem dobře udělaný i bez exceptions a něco takového kdyby bylo v C++, tak bych neměl moc důvod dívat se jinam.
Problém exceptions ale není jen toto - udělat review kódu, kde se používají exceptions prostě není možné, a proto se někde vůbec nesmí používat. Jenže když se nepoužívají, tak pak celé C++ se používá dost špatně, protože si člověk musí implementovat všechno sám bez exceptions a na propagaci chyb se pak nejlíp používají různé makra. A právě toto má Rust udělané mnohem líp.
Tak já třeba Rust používám jako jeden z mých hlavních jazyků, a absence výjimek mě tam spíše mrzí. Ano, dá se to používat, neříkám nic. Ale nejsem přesvědčen, že by to bylo čitelnější.
Abych doplnil důležitý bod: obhajuji kontrolované výjimky - tedy, když vytvořím funkci, která volá funkci, která může vyhodit výjimku, a já ji nekontroluji, tak to musím v signatuře dát vědět. Jinak to přehledné být přestane.
Nutnost řešit, že funkce může vracet chybu při volání té funkce, mi přijde z architektonického hlediska zbytečně zbabělé.
A právě toto má Rust udělané mnohem líp.
Ok, filozoficky: jak se liší Exception od std::panic::catch_unwind a panic!? Pokud je někdo pr*se a řídí pomocí Exception flow programu, může to teoreticky řídit i catch_unwind. Podle mě zásadní neopochopení Exceptions (ačkoli se to píše snad všude) je že lidé toho zneužívají k řízení programu a ne k signalizaci "výjmečného" stavu
15. 1. 2024, 16:48 editováno autorem komentáře
Výjimečný stav v C++ definuje 10 z 10 C++ programátorů jinak, dokonce se v mnoha případech neshodnou ani lidi ve stejném teamu. Toto je moje zkušenost.
- https://doc.rust-lang.org/std/panic/fn.catch_unwind.html
Zrovna v Rustu má podle mě panic jasný význam: Neopravitelná chyba. Což je trochu něco jiného než jak s výjimkama pracuje C++.
Neočekávaný stav není ve všech situacích stejně neočekávaný a také k němu nebudete vždy přistupovat stejně. Někdy chcete aplikaci ukončit, ale někdy chcete, aby se zotavila, pokračovala dál, nebo zopakovala volání (časté u síťových operací). Těch příkladů lze najít celou řadu. Je naprosto legitimní použít exceptions pro řízení flow programu nebo jeho části, pokud to dává smysl.
Bohužel není vždy jednoduché se správně rozhodnout a také ta výchozí situace se může v průběhu času změnit.
A proto se mi líbí přístup Rustu, kde vlastně není chyba nijak výjimečná, jen je reprezentována typem Result a který lze jednoduše převést na Option když je potřeba.
Navíc se dá snadno zakázat neošetření výjimky lintem. Občas se sice hodí povolit expect v případě kdy chyba reálně nemůže nastat, ale i pak je fajn mít to zakázané, snáz to donutí vývojáře explicitně povolit výjimku a dokumentovat proč.
Tohle mě na Rustu opravdu baví. Oproti C/C++ nacházím v existujícím kódu mnohem míň pastí.
Mne to prijde jako zbytecna diskuze v tuhle chvili.
C++ podporuje Designated initializers az od C++20 (nektere kompilatory je umi i s nizsim language levelem, ale jiste to neni). A tak nejak tusim, ze zrovna techhle inicializaci muze byt kernel celkem plny... Takze bez C++20 se to stejne nezbuildi.
Tohle je pro me osobne asi nejvetsi rozdil mezi C a C++ (casem tedy zhyne, ale jeste ne).
Třeba Zig má speciální podporu pro vektory, kde generuje SIMD instrukce. Nebo pro lepší využití cachí procesoru umí pomocí comptime převést pole se strukturou na strukturu s polemi uvnitř. Podobné věci umí i Odin.
Pro jádro pak může být zajímavá podpora vlastních alokátorů (Zig i Odin), elegentní error handling (Odin), číselné typy s libovolný počtem bitů (Zig - až u65535), volba endiánu (Odin - např. u32le).
Samozřejmě drobná vada, je že Zig není vhodný pro produkční nasazení, protože se mění ze dne na den.
BTW Nějaké intristics má i C#/F#
No on si v podstate odpovedel sam, ze ne
https://lore.kernel.org/lkml/152261521484.30503.16131389653845029164.stgit@warthog.procyon.org.uk/
Vyjmenoval spoustu konstruktu z C++, ktere by zakazal a nekolik problemu. Jaky to pak ma smysl?
Navic, kdo by urcil, jestli nejaky kod ma byt napsany objektove, neobjektove, pomoci template, atd?
12. 1. 2024, 18:04 editováno autorem komentáře
Já myslím, že to hodně lidí nepochopilo. O žádný přepis nejde. Idea je taková, že C++ umí některé věci, které jsou dobré a ten jazyk je blíž C než Rust. Takže třeba type-safe šablony pro kontejnery, kde se teď všechno castuje, tam by to bylo lepší, ale nikdo nemluvil o tom dělat nějaké šílené OOO.
Podle mě to nemá šanci, ale vzhledem k tomu, že tam už je Rust, tak to asi stojí o diskuzi, případně nástřel toho, co by se v C++ mohlo používat a co ne. Exceptions, RTTI, a komplet std bude určitě zakázané, takže rozhodně nejde o nějaké "moderní C++ v kernelu", ať už si pod tím představí kdo chce co chce...
Ještě bych dodal, že třeba fuchsia má taky jádro v "restricted" C++ - dnes je to celkem běžná věc.
12. 1. 2024, 19:41 editováno autorem komentáře
Tak celkem rozumna typova bezpecnost je u dulezitych veci implementovana pomoci sparse a maker/inline funkci, takze nejaky extra posun pri pouziti sablon nevidim.
Uz druhe desetileti se zivim vyvojem a portovanim Linuxu na ruzne architektury (hlavne telekomunikacni systemy) a predstava, kdy resim nefungujici kod vygenerovany C++ prekladacem na obskurnim HW, kde je znacna sance, ze muze byt spatne jak ten kod, tak prekladac, tak i HW, me desi.
Treba prohlizet kod vznikly specializaci sablon asi nebude uplne zuzo, obzvlast jakmile se sablony zacnou zanorovat. Kdyz k tomu pridame to, ze by se ztratila rozsirena typova kontrola, kterou dela sparse (pokud by ho nekdo nerozsiril o sablony), tak mi to proste prijde kontraproduktivni. A to, ze gcc pouzije spatne typy neni tak neobvykle, uz jsem objevil chybu, kdy ztratilo volatile kvalifikator z elementu struktury ve strukture a kdy pro rozdil dvou ukazatelu bylo pouzito signed odcitani.
Vec<> bohuzel neni moc dobry priklad, protoze uvnitr kernelu se proste nespolehas na jinou, nez svou vlastni implementaci, protoze potrebujes presne vedet, co ten kod dela. Pridej k tomu specializace pro ruzne typy a uz z toho mas binec, ve kterem se nikdo nevyzna. Dobry priklad je treba nasledujici:
Pokud v C pridam pro vektory typu bool bitove pole, namisto puvodniho unsigned char pole, tak ho nazvu jinak a kdyz ho nekdo bude chtit pouzit, pak bude muset prepsat svuj kod a prejit na novou implementaci.
Pokud udelas specializaci sablony v C++ pro vektor typu bool, ze je bitove pole a ne char pole, tak v momente, kdy nekdo takove pole pouziva o takove zmene vubec nemusi vedet a pravdepodobne to bude fungovat dobre, ale v pripade, ze se bude spolehat na puvodni velikost, tak ma problem.
Tohle lze samozrejme ale vyresit ze zavedes standardni postupy, taky nemenis velikosti struktur a drzis API/ABI atd., ale jako priklad je to dobre.
Navic cela std knihovna je nepouzitelna taky ze stejneho duvodu, jako je nepouzitelna C standard library.
Divej, že má C++ posraný std::vector<bool> víme všichni a nevim proč by toto měl být nějaký argument k této diskuzi, když víme, že std se do kernelu nikdy nedostane, protože exceptions...
Kernel taky alokuje paměť a má na to vlastní alokátor, tak by Vec<> použil ten... V tomto konkrétně nevidím problém. To co implementovali v kernelu v C není problém implementovat v C++.
Nebud tak haklivy, neni to pripad std::vector<bool>. Zamerujes se na jeden konkretni problem, ale muj priklad byl spise o tom, ze pokud mas nejaky kontejner, tak proste potrebujes vedet, jak presne funguje a nesmi se to rozbit. Coz se nejspis stane, kdyz nekdo pro dany typ dat udela specializaci. Myslel jsem to hodne obecne. V praxi by se to samozrejme resilo vlastni implementaci v kernelu.
Takze navrhujete udelat dalsi sablonu, ktera by definovala, jaky alokator a s jakymi flagy se pouzije + jeste rozsirit API, aby se dal zmenit alokator za behu pro pouziti, kdy se cast alokuje pri startu pred inicializaci slabu a cast az potom. To uz se trochu zacina blizit tomu sablonovemu peklu, ktere jsem popisoval jako duvod, proc C++ nepouzivat.
Dalsi otazka je, jestli bychom timto zpusobem neprisli o optimalizaci, ktera vybere cilovou slab cache uz v dobe prekladu, pokud alokovana velikost je compile time constant. Vlastne neprisli, protoze tu velikost bychom prece taky mohli dat do parametru sablony...
Nějak nerozumím. Ten alokátor je typ s jednoduchým rozhraním pro alokaci a dealokaci. Vec ani vector to víc řešit nepotřebují. A celý alokátor se tomu vektoru předá při konstrukci.
Jestli ta alokace potřebuje nějaké flagy či něco podobného, tak to může být schované uvnitř toho alokátoru. Žádné rozšiřování šablon není potřeba.
Různé alokátory pro různá použití je naprosto běžný use case.
Prave ze nemuze, v Linuxu pri alokaci pouzivate ruzne flagy, urcujici treba jestli alokator muze blokovat. Napr. B+ strom nema ty flagy ulozene nekde v instanci, ale jsou parametrem insertu. Vizte https://elixir.bootlin.com/linux/latest/source/include/linux/btree.h#L110
Implementace stromu nemuze vedet, z jakeho kontextu je volana. To, ze se u jedne instance nejake genericke struktury pouzivaji rozdilne flagy pri alokaci je naprosto bezne, protoze se proste pouziva ze dvou rozdilnych kontextu. Vpodstate cele STD API je kvuli tomu nepouzitelne.
> Treba prohlizet kod vznikly specializaci sablon asi nebude uplne zuzo
Prosím reálnou situaci, kdy to potřebuješ? Jako myslíš si prohlížet kód v C++?
Já třeba si prohlížím výsledek v asembleru a kolikrát několikrát vnořená šablona vede na deset instrukcí kódu. A jsou i situace, kdy ta šablona nevede na žádný kód díky optimalizaci na takovou úroveň, že se vše udělá v rámci jiné věci a výsledky se jen reusnou. Takové rekurzivní volání v šabloně mnohdy překladač rozbalí do několika instrukcí za sebou.
Při tomhle prohlížení mne ale spíš zajímá, jestli překladač někde zbytečně nezahazuje nějakou optimalizaci, nikdy by mě nenapadlo to kontrolovat z hlediska funkčnosti. Pokud ta věc je jazykově správně napsaná, pak musí fungovat aniž bych to musel prohlížet a kontrolovat.
Tak já se s tím setkal jednou asi po 10 letech a to v důsledku použití nových featur (korutin) v gcc-11. A nebylo to v šablonách. Tam ale asi má smysl kontrolovat disassembler, než se probírat cestami dané šablony.
Nevím co používáte za překladače nebo HW, že jste tak často obětí chyb. V drtivá většina chyb vzniklá třeba po zapnutí optimalizací byla chyba na mé straně (přístup ke zničené proměnné typicky jako reference na temporary)
Nikdy!
Respektive pokud se nekdo rozhodne Linux forknout, at si jej prepist do ceho je mu libo. Klidne treba do Pascalu, kdyz bude chtit a ma na to koule. Vas projekt - Vas jazyk - Vase volby - Vase pravidla a ... Vase utrpeni.
Maly dodatek: To se netyka modulu v userspace, jako je napriklad FUSE. Tam je naopak podpora co nejvetsiho mnozstvi jazyku zadouci.
13. 1. 2024, 10:56 editováno autorem komentáře
Osobne si myslym, ze C a C++ sa mohli spojit do jedneho standardu, ktory by sa vyvijal spolocne. Syntakticky je C viac-menej podmnozinou C++. To co je odlisne je exekucny model, kde C ma jednoduchsi a mozno efektivnejsi model a C++ ma komplexnejsi model, ktory ale ulahcuje programovie a zivot samotnym vyvojarom.
V C by sa kludne mohli pouzivat aj niektore objektove principy (prakticky triedy bez virtualnych funkcii).
Cielovy exekucny model by sa zvolil pri kompilacii nejakym prepinacom, kedy by sa aktivovali aj patricne obmedzenia na syntax a pod.
Iste ficurky objektoveho programovania by sa hodili aj pri kodeni low-level veci ako je kernel.