...
splachovaci, a proto se musim branit: V C++ delam od r.1993, profesionalne od 1999. Naucil jsem se drive C++, nez C (ne, to neni protimluv - psat udrzovatelne a korektne velky projekt v C je neco jineho nez "nepouzivat tridy a templaty" v C++) a na C jsme presli (cely team) kolem roku 2008.
...
Zaujima prechod z C++ na C. Kedze s tym mas skusenosti prosim Ta mohol by si to trochu viac rozviest. Napr. aky velky projekt robite? Ako mate implementovane kontajnery (makrami, ...)? Pouzivate vela makier? Nechyba vam obcas C++? Rychlost kompilacie je asi podstatne vyssia? Pouzivate GUI? Nemuseli ste si vyrobit objektovy system podobny GObject-u? Pouzivate GOTO (napr. ako nahrada vynimiek)? Kludne mi to mozes poslat ako PM, aby sme nespamovali toto vlako.
Jedna se o projekt toho typu o kterem se zminil "ferren" - neustale bezici system, kde neni vyzadovana ani tak nejvyssi rychlost, ale hlavne spolehlivost. Takze implementujeme presne to o cem "ferren" psal - jakakoliv chyba, ktera je "neopravitelna" (nejde forknout, nejde alokovat pamet, atd.) zpusobi spadnuti celeho jednoho procesu. Cela "architektura" je realizovana jednim procesem ktery spousti dalsi procesy, ktere spolu komunikuji pres local-unix-sockety. Nikde zadna sdilena pamet a zadne thready (byly, ale bylo s nimi vic problemu nez je zdravo - procesy + messages jsou sice "mene uzivatelsky pritulne", ale zato radove robustnejsi.
System je duplikovany, takze v pripade padu prebira praci jiny server, dokud nekdo neda puvodni do poradku.
Posledni verze napr. na vetsine produkcnich serveru bezi bez restartu dele nez rok.
Projekt neni ani tak velky co se tyce mnozstvi kodu, jako spise komponent - mame cca 40% kodu v C, 50% v pythonu, zbytek v plsql + nejake shell skripty a dokonce je nekde i nejaky legacy perl (nemluvim o GUI).
Tj. co se GUI tyce - ne, zadne gui, je to ciste "back-end", GUI dela jiny team.
Kontejnery - pouzivame GLib/GObject knihovny tam kde je potreba neco "trochu vysokourovnovejsiho".
Makra - pokud pominu ze GLib/GObject sam pouziva maker hodne, tak i my nejake pouzivame, ovsem ve vetsine pripadu se jedna o "lokalni" makra - pouzite v jednom souboru a nebo pro ruzna "DEBUG-mode zavisla" makra.
Co se "chybeni C++" tyce, GObject (teoreticky) poskytuje dokonce i vic "objektovych" vlastnosti nez C++, ovsem za cenu neuveritelneho mnozstvi dosti oskliveho "boiler-plate" kodu. Vetsinu "vysokourovnovejsich" objektu tedy resime tak, ze je napiseme ve Vale (mozna si nekdo vzpomene ze jsem zde pred par dny pred valou varoval), ta je "zkompiluje" do C a pak pokracujeme dale upravami vygenerovanych souboru.
Pokud neni nutne pouzivat nejake "objektove" vlastnosti (napr.dedicnost) a staci neco jako "zapouzdreny objekt", pouzivame konstrukci s "opaque" struct, tj. v .h souboru je definovany jen "typedef struct SomeObject_ SomeObject;" a funkce pracujici s "SomeObject" - tim mame vzdy dvojici ".h" a ".c" souboru fungujicich jako urcita "zapouzdrena jednotka". To je asi hlavni bod "prechodu na C" a zaroven vyreseni problemu neexistence "namespace" v C - vetsina "discipliny" je o tom dodrzet urcita pravidla pro jmena funkci a nektere "povinne" funkce/makra (new, delete, cast, asserty)
Tj. pouzivame vedle sebe jak GObject objekty (odlisene prefixem) i "dumb" objekty (viz vyse). Rekl bych ze GObject objektu mame tak 5% a postupne je eliminujeme ("vyssi" funkcnost se presouva do pythonu a plsql).
GOTO pouzivame velmi silne, ovsem z 99% pouze pri osetrovani chyb pri "komplikovanejsi" konstrukci objektu k zaruceni pouze jednoho return-u a/nebo k zajisteni korektniho "uklidu" pri chybe v konstrukci objektu. Takze se da rict, ze v ramci jedne funkce GOTO do urcite miry supluje vyjimky.
Jinak na to co by mely byt vyjimky pouzivame GError.
Atd, atd.
Co se zkusenosti tyce - nejvetsi plus je spolehlivost - za posledni 3 roky jsme meli pouze jeden "zahadny bug", ktery se navic nakonec ukazal jako spatne nastaveni objektu z externi knihovny. Obecne muzu rici, ze debugovani Cckoveho kodu, i kdyz je ho vice, je mnohem snazsi, diky tomu, ze C-cko je primitivnejsi, tak je i vetsina chyb "jednodussich" - paradoxne jsme jeste nemeli memory leak nebo segfault v produkci - coz s C++ bylo celkem caste. Ja osobne to pricitam efektu, ktery jsem mockrat v ruznych variantach cetl na ruznych mistech - protoze debugovani je slozitejsi nez psani kodu, tak kdyz napisu kod tak chytry jak to jen dokazu, tak ho pak nedokazu debugovat.
Navic code-review je opet mnohem jednodussi, protoze neni nutne zjistovat zadne vnorene vyjimky, zadne pretizene operatory, a hlavne, kdo nekdy videl jak vypada hlaseni chyby kompilatoru z nekolikrat "zanoreneho" templatu, tak oceni ze temer kazda chyba je celkem zjevna.
Nekteri lide vyzdvihovali jako prednost veci jako: stabilni ABI (u C++ to je trochu horsi), dostupnost C kompilatoru pro snad kazdou existujici platformu (vcetne embedded) - coz myslim uz dnes neni moc pravda (a mozna to nebyla pravda ani v 2008).
Rychlost kompilace rozhodne JE plus - kompilace celeho C kodu je na mem notebooku pod 30 sekund - tehdejsi C++ varianta se kompilovala i pres 10 minut (ano, mel jsem pomalejsi pocitac) - ovsem diky modularite projektu to neni az takove plus jak by se mohlo zdat.
Abych jen nechvalil - jak jiz jsem zminil, hlavni nevyhodou je delsi kod a slozitejsi testovani (mame vicemene 99% kodu pokryto unittesty), kde je nutne pouzit trochu jine principy (klasicke mockobjekty definujici nejaky interface jdou s GObject pouzit, ale je to vesmes vice prace nez se vyplati). Rozhodne je nevyhoda i to, ze kazdeho noveho programatora je nutne "prevychovat", protoze kdyz nekdo napise v CV, ze "umi C", mysli tim v 99% ze umi C++ a tedy ze vlastne umi i C.