Taky mě trochu hlava nebere. Rust se srovnává s C, přitom třeba má vlastnosti hodně podobné C++, včetně všelijakých kontrol. Pramení to snad s dodnes opakované a nepravdivé Linusovi mantry o tom, že C++ se pro vývoj kernelu nehodí? Nikdy by nikdo nebyl schopen odpovědět na otázku proč.
BTW: C++ používám pro psaní software pro Arduino. Bez problémů (jen mi chybí výjimky, ale chápu, proč jsou problém)
No to nevim. Zacinal jsem na wiringu ale pak jsem zjistil, ze je to postaveny nad c++ takze jsem to zacal psat ve visual studiu naplno v c++ a wiring pouzil jako knihovnu k desce. Pouzivam sablony, alokace objektu (destruktory to vola), i virtualni funkce (akorat tam v knihovne chybi nejake funkce ktere se predpokladaji ze jsou v libc, da se obejit) ... a prekladac odmitne prelozit kod obsahujici throw.
Pokud to člověk chce apriori považovat za neplatné, tak stejně žádnému důvodu neuvěří. Podle mě je C++ dost strašný jazyk, který vynucuje nešikovný objektový model a přitom nenabízí nic směrem k odolnosti vůči chybám, spíš naopak. Oproti tomu je C jednoduchý a dobře standardizovaný jazyk, který lépe odpovídá tomu, co se děje ve výsledné binárce, lépe se v něm řeší binární i zdrojová kompatibilita, nevynucuje jeden nepříliš dobrý objektový model.
Ale jak říkám, člověk, který se jednou zahrabal v C++ a považuje to za nejlepší věc na světě tohle asi nepobere.
OLOL OLOL
Pane, napsal jsem ve standardním C hru. Pak jsem zkusil C++ a pak jsem se učil Javu a OOP a pak jsem se vrátil zpět k C++ a vzpoměl jsem si, jaká trápení jsem během psaní té hry zažil,jak jsem si vyvíjel systémy, které řešili
- stringy
- virtuální funkce
- lambda funkce (ono známé, předání pointeru na funkci a kontext)
- zapouzdření dat (AI nestvůr v té hře by se realizovala mnohem lépe pomocí objektů)
čisté C? Nikdi více!
A co jako? Mám přesně opačnou zkušenost. Jestli má někdo potřebu se po naučení Javy vracet k C++, tak to ten jeho kód musí být teda pěkná "lahůdka". Raději bych to snad ani nechtěl vidět, natož tam muset něco modifikovat. A soudě dle vašich různých názorů zde v diskusích mám z vás pocit, že přestože "jste napsal hru", tak bych si k vám pro rady ohledně technik programování a softwarového inženýrství rozhodně nechodil.
A to je podle me i odpoved na to, proc se rika, ze Rust ma ambici nahradit C. C++ je C s pridanou hodnotou (a hromadou balastu) a +- se v nem porad da psat normalni C. Rust se snazi predefinovat "nastupce C" s minimem balastu a znalostmi z okolniho sveta, ktere autori maji.
Myslim, ze Linus to vysvetloval tim, ze C++ skryva pred programatorem alokaci pameti, coz muze byt v kernelu kontraproduktivni. Napr. diky implicitnim datovym konverzim zavolate metodu a pred jejim faktickym zavolanim si vytvorite hromadu docasnych objektu na stacku.
C a C++ maji "problem" v tom ze nerozlisuji mezi poiterem na stack a pointerem na heap. Snadno muzete na heapu vytvorit instanci, ktera ukazuje na stack vaseho vlakna a vsechno bude krasne fungovat dokud vase vlakno neumre. Takove chyby, jsou jen velice tezko odhalitelne. (I kdyz v C++ se daji udelat smart pointery, ktere neco takoveho nedovoli)
Pokud je to pravda, tak Linus nezná C++, jinak by to netvrdil ... ale dost možná to spíš neřekl nebo to řekl jinak.
C++ nedělá žádné skryté alokace. Možná je dělá knihovna STL. Ta je sice součástí standardu, ale není povinnost ji používat. Objekty lze v C++ nadefinovat tak, že se chovají přesně tak, jak tvůrce vyžaduje, včetně toho, že má pod kontrolou přesný postup co se volá při vytvoření a při destrukci (což taky znamená, že se nemusí dít vůbec nic).
Takže ano, pokud napíšete std::string x = "ahoj", pak se tam alokuje paměť pro řetězec "ahoj". Já z toho důvodu používám objekt, který nazývám StringView, který nic nealokuje. Mimochodem, něco podobného (jako std::string_view) se chystá do C++17. Ale to nebrání tvůrce kernelů si něco podobného vyvinout už od C++98.
Ano, co pro C++ chybí je knihovna pro programování kernelů..., kde by byly do objektů zabalené věci na míru implementované potřebám kernelu.
C++ nedělá žádné skryté alokace.
V originale Linus psal:
any compiler or language that likes to hide things like memory allocations behind your back just isn't a good choice for a kernel.
Nemluvi tedy o skrytych alokacich, ale o jazycich, ktere rady skryvaji veci jako je alokace, coz ma trochu jiny vyznam. Staci se zamyslet nad tim, proc vznikaji vyssi jazyky. Je to prave proto, aby skryvaly jednotlive nudne casti programu a nezatezovaly tim programatora. Kdyz tedy C++ nic neskryva, k cemu je dobre?
Takže ano, pokud napíšete std::string x = "ahoj", pak se tam alokuje paměť pro řetězec "ahoj".
Nejak na tom radku postradam explicitni alokaci pameti. Nebude to tim, ze ji C++ skryva?
Vidím tam viditelné volání konkrétní funkce (strdup) s konkrétními parametry. Takže, když už volám nějakou (cizí) funkci a nevím co dělá, tak šup do dokumentace. Třeba:
http://man7.org/linux/man-pages/man3/strdup.3.html
... a vida. Je tam jak informace i že volá malloc (a ne nic jiného, třeba new) a je tam i info o free.
Nikde není zamlčeno, že tam (existuje) "mezilehlý" návratový alokovaný prostor, není zamlčeno kde že se vytvoří (heap x stack x ...) a jaké funkce k tomu použil (malloc x new x static ...). A je jasně dáno (tedy, je to dáno na mne, programátora) kdy to místo bude uvolněno, respektive sám určím kdy uvolním.
Vím moc dobře, jak to tam je a vím (pracuji na low-level, embedded) a že tam s tím dokáži udělat pěkné prasárny. Stačí to toho zapojit pár online a optimalizaci a mírně se to zamotá děděním. Plus problém se systémy, kde např. konstanty (vč. statických stringů) mohou být chápány různě v závislosti na nastavení překladače (a pak jeho následné optimalizaci).
Navíc, sakra záleží na obou stranách přiřazení. Nejjednodušší pak bývá jít se podívat do assembleru, co že mi tam reálně udělal. Někdy to bývá docela překvápko a jsou to 3-4 instrukce a hotovo. Někdy (stačí drobná změna) je z toho alokace na stacku, volání konstruktoru a destruktoru hned za sebou (který nic nutného neudělá, žádné volatile) a pak přiřazení něčeho úplně mimo.
No, rozepisoval bych se moc, zkrátím to. Vracet objekt je věc, která může nadělat dost škody i ve chvíli, kdy člověk myslí, že zná dokonale, jak mu překladač na daný systém funguje.
... a to je to, čím může být c++ nebezpečné. Jak některé věci může skrývat před očima programátora, tedy dělat implicitně i alokace na stacku (minimálně), vlastně jen dle uvážení (doslova, někdy ano někdy ne, dle optimalizace). Je to fajn, dokud funguje a je dost místa a runtime času a nesnaží se optimalizovat - pak obvykle nezkrachuje. Obvykle. Ale jakmile se zajde do hloubky ...
Mel jsem na mysli neco takoveho:
#include <stdio.h> class Foo { public: Foo(int x) {}; }; class Bar { public: Bar(Foo const &_f) : f(_f) {}; Foo const& f; void print() { const void *p = &f; printf(" pointer: %p \n", p); } }; int main() { Bar *pf1 = new Bar(Foo(42)); Bar *pf2 = new Bar(42); pf1->print(); pf2->print(); return 0; }
Zavolam "new Bar(42);" a vubec si nevsimnu, se vlastne vykona "new Bar(Foo(42));" a mam na heapu instanci, ktera ukazuje na muj zasobnik.
PS: uz jsem v C++ dlouho nedelal, takze jestli je to blbost, tak me opravte.
Popravdě jsem si myslel, že s deklarací konstruktoru `Bar(Foo const &_f)` se kvůli ampersandu výraz `new Bar(Foo(42))` (popř. `new Bar(42)`) ani nezkompiluje, protože to není left-side-expression. Ale ono se to fakt zkompiluje: http://codepad.org/5Bc1s0Az
Zřejmě se to skutečně alokuje na stacku.
Na svou obranu dodám, že C++ mě neživí a beru to spíše jako součást všeobecného rozhledu.
Nikdo netvrdi ze C++ je bezpecny jazyk ale lze v nem psat bezpecne. Vse je o lidech a o stabni kulture. Tak napriklad vyse uvedeny priklad se prelozi, protoze const T & se da aplikovat i na docasne objekty a nepredpoklada se, ze se budou drzet jako const reference, jejich platnost by mela byt zajistena do navratu z funkce (constructoru). Pokud uz to ale nekdo takhle napise (i ja to tak nekdy delam) tak se tam dava klicove slovo explicit, cimz se zabrani automaticke konverzi (ale nezabrani to kdyz to nekdo udela manualne, holt je to v lidech)
Proto by konstruktor s jedním argumentem (když to není copy/move) měl být deklarovaný jako explicit, pokud opravdu nechci definovat typovou konverzi. Ale úplně stejně špatně je i new Bar(Foo(42)) v inicializaci pf1. Když si třída Bar drží referenci na argument konstruktoru, tak by tam asi měla být jako pojistka ještě jedna deklarace konstruktoru: Bar(Foo &&_f) = delete
pouziti explicit, nebo vymazani rvalue copy constructoru resi ten problem jenom castecne.
"Vychytraly" programator muze snadno udelat treba:
Foo f;
Bar *b = new Bar(f);
Hlavni problem je ze kdyz si nekde ukladam ukazatel na objekt, tak ten objekt by nemel mit kratsi zivotnost nez ten ukazatel.
V dokumentaci ke tride bar by melo byt jasne napsane - uklada si ukazatel na parametr, je potreba zajistit zivotnost... a programator by se tim mel ridit :-)
Např. v libc++ std::string x = "ahoj" nic navíc nealokuje, protože krátké řetězce (do cca 20 znaků) se vejdou přímo do objektu std::string. Navíc pro třídy z STL je možné definovat vlastní alokátory a tím libovolně změnit způsob, jak se dynamická paměť alokuje a dealokuje.
I v kódu kernelů psaných v C se spousta věcí skrývá ve funkcích, v horším případě v makrech, v nejhorším případě v kódu generovaném pomocí ad-hoc skriptů při buildu. Ekvivalentní kód v C++ používající šablony, lambdy a inline funkce je obvykle o dost srozumitelnější. Nebo naopak programátor musí vědět, že má zavolat přesně definovanou posloupnost operací. A když to udělá, tak musí přidat do každé následující větve kódu jiné operace. Typicky takto vypadá použití zámků. Třídy a RAII v C++ v takových situacích poskytují lepší řešení.
Ja si treba myslim, ze C je na kernel lepsi z hlediska code-review a kontroly spravnosti kodu. C je 'jednodussi' jazyk - clovek si vystaci s par klicovymi slovy nekolika malo pravidly a jedna z nejslozitejsich veci je priorita operatoru. Naproti tomu v C++ clovek musi umet ruzne konstruktory, musi vedet ze jeden zapis (treba Foo a = b) muze volat ruzne konstruktory, v zavisloti na typu vyrazu b a tak podobne. Za mne je C++ sice casto lepsi nez C, ale je to bastl, takze na to aby clovek mohl _cist_ program se toho musi naucit mnohem vic nez v pripade C. Jine jazyky vyssi urovne se tomu vetsinou vyhly, protoze mely vyrazne konzistentnejsi vyvoj (ale existuji vyjimky jako treba PHP, to je imho jeste horsi bastl).
Ekvivalentní kód v C++ používající šablony, lambdy a inline funkce je obvykle o dost srozumitelnější.
To je hezke pri normalnim vyvoji, v pripade jadra to muze byt zasadni nevyhoda, protoze na jednu stranu ziskate sice srozumitelnejsi kod, ale zase ztracite predstavu o tom, jak dany kod bude vypadat po prelozeni do assembleru/strojoveho kodu.