Autodesk ne invata sa programam

Posted in Codare cu premeditare, Slagare internationale on August 7th, 2011 by Mihnea

Am mai mentionat faptul ca 3D Studio Max este scris de maimute cu sindromul Down. Ei bine, unul din maimutoii in cauza, pe numele sau de botez Cristofor (adica purtatorul lui Cristos), a decis ca el e Alesul ce va scoate lumea programarii din bezna si si-a inceput lucrarea de educare a maselor postind pe blogul pus la dispozitie de uzina-mama un fel de Philosophiæ Naturalis Principia Programatica. Ópusul are toate ingredientele necesare unei nominalizari la premiul Vocea Minerului: proverbe, indemnuri, acronime si link-uri la site-uri de agili. Singura problema este ca lucrarea este in primul rind una teoretica, adresindu-se exegetilor, deci cititorii de rind pot ramine nelamuriti cu privire la modul in care Principiile se aplica in practica. De aceea imi voi face datoria de umil scrib si voi ilustra opera de geniu cu extrase din rodul lumesc al trudei din Gradina Autodeskului.

Primele doua porunci vin la pachet, dupa cum aparent s-a prins si Cristi:

DRY – Don’t repeat yourself. – Probably the single most fundamental tenet in programming is to avoid repetition.

Abstraction Principle – Related to DRY is the abstraction principle “Each significant piece of functionality in a program should be implemented in just one place in the source code.”

Un prim exemplu al modului in care aceste principii pot transforma programatorul ordinar in Ahitect se gaseste in clasa Texmap. Texmap asta e o poza care in teorie are ca scop sa primeasca niste coordonate si sa returneze un pixel. In practica, maimutele au observat ca uneori texturile astea sint folosite pentru bump mapping, caz in care trebuie luate mai multe sample-uri si calculate niste chestii. In programele care nu au auzit de Principiile Lui Cristofor, calculul asta se face separat, independent de poza, caci e la fel indiferent ca poza e un checker procedural sau un bitmap. In Max, primele doua porunci se incarneaza in felul urmator in documentatie:

To understand how this is done, let’s look at some sample code. The method responsible for returning the perturbed normal is Texmap::EvalNormalPerturb(). The code below happens to be from \MAXSDK\SAMPLES\MATERIALS\CHECKER.CPP but all the other 2D textures use a similar approach.

Point3 Checker::EvalNormalPerturb(ShadeContext& sc)
{
	Point3 dPdu, dPdv;
	if (!sc.doMaps) return Point3(0,0,0);
	if (gbufID) sc.SetGBufferID(gbufID);
	uvGen->GetBumpDP(sc,dPdu,dPdv);
	Point2 dM = uvGen->EvalDeriv(sc,&mysamp);
	return dM.x*dPdu+dM.y*dPdv;
}

Sintagma “a similar approach” se refera la faptul ca bucata de cod de mai sus se gaseste in toate sample-urile lor, si ca tu trebuie sa faci acelasi copy/paste daca implementezi un Texmap d-asta, ca ei n-au fost in stare sa scrie o functie. DRY, da?

Urmatorul exemplu se gaseste intr-unul din fisierele mele preferate din Max: blizzard.cpp. Acest fisier care implementeaza un sistem de particule simplist in doar 9000 de linii de cod contine nenumarate exemple de DRY in interiorul sau – dupa cum va las placerea sa descoperiti – dar de asemenea in exterior, deoarece celelalte sisteme de particule (parray.cpp, pcloud.cpp etc.) sint implementate luind mari bucati din el si schimbind chestii in citeva locuri. Tot in dinsul gasim si urmatorul comentariu DRY-compliant:

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// WARNING - a copy of this class description is in maxscrpt\maxnode.cpp
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Am putea zabovi mult in zona asta a copypastei, dar haideti sa facem un efort si sa trecem la urmatorul triplet leninist:

KISS (Keep it simple, stupid!) – Simplicity (and avoiding complexity) should always be a key goal.

Avoid Creating a YAGNI (You aren’t going to need it) – You should try not to add functionality until you need it.

Do the simplest thing that could possibly work

In Maya, daca vrei ca un material facut de tine sa se picteze singur in viewport, trebuie sa implementezi o metoda numita glGeometry(). Daca vrei sa faci pe desteptul, mai sint vreo trei metode pe care le poti implementa ca sa suporti batching (adica sa te cheme intii ca sa-ti setezi state-ul, dupa care sa te cheme sa te desenezi pentru fiecare mesh, dupa care sa te cheme sa faci curat) dar sint optionale. Probabil va ginditi ca e bine asa, dar gresiti. Asta nu e KISS. Daca vreti sa vedeti KISS si YAGNI, trebuie sa va uitati cum sta treaba in Max.

Intii si-ntii, in Max trebuie sa derivezi din interfata IDX9VertexShader, care contine 18 functii virtuale pure, dintre care nu te intereseaza nici una. Te intereseaza functia care se cheama la fiecare frame, ca sa pictezi din ea, dar aia e intr-o clasa de baza a lui IDX9VertexShader, numita IVertexShader. Functia aia se cheama Initialize(). Nu glumesc. Aparent ar trebui sa faci doar initializari in ea si sa-l astepti pe Max sa incerce sa deseneze cu tine mai tirziu, dar pina si lor li s-a parut prea complicat, asa ca sample-urile din SDK deseneaza direct de acolo.

Dupa IDX9VertexShader vine IDX9PixelShader cu inca vreo 6 virtuale pure. Asta nu stiu la ce foloseste. Nu vad de ce i-ai povesti lui Max despre shaderele pe care le incarci, tu vrei doar sa te cheme sa te pictezi si sa-ti dea geometria. La fel, nici aia care au facut sample-urile n-au inteles, asa ca nu implementeaza deloc interfata asta. Mai departe vine IDX9DataBridge, cu inca vreo 5 virtuale pure pe care trebuie sa le implementezi ca {}. Preferatele mele sint GetDXVersion(), din care trebuie sa returnezi tot timpul 9.0 (ca float, FFS), deoarece clasa asta oricum merge doar cu DX9 (numindu-se ea IDX9DataBridge) si DisableUI() deasupra careia troneaza urmatorul comentariu:

//! This is no longer used
/*! \remarks Currently this method is not used\n\n
  */
virtual void DisableUI()=0;

Cel mai tare princpiu este insa asta:

Principle of least astonishment

In Max, atributele nodurilor se definesc cu ajutorul clasei ParamBlockDesc2. Constructorul acestei clase este:

ParamBlockDesc2(BlockID ID, MCHAR* int_name, StringResID local_name,
ClassDesc2* cd, USHORT flags, ...);

Alea 3 puncte de acolo deschid calea catre cel mai grotesc simulacru de domain-specific language pe care l-am vazut vreodata. Pentru fiecare atribut trebuie sa pasezi diverse tag-uri urmate de valori:

static ParamBlockDesc2 link_const_paramblk (
	link_const_params, _T("LinkConsParameters"),  0, &linkCD,
	P_AUTO_CONSTRUCT + P_AUTO_UI, LINKCTRL_PBLOCK_REF,
	IDD_LINK_PARAMS, IDS_AG_LINKPARAMS, BEGIN_EDIT_MOTION, 0,
	&linkConstDlgProc,
	link_target_list,  _T(""), TYPE_INODE_TAB, 0, P_VARIABLE_SIZE,
		IDS_LINK_LIST,
		p_accessor,		&link_const_accessor,
		end,

	link_key_mode, _T("key_mode"), TYPE_INT, 0, IDS_AG_LINK_KEY_MODE,
		p_default, 		0,
		p_range, 		0, 2,
		p_ui, 			TYPE_RADIO, 3, IDC_NO_KEY,
		IDC_KEY_NODES, IDC_KHIERARCHY,
		p_accessor,		&link_const_accessor,
		end,

	link_start_time, _T(""), TYPE_TIMEVALUE_TAB, 0, P_VARIABLE_SIZE,
		IDS_START_FRAME_LIST,
		p_accessor,		&link_const_accessor,
		end,
	// etc.

Toate cacaturile alea cu p_ in fata sint valori dintr-un enum al lor si trebuie sa fie urmate de parametrii propriu-zisi. Pentru un nod cu citeva zeci de atribute trebuie sa pasezi citeva sute de parametri catre constructorul asta. Nodul lor de color correction (maxsdk\samples\materials\color_correction.cpp) are vreo 65 de atribute, ceea ce rezulta in cam 800 de parametri pasati catre constructor. Least astonishment, da?

Si mai putin surprinzator este cit de robust e maretul sistem. Daca uiti un parametru undeva pe la mijloc, Max crapa la startup fara sa-ti spuna nimic (nici macar ce definitie l-a speriat). In exemplul de mai sus, daca uiti un parametru dupa p_range la link_key_mode, o sa creada ca maximul atributului este valoarea lui p_ui, dupa care va crede ca urmatorul tag e TYPE_RADIO si in functie de valoarea aluia va face lucruri “least astonishing”. Succes la debug.

Chiar daca reusesti sa ii descrii atributele fara sa crape, urmeaza un amuzament maxim cind vrei sa adaugi atribute intr-o versiune ulterioara a plug-in-ului. In primul rind, nu ai voie sa stergi nimic, pentru ca boul citeste valorile din fisiere si le potriveste cu definitiile pe baza indexului, nu dupa nume, cum fac alte programe scrise de fraieri care nu stiu Principii. Singurul lucru pe care poti sa-l faci este sa adaugi noi atribute la coada, dar si asta cu diverse restrictii. Daca nu-ti convine, au un mecanism extrem de complicat si fragil prin care te cheama la load time sa cirpesti datele din fisier, dar deoarece au aplicat KISS cu sfintenie, e inutilizabil.

Din pacate nu pot sa dau link aici la fisierul mesh.cpp pentru ca doar aia care sint registered developers au privilegiul de a se desfata cu continutul sau. Pot insa sa va spun ca in acel fisier se afla functia Mesh::render() in valoare de 3430 de linii. Acolo se vede cum se imbina armonios toate Principiile Lui Cristofor, mai ales ala cu “Write Code for the Maintainer“: goto-uri, nenumarate catch(…)-uri, if-uri cu cite 11 conditii urmate de else if-uri cu o parte din conditiile din primul if, dar negate si in alta ordine, instantiat obiecte COM prin al saptelea nivel de nesting de if-uri si switch-uri etc. Din cind in cind prin cod se gasesc comentarii confuze, unele semnate si datate, ca scrijeliturile de pe peretii celulelor de puscarie. Cele mai tari sint astea doua, anonime din pacate:

else {
    // (How can this happen?)
    delete pSelConv;
}

12 linii mai jos:

else {
    // How can this happen?  (I'm just copying above...)
    delete pNormalSpec;
}

In fine, nu mai stau sa iau si celelalte Principii la rind, ca m-am enervat si mi s-a facut greata de la atita rascolit prin gunoi. Ce vroiam sa spun e ca-mi bag pula in Cristofor si regulile lui. In loc sa puna mina sa stearga puroiul ala de soft si sa arda toate backup-urile surselor, el se preface ca tocmai a fost in excursie pe multele Sinai si ne blagosloveste pe noi cu zicatorile despre programare pe care le-a prins din zbor dind click-uri random pe wikipedia. Muie ba!

PS: nu pot incheia un post despre Max fara citeva citate din maxsdk\samples\mr-notes.txt:

note that these probably won’t map nicely…for example, mi_img_xxx functions will map somewhat to Bitmap and GBuffer functions, but probably not exactly

Norm — actually shader versioning was kinda a mystery to me. That is…miMTShader has get/setVersion, and this is used when generating the MI, but I can’t tell where the translator gets the version from…or does it?

What follows is a rough mapping of MR shader state variables to MAX class vars, functions, etc.
(Urmeaza o lista cu o coloana de nume de variabile din mental ray si o coloana numita “MAX Class or Function” care e complet goala.)

Tags: , , , , , , , , ,

Teorema stergerii cu rest

Posted in Codare cu premeditare, Slagare internationale on April 13th, 2011 by Mihnea

Am stabilit deja ca lista inlantuita e un subiect delicat. Este timpul sa aratam ca nu doar Ovidiu “MVP” Cucu are probleme cu dinsa, ci si aia carora el le pupa bombeurile au mici dificultati in intelegerea acestei structuri de date fenomenal de complicate. Sa urmarim pentru inceput un scurt material motivational in care se incearca stergerea unei configuratii dintr-un proiect in Visual Studio 2008 (nu stiu de ce se misca in reluare, muie youtube sau muie ffdshow):

Asta e, nu s-a sters. Lucrurile devin insa cu 57% mai interesante daca incercam sa stergem mai multe configuratii deodata:

Acum s-a reusit stergerea, dar ar fi fost un pic mai bine daca ar fi sters ce i-am zis sa stearga, nu ce doream sa pastram (de asemenea gasesc interesant cum in dropdown a ramas selectat “Debug2011”, desi cind il deschizi nu e acolo). Care sa fie explicatia?

Pai e destul de simplu, de fapt. Proiectul are doua configuratii care se cheama Debug5, doua Debug6 si asa mai departe, cite una pentru fiecare platforma (Win32 si x64). Acest lucru se poate observa cu ochiul liber in vcproj, unde nu exista Debug5, ci Debug5|Win32 si Debug5|x64. Mai departe in arhitectura lui VS, o configuratie trebuie sa existe in toate platformele – nu poti, de exemplu, sa ai Debug5 doar in Win32. Orice programator care a folosit vreodata platforme multiple in VC stie asta. Indianul care a implementat stergerea nu stie. Cind apesi pe “Remove”, el cauta prima configuratie care se cheama “Debug5” si o rade. In felul asta se sterge doar aia de Win32, dar cind deschide din nou dropdown-ul, o gaseste p-aia de x64 si o afiseaza acolo, iar acum proiectul e intr-o stare invalida, cu o configuratie care nu exista in toate platformele. Workaround-ul este sa stergi o configuratie, sa inchizi dialogul de edit, sa-l deschizi din nou si s-o stergi inca o data. In felul asta se sterge si Debug5|x64, si proiectul e utilizabil din nou.

Daca te aventurezi sa stergi mai multe configuratii de-odata, ca in al doilea filmulet, se evidentiaza o noua latura a retardarii indiene, aceasta ruda karmica a retardarii ardelene. Boul sterge configuratiile dupa index, nu dupa nume. Daca stersul propriu-zis i-ar fi mers, asta n-ar fi fost o problema. Din pacate insa, deoarece configuratia ramine acolo cind se manifesta prima parte a incompetentei, se fut maparile intre indecsi si nume, asa ca atunci cind dai sa stergi Debug6, se sterge de fapt altceva (jumate de altceva, mai exact). Mai departe retardarea 1 se compune cu retardarea 2 intr-un fel sublim, astfel incit tu dai sa stergi 6 din cele 8 configuratii, dar de fapt se sterg doar 3, printre care si singurele doua pe care vroiai de fapt sa le pastrezi.

VC are acest bug de la 2005, de cind a fost introdus noul configuration manager, cu platforme si cacat. De atunci au iesit 2005 SP1, 2008, 2008 SP1 plus o intreaga pleiada de hotfix-uri, dar nimeni nu s-a ostenit sa invete cum functioneaza de fapt configuratiile si cum se cauta intr-o lista. Bug-ul s-a rezolvat in sfirsit in 2010, pacat ca ala e inutilizabil gratie rescrierii editorului in dotniet (plus alte “goodies”, gen gunoiul ala de MSBuild).

Sa trecem acum in tabara adversa. Aparent exista o corelatie intre retardare si softurile de instalat alte softuri. Cel mai idiot lucru scos vreodata de Microsoft este MSI (chiar luind in considerare Songsmith si reclama pentru el). Cel mai idiot lucru scos de Apple este PackageMaker, echivalentul hipsteristic al lui MSI. Iata ce face PackageMaker cind vrei sa stergi un target din installer:

Pentru asta n-am o explicatie, caci nu inteleg cum functioneaza mintea oamenilor care programeaza pentru Apple. Cert e ca atunci cind stergi ceva, reuseste sa amestece restul target-urilor si chiar sa lase un fisier orfan, atasindu-l de root-ul proiectului. As dori sa mentionez ca in mod normal n-ai cum sa atasezi fisiere direct acolo, folosind UI-ul lui.

Workaround-ul este sa editezi proiectul de mina, caci este tinut in XML. In XML-uri, mai exact. Cite 2 XML-uri pentru fiecare target, plus un XML mare, to rule them all (in speta, 71 de fisiere in proiectul din film). Si aceste XML-uri sint scrise pe o singura linie, cum mentionam in post-ul despre XCode. Si numele lor conteaza, fiind prefixate cu un numar. Si alea in care zici ca vrei sa instaleze tot ce-i intr-un director contin si numele fisierelor din directorul ala, la momentul la care ai facut proiectul. Care nu folosesc la nimic, pentru ca daca adaugi un nou fisier intre timp, se va copia, asa cum iti doresti, dar nu va fi trecut in XML. Si asa mai departe, in pula mea.

Ca sa fiu perfect obiectiv ar trebui sa expun si o muie dintr-un IDE de Linux. Gluma asta se scrie singura, va las pe voi sa va imaginati ce vreti.

Oricum, ce vroiam sa spun e ca le doresc epidermoliza buloasa alora care-s responsabili de chestiile astea. Sau munca silnica pe viata in mina cu Silviu Ardelean ca team leader.

Update: am elucidat si misterul PackageMaker. Si aici retardarea este usor de inteles: gunoiul are un index.xml in care, odata ce-l formatam sa nu mai fie tot pe o singura linie, putem vedea chestii de genul:

<choice title="8.5 plug-in" id="choice210">
	<pkgref id="com.nextlimit.realflowPluginForMaya.realflow.pkg"/>
</choice>
<choice title="2008 plug-in" id="choice211">
	<pkgref id="com.nextlimit.realflowPluginForMaya.realflow-1.pkg"/>
</choice>
<choice title="2009 plug-in" id="choice212">
	<pkgref id="com.nextlimit.realflowPluginForMaya.realflow-2.pkg"/>
</choice>

Mai jos in fisier scrie si:

<item type="file">01realflow.xml</item>
<item type="file">02realflow.xml</item>
<item type="file">03realflow.xml</item>

Observam deja un design fabulos, caci corespondenta dintre “choice-urile” alea si fisierele in care se spune ce contin se face pe baza ordinii. Nu s-a putut pune nodul ala de item sub nodul de choice, sau ceva. Mai departe, daca privim in 01realflow.xml, vedem ca e scris package name ala, ba chiar are si un UUID dupa care ar putea fi identificat:

<pkgref spec="1.12" uuid="A23E47A9-3AB3-4619-847F-2104601981F9">
	<config>
		<identifier>com.nextlimit.realflowPluginForMaya.realflow.pkg</identifier>

Atingerea de geniu este ca muistul tine package name-urile alea acolo doar de decor. De fapt el se asteapta ca in primul fisier sa fie definit intotdeauna pachetul “com.nextlimit.realflowPluginForMaya.realflow.pkg”, in al doilea sa fie ala cu -1 in coada, in al treilea ala cu -2 etc. Nu conteaza ce scrie de fapt in fisier, iar UUID-ul ala nu e folosit la nimic.

Deci ce se intimpla cind dai click dreapta remove? Pai simplu, indianul care a implementat functia de sters nu stie ca numele sint hardcodate. El sterge nodul din XML, sperind ca potriveala se va face dupa nume. Din cauza ca se face dupa ordine, totul aluneca cu o pozitie in jos, deci pachetul de 2009 ajunge in choice-ul de 2008, ala de 2010 in choice-ul de 2009 etc. Primul pachet ramine orfan, iar ultimul choice ramine gol.

Solutia e sa stergi de mina XML-urile corespunzatoare target-urilor de care vrei sa scapi, dupa care sa iei la rind toate XML-urile ramase si sa cirpesti package name-urile alea, ca sa fie consecutive. Apropo, nu eu am dat numele alea care-s toate la fel, sint generate de el pe baza numelor fisierelor din target-uri, iar daca le schimbi se fute.

Uimitor. Fabulos. Nici la scoala ajutatoare nu vezi asemenea “design”. Ala care a facut cacatul asta n-a inteles nimic din programare.

Cum ziceam, epidermoliza buloasa.

Tags: , , , , , , , , , , , , , ,

Purely Fabricated Interview Expert Pattern

Posted in Slagare internationale, Stand-up philosophy on February 21st, 2011 by Mihnea

Am aflat de la Andrei Ignat ca Scott Hanselman a publicat o noua lista de intrebari de interviu pentru programatori. Cred ca Hanselman asta e un fel de Ovidiu Cucu international, ca vad ca tot publica liste d-astea. Ca orice programator care se respecta, si eu sint constient ca pot deveni somer peste noapte, deci fac eforturi mari sa ma tin la curent cu ultimele evolutii in domeniul intrebarilor de interviu. Atasez mai jos raspunsurile mele la intrebarile lui, pentru cei 3 cititori din totalul nostru de 5 care nu sint membri pe programare.org. Scuzati limba lu’ Shakespeares, am zis sa nu break character.

What is SOLID?

YABA.

Why are patterns important?

Patterns are important because people who cannot program must be able to project importance by wielding pompous words.

Who are the Gang of Four? Why should you care?

The Gang of Four, also known as The Alpha Wankers, are four non-programmers who understood what power words are and applied the concept to programming. I care because Aurelian Popa told me that today it is more important to be “social” than to know how to program, and using pretentious jargon like “singleton” instead of “global variable” is a guaranteed way of improving one’s social status. They are not to be confused with the Wang-Zhang-Jiang-Yao group, whose name they borrowed in a spectacularly ill-advised stab at self-irony.

Explain the concept of Separation of Concerns and it’s pros and cons.

Separation of Concerns means that when you concern yourself too much with design patterns, you can stop being concerned about grammar. No man can understand the singleton pattern AND remember the basic rules of languages with immensely complex grammars such as English, so once you embrace the teachings of the Gang of Four, grammar becomes Somebody Else’s Concern. The first sign of this transcendence is putting an apostrophe in the possessive pronoun “its”.

Discuss the concept of YAGNI and explain something you did recently that adhered to this practice.

YAGNI is something YAGN, as it’s related to XP.

Are you still writing code? Do you love it?

No, I’m too busy calling my global variables singletons and speaking at conferences about it.

What do you do to stay abreast of the latest technologies and tools?

Hahaha you said breast!

How do you react to people criticizing your code/documents?

I punch them, fire them and/or write inflammatory posts about them on forums or blogs.

Whose blogs or podcasts do you follow? Do you blog or podcast?

Are blogs and podcasts design patterns? Never heard of them. Anyway, I like the color fuchsia, I am an open-minded person focused on self-improvement and my hobbies include hiking, music, reading and taxidermy. I hope this helps you determine if I’m a good programmer or not.

What is the last programming book you read?

I only ever read one, “Design Patterns”. I memorized every word. I don’t need to know anything else.

What’s so great about <cool web technology of the day>?

<generic answer>

How can you stop your DBA from making off with a list of your users’ passwords?

I didn’t know such sexual deviationism is so common among DBAs and anyway, why is that my problem instead of HR’s? Oh wait, you said “making off”, not “making out”. My bad.

What do you do when you get stuck with a problem you can’t solve?

The Design Patterns book is a complete list of solutions which are just waiting for problems. Unenlightened people approach programming the wrong way: they see problems and look for solutions. I use the sure-fire method of picking solutions from The Book and inventing problems for them. That way, I’m never stuck. Any feature or system which cannot be implemented in this way is ill-defined and not worth my concern in the first place. See also the “Separation of Concern” principle.

What’s the difference between a web server, web farm and web garden?

Wait, what? Web garden?

When do you know your code is ready for production?

When I have at least 157 design patterns in it.

What’s YAGNI? Is this list of questions an example?

Didn’t you ask this already?

Noile mele cuvinte favorite: Information Expert (a nu se confunda cu Code Expert), Pure Fabrication, Protected Variations.

LE (adica Later Edit, Ovidiu): hahahaha am aflat ce-i ala web garden. Redefinirea cuvintelor limbii engleze, in pula mea.

LLE: GoF au incercat sa si cinte la un moment dat. Punk, pentru ca si la muzica se pricep la fel de bine ca la programare. Iata-i aici prezentind pattern-ul “Old Grey Whistle Test”:

Tags: , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,

Da-mi taticule si mie o lopata si-o mistrie

Posted in Slagare internationale, Stand-up philosophy on February 19th, 2011 by Mihnea

Cei ce trec prin viata doar in plimbare
Uita ca exista si penitenciare
Unde nu-i soare si nu creste-o floare
Si doare cind musti din covrig

Nenumarati contabili au flatulat de-a lungul timpului perspective numerologice asupra traiului de programator, de la regula 80/20, care a fost aplicata sistematic si contradictoriu tuturor perechilor posibile de cantitati numarabile (dar nu neaparat masurabile) din aceasta ingrata activitate, pina la transpiratul 10/50/30/10. Eu propun o simplificare a acestor dihotomii, triotomii si cvadruplotomii ciomuistice: programatorii isi petrec majoritatea timpului facind cacaturi care n-au absolut nici o legatura cu lucrul pe care vor sa-l programeze.

Cea mai de cacat situatie in care te poti gasi ca programator este atunci cind trebuie sa cooperezi cu niste cod facut de altcineva, adica intotdeauna. In general tu vrei sa faci o chestie clara, dar pina sa ajungi sa scrii alea 2 linii care te intereseaza pe tine trebuie sa te bati cu sute de muisme care iti stau in drum: compilatoare, IDE-uri, SDK-uri, framework-uri, biblioteci, OS-uri si asa mai departe. In loc sa te preocupi de ce face codul tau, te chinui sa intelegi care din mizeriile de lib-uri pe care le folosesti fragmenteaza memoria intr-un asemenea hal ca nu mai vrea OS-ul sa-ti dea 20 de amariti de megi de memorie, cind tu ai commit size pe la 700 MB.

Mai deunazi, in uzina la noi se barbota cu sirg la un plug-in de Max care trebuia sa coopereze cu un alt plug-in, ce nu era al nostru. Acest alt plug-in avea amabilitatea de a oferi chiar un SDK, adica niste headere cu nume de variabile din maxim 2 litere si fara documentatie, deci nu se intrevedeau incidente. Totul a mers ca uns pina in clipa in care am chemat o metoda numita “Eval” si am luat unresolved external.

Vedeti voi, plug-in-ul nostru nu poate fi linkat cu lib-urile celuilalt plug-in, caci trebuie sa se incarce si daca plug-in-ul alalalt nu exista. Daca punem o dependinta statica catre DLL-ul lor, al nostru nu mai merge decit daca e si al lor instalat. In concluzie, trebuie sa incarcam dinamic un pointer la metoda aia s-o apelam doar daca exista, lucru banal in teorie. Procesoarele, aceste chestii de care in zilele noastre sint mai preocupati gamerii decit programatorii, chiar au o instructiune pentru apelat functii, deci n-ar trebui sa existe articole pe blog-uri despre asta, nu? Ei bine, pina la CPU exista C++, compilator si Max, iar retardarile lor combinate dau o retardare mai mare decit suma partilor.

O metoda e o functie ordinara careia trebuie sa-i pasezi cumva si this-ul pe sub mina. In functie de platforma, dinsul trebuie sa fie pus intr-un registru sau pe stiva. Pe linga instructiunea de apelat functii, CPU-ul, acest lucru magic, are in mod surprinzator si instructiuni pentru pus chestii in registri sau pe stiva. Problema e cum il convingi pe compilator sa le genereze cind tu ai un pointer la functia aia – pe care l-ai luat cu GetProcAddress() – si pointerul la obiect.

Intr-o lume ideala s-ar face ceva de genul:

// In expozitiune:
#ifdef _WIN64
const char* methodName = "?Eval@ClasaMuista@@QEAAMM@Z";
#else
const char* methodName = "?Eval@ClasaMuista@@QAEMM@Z";
#endif
void* method = GetProcAddress(dll, methodName);

// In desfasurarea actiunii:
ClasaMuista* instPtr = ...;
float arg = 0.37f;
float retVal;
#ifdef _WIN64
__asm
{
	mov		rcx, instPtr
	movss	xmm1, arg
	call	method
	movss	retVal, xmm0
}
#else
__asm
{
	mov		ecx, instPtr
	push	arg
	call	method
	fst		retVal
}
#endif

Deoarece nu traim intr-o lume ideala, un mintos de la Microsoft care crede ca procesorul e o legenda urbana s-a decis ca Visual C++ nu va avea inline asm in 64-bit. La momentul la care a fost proclamata, aceasta prostie fenomenala a fost intimpinata cu urlete si mui de catre cei afectati, dar pe Microsoft i-a durut la penis. Motivul oficial este la fel de retardat ca decizia in sine: cica nu poti scrie acelasi asm pentru 32 de biti ca pentru 64. Si ce? D-aia avem ifdef, in mortii lui de ifdef. Nu inteleg ce ii fute pe ei grija ca eu trebuie sa fac gimnastica cu ifdef. Daca trebuie, o fac. Alternativele propuse de mintos sint ori sa te descurci cu intrinsics, ori sa scrii codul intr-un fisier separat si sa-l compilezi cu un assembler. Aia cu intrinsics nu se aplica aici, dar si in cazurile in care se aplica o mai da in bara, caci VC nu e tocmai Tata Lor™ la tradusul intrinsic-urilor in cod (ceea ce ma mira foarte, caci de pe banca de unde stau eu mi se pare destul de usor sa produci cod optim din asa ceva, sau cel putin mult mai usor decit alte optimizari pe care compilatorul lor le face bine). P-aia cu assembler-ul extern nu vreau s-o aplic pentru ca mi se pare insultator sa fiu nevoit sa instalez masm sau alta atrocitate ca sa chem o functie.

Un scurt moment am cochetat cu ideea de a face un const char* cu opcode-urile unei functii care pune argumentele unde trebuie si cheama cacatul de metoda, dupa care sa indrept un function pointer spre el si sa-l chem, da’ cirpeala e cam mare, asa ca am zis sa o luam totusi pe calea ortodoxa: facem un pointer la un member function si-l chemam p-ala. Adica:

// Expozitiune:
typedef float (ClasaMuista::*EvalFunc)(float);
union
{
	void*		voidPtr;
	EvalFunc	funcPtr;
} p;
p.voidPtr = GetProcAddress(dll, methodName);
EvalFunc method = p.funcPtr;

// Desfasurarea actiunii:
ClasaMuista* instPtr = ...;
float retVal = (instPtr->*method)(0.37f);

Observam tigania cu union-ul, care-i necesara pentru ca n-ai voie sa castezi void* la pointer la metoda. C++ e retardat si-ti permite sa derivezi din mai multe clase de-odata, caz in care pointerul ala trebuie sa mai contina niste cacat pentru ajustarea corespunzatoare a this-ului. Desi Java a transat problema asta acum vreo 15 ani (spre surprinderea tuturor, caci e singurul lucru facut bine in Java), C++ inca n-a inteles ca numai cretinii deriveaza din mai multe clase cu date in ele. Ba mai mult, ca sa le dea apa la moara cretinilor, ofera si virtual inheritance, acest goatse al OOP-ului care pretinde ca te scoate din cacat cind diagrama ta de clase seamana cu Coloana Infinitului, dar de fapt sileste compilatorul sa produca cod de o uritenie rar intilnita. In fine, C++ n-o sa primeasca interfete doar pentru ca ma oftic eu aici in fata poporului (la urma urmelor, se chinuie de vreo 12 ani sa-i creasca closures si inca nu e gata), dar in cazul de fata toate aceste griji pot fi date uitarii, caci clasa cu care trebuie sa cooperam nu e derivata din nimic, deci pointerii la metodele ei sint pointeri normali. Deci rulam codul si crapa. Cu WTF-ul pe buze, privim in disassembly si ne minunam:

000000013F191043  movss       xmm1,dword ptr [__real@3ebd70a4 (13F1921B4h)]
000000013F19104B  movsxd      rcx,dword ptr [rsp+28h]
000000013F191050  add         rcx,rbx ; Tu cine pula mea esti?
000000013F191053  call        rax

Ce o fi cu acea adunare de colea, ce seamana izbitor de tare cu o ajustare a this-ului pentru multiple inheritance? In acest moment, ne revine din negura vremurilor amintirea ca VC++ are un flag care-ti permite sa fortezi toti pointerii la metode sa foloseasca formatul extins, cu offseti si cacat. Cautam in MSDN si aflam ca flag-ul se cheama /vmg. Privim in project settings, dar nu-i. Mai privim odata in MSDN si observam ca exista si un pragma care face acelasi lucru, moment in care vedem rosu in fata ochilor.

In acest punct de turnura al naratiunii trebuie sa vorbim un pic despre 3D Studio Max si programatorii care lucreaza la el, caci poate nu-i un subiect familiar tuturor. La Max au lucrat de-a lungul timpului cei mai prosti programatori care au scris vreodata cod. Banuiala mea e ca singura cerinta de angajare la Discreet/Kinetix/Autodesk este sa fi lucrat in prealabil la Yahoo Messenger. Proportiile prostiei celor care au facut Max-ul nu se pot descrie in citeva rinduri, insa pentru a va forma o idee pot mentiona ca absolut toti cei care au pus vreodata mina pe SDK-ul de Max, indiferent de nivelul lor de pricepere, au sfirsit prin a dori moartea violenta a dobitocilor care au gindit si implementat acest program de cacat care nu mai moare odata. As dori sa va pot arata niste cod din matele Max-ului, dar cica n-am voie ca NDA si mui.

Cind am vazut ca exista acel pragma, m-am gindit instantaneu la MAXScript. Acest infect limbaj de scripting este facut de cineva cu stofa de Silviu Ardelean, deci am avut o banuiala ca pe undeva prin mecanismul de expus functii C++ in MAXScript voi gasi muia aia. Am dat cu pragma pointers_to_members in virful headerului precompilat, si prompt VC++ mi-a zis:

c:\program files\autodesk\3ds max 2011\maxsdk\include\maxscrpt\..\maxscript\macros\define_instantiation_functions.h(55) : error C2285: pointers to members representation has already been determined – pragma ignored

Muie Max. N-am rabdare sa vad de ce crede dinsul ca are nevoie de cacatul asta, dar va pot garanta ca e gresit. Sigur au futut ceva prin alta parte si au cirpit-o in felul asta. Din cauza imbecilitatii lor, eu nu pot sa chem un cacat de functie.

Ajungind aici, eu m-am enervat si am plecat acasa, dar jos8cal a mai dat un ochi in pagina aia din MSDN care zicea cum e cu pragma si /vmg si a vazut ca mai exista o cirja cu care se poate abuza limbajul: keyword-ul __single_inheritance. Chiar daca un retard ii spune compilatorului sa faca pointeri de cacat, poti reveni la pointeri ordinari pentru clasele care te intereseaza punind keyword-ul ala in declaratie. Deci l-a pus, si a mers. URA AM REUSIT SA CHEMAM O FUNCTIE!

Sau nu. A doua zi, a trebuit sa repetam manevra intr-un plug-in de Maya. Maya nu-i facut de handicapati, deci nu-i nevoie de pragme si keyword-uri si alte chestii d-astea. Din pacate insa, el ruleaza si pe OS-uri de hipsteri si comunisti, unde rolul Domnului Trandafir este jucat de GCC. GCC nu se osteneste sa faca pointerii la metode sa fie pointeri chiori in cazurile cu imbecilitate redusa; dinsul foloseste pointeri d-aia extinsi tot timpul si n-ai cum sa-l rogi sa faca exceptii. Deci ce cacat facem?

GCC are inline asm si in 32 de biti si in 64:

// Expozitiune:
void* method = dlsym(dll, "_ZN11ClasaMuista4EvalEf");

// Desfasurarea actiunii:
ClasaMuista* instPtr = ...;
float arg = 0.37f;
float retVal;

#ifdef __LP64__
__asm__ __volatile__(
	"call *%%rax"
	: "=Yz"(retVal)
	: "Yz"(arg), "D"(instPtr), "a"(method)
	: "memory"
);
#else
__asm__ __volatile__(
	"push (%2)\n"
	"push %3\n"
	"call *%%eax\n"
	: "=t"(retVal)
	: "a"(method), "r"(&arg), "r"(instPtr)
	: "memory"
);
#endif

Singura manevra aici este sa afli cum se cheama constraint-urile alea pentru template-urile de ASM pentru fiecare clasa de parametru care-ti trebuie. Daca il intrebi pe goagal de “gcc inline assembly constraints”, pagina relevanta (adica asta) nu e printre hit-uri (e o pagina care duce la o pagina care duce la asta, totusi). Sint alte pagini insa, scrise de firtati de-ai lui Silviu care simt nevoia sa se exprime desi habar n-au despre ce vorbesc, si care-ti spun, de exemplu, ca constraint-ul pentru rdi este “di”.

Trebuie notat totusi ca am avut bulan ca functia vrea un singur parametru de tip float. In Linux 64-bit, parametrii de tip float se dau in xmm0, xmm1 etc. Exista un constraint pentru xmm0 (numit intuitiv “Yz”, cum se vede mai sus), dar pentru restul nu mai exista (e doar “x”, care inseamna orice registru SSE, adica il lasi pe nenorocit sa aleaga). Ca sa-ti dai parametrii in cazul ala, ii pui in variabile, dai adresele variabilelor, faci lucru manual cu movss ca sa le incarci in registri si pui registrii respectivi in clobber list. Nu ca ce se intimpla deasupra ar fi optim sau ca ar conta, da’ totusi, obscen.

E de notat ca puteam folosi pointerul la metoda si in GCC, si in VC++ fara __single_inheritance, daca ne bateam capul sa scriem chestii in dinsul astfel incit sa faca call-ul care ne intereseaza (layout-ul e documentat pentru GCC si se gaseste pe net sau cu ochiu’ in dezasamblare pentru VC++). Mie asta mi se pare un hack si mai mare decit un const char* cu opcode-urile care fac ce doresc.

Dragi tovarasi, VICTORIE! Am reusit sa chemam o functie! Programarea e o meserie magica, plina de satisfactii, unde visele devin realitate prin simpla apasare a unor butoane!

PS: toata aceasta voma se putea evita daca aia care au facut celalalt plug-in se prindeau ca atunci cind vrei sa expui clase C++ dintr-un DLL, e o idee buna sa faci o interfata si s-o dai p-aia afara. Daca ClasaMuista::Eval() era virtuala, instanta aia de ClasaMuista pe care o primeam de la ei ar fi avut in ea un pointer la vtable, in care s-ar fi aflat si adresa functiei Eval() si ar fi mers totul ca prin minune. Runtime dispatch, chestii avansate nu gluma. Maxim 0.43% din totalul programatorilor de pe planeta asta sint in stare sa faca un API, dar din pacate un procent mult mai mare incearca.

Tags: , , , , , , , ,

XCode

Posted in Slagare internationale on February 17th, 2011 by Mihnea

Steve Jobs are cancer la pancreas deoarece o multime de programatori au vrut sa-si bage pulile in ficatul lui si unii din ei au nimerit pe linga. Probabil Steve nu e direct de vina pentru faptul ca XCode este un puroi revoltator, dar oftica e mult mai spornica atunci cind o poti canaliza in directia unei persoane si Steve e usor de nimerit in pozitia fruntasa in care s-a postat. Cancerul nu este total nejustificat insa, caci rolul de Führer al Nationalsozialistische Hipsterisch Arbeiterpartei ii confera lui Steve dreptul de a-i spinzura cu coarde de pian pe cei care au adus pe lume abominatia; faptul ca el nu-si exercita acest drept este o insulta la adresa intregii omeniri de la Charles Babbage incoace.

Pentru a intelege cum a putut lua nastere XCode, acest abject esec al spiritului uman, trebuie sa examinam intii contextul in care a fost construit el. Vom vedea ca soarta lui a fost pecetluita inca inainte de a se scrie prima linie de cod, dar ca maimutele care au lucrat la el nu s-au multumit sa mosteneasca imbecilitatile platformei pe care cladeau, ci au dus stafeta prostiei pe noi culmi.

Contextul lui XCode este OS X. In Finder (echivalentul hipsteristic al lui Windows Explorer, pentru neinitiati), tasta pentru a deschide un fisier selectat nu este Enter, ci Cmd+O. Enter face rename. Articolul asta s-ar putea incheia aici. Evident n-o sa se incheie, dar macar discutiile din magazinele Apple intre clientii potentiali si vinzatori ar trebui sa se sfirseasca in acest mod.

– Cum fac rename la un fisier?
– Apesi Enter.
– Muie.

Este clar de ce cred Apple ca au facut asta cu Cmd+O: pentru consistenta. In alte programe, “Open” este Cmd+O, deci sa fie si in Finder. Motivul real pentru care au facut asta este la fel de clar: pentru ca sint retardati. Pentru rename cu enter nici macar nu exista un motiv inchipuit, exista doar retardarea.

Fanboii vor spune ca asta cu tastele e doar o preconceptie a cuiva care vine dintr-un alt mediu. Pentru ei am exemplul redimensionarii. In OS X, ferestrele se pot redimensiona doar din coltul din dreapta-jos. Daca ai un geam in dreapta ecranului si vrei sa-l faci mai lat, intii trebuie sa-l tragi mai la stinga, dupa care sa-l maresti. In general n-o sa stii cit de mare vrei sa-l faci, deci n-o sa-l tragi suficient de mult in stinga si n-o sa ai loc sa-l faci cit de mare vrei, asa ca o sa-l mai tragi un pic, si o sa-l mai maresti un pic. Aparent, un test cu un focus grup a relevat faptul ca utilizatorii sufera un atac de panica daca ii lasi sa redimensioneze geamurile din orice colt. Eu cred ca de fapt testul a relevat surprinzatoarea implicatie ca daca faci focus grupuri cu retardati, ajungi la concluzii retardate.

In Biblia UI-ului Apple, unde regasim porunca asta cu strictetea redimensionarii, putem citi si citeva reguli de bun simt. Una din ele e sa nu-ti faci alfabetul tau din pictograme, pentru ca in general oamenii stiu deja un alfabet. Pictogramele trebuie folosite pentru grupat vizual chestiile similare, nu pentru a inlocui textul. Exceptie fac pictogramele pe care le pricepe orice om, gen sagetile, dar si alea trebuie folosite cu mare grija. In aroganta lor, Apple isi dau derogare de la regula asta si pentru mazgalelile de pe tastele lor speciale, caci daca nu vrei sa inveti ca semnul de puscarie e tasta de linga spatiu, nu meriti Lumina lui Steve. Scrie in pula mea “Alt+Cmd+Enter”, nu “juma’ de zvastica puscarie inapoi”, ca nu vreau sa invat ideograme hipsteristice doar ca sa pot sa joc Finder din taste. De asemenea, tastaturile Apple au ergonomia unei mine antipersonal, deci daca vrei sa-ti mai poti misca miinile dupa o zi de interactionat cu un Mac trebuie sa-ti iei o tastatura de oameni, care n-o sa aiba simbolurile cultului Apple pe tastele speciale si n-o sa poti sa te uiti sub degete sa vezi pe ce cacat de buton vrea sa apesi. Pentru avansati avem si o intrebare bonus: cum se numesc cele trei taste din poza urmatoare? Indiciu 1: nici macar pe tastaturile Apple nu sint. Indiciu 2: da, sint 3, muia aia din stinga care abia se vede asa apare in dialogurile de configurare.

Problemele astea sint minore, dar le-am mentionat pentru a putea zugravi o mentalitate. Apple cred ca orice iese din mina lor este perfectiunea intruchipata si o binecuvintare pentru intreaga suflare a planetei, asa ca vor tine cu dintii de orice idee, chiar daca se dovedeste a fi o imbecilitate. Mai mult, daca intrebi cum poti face o chestie care nu se poate face, nu-ti vor spune “nu se poate, sintem retardati”, ci vor incerca sa-ti explice ca de fapt nu vrei sa faci chestia aia. Nu vei putea redimensiona niciodata geamurile ca oamenii, din orice colt, pentru ca Apple stiu mai bine decit tine ca nu vrei sa faci asta, asa cum au stiut timp de 20 de ani ca mouse-ul trebuie sa aiba un singur buton. Heil Apple.

De la mentalitatea asta ajungem la problema editarii textului, care il afecteaza direct pe XCode. Prin 1986, IBM s-au prins ca oamenii folosesc tastatura pentru a scrie text si ca ar fi frumos daca ar pune pe ea niste butoane pentru chestii uzuale cum ar fi mers la inceputul rindului, la sfirsitul lui, o pagina in sus sau una in jos. Apple nu au fost de acord. Cine cacat vrea un buton de home? De la balconul Geamiei Perfectiunii si Adevarului Suprem, muezinii Apple le-au tot repetat credinciosilor: mouse-ul are un buton si n-aveti nevoie de home, end sau function keys. Pina la urma fatwa-ul impotriva butoanelor a fost ridicat si o vreme tastaturile Apple au avut butoane de home si end, dar tocmai a fost reinstituit, si ultimele instrumente de torturat degete marca Steve iar au pierdut butoanele astea eretice. Oricum, nici cind le aveau nu mergeau, pentru ca in dorinta lor de a fi altfel, Apple le-au pus sa te duca la inceputul si sfirsitul documentului, dar fara sa mute cursorul, ca asta sigur e o chestie care-ti trebuie foarte des (mult mai des decit sa duci cursorul la inceputul liniei). De asemenea, page up si page down muta doar view-ul, nu si cursorul, alta chestie teribil de utila (mai ales cind ai scroll wheel la mouse, o inventie recenta in lumea Apple, ce-i drept).

In concluzie, viteza cu care poti edita text in XCode l-ar face pe Gutenberg sa se sufoce de ris. Risul lui Gutenberg poate duce la cancer la pancreas pentru Steve prin mecanismul descris in introducere, avind in vedere ca programatorii tind sa-si petreaca majoritatea timpului scriind text. Sigur, te poti duce la inceputul rindului apasind Ctrl+A, ca pe vremea lui VT100, sau Cmd+Stinga, dar Apple n-au auzit nici macar de smart home, un feature care e in toate editoarele de text din ultimii 20 de ani. Ctrl+A si Cmd+Stinga te duc intotdeauna la prima coloana, nu schimba intre primul caracter din rind si prima coloana, ca la oameni. Au si Alt+Page Up / Alt+Page Down, care muta si cursorul (normal, doua taste pentru functia uzuala si una singura pentru aia inutila). Daca te simti aventuros poti chiar sa-ti redefinesti tastele ca-n jocuri si sa pui home sa faca home, doar ca sa-ti amplifici frustrarea in momentul in care trebuie sa faci ceva la calculatorul altcuiva. Cu putina vointa poti sa-ti cresti productivitatea pina undeva la nivelul lui notepad din Windows, moment in care intervin feature-urile avansate, alea care ne arata ca XCode stie ca nu-i un editor de text chior, ci un editor de cod.

XCode are code completion. Tasta cu care il activezi este Escape. Asta e si mai tare decit rename cu Enter. Escape, in pula mea. Bine, ca in practica nu conteaza, deoarece parserul de C++ e cam la nivelul pe care l-ai obtine daca i-ai cere celui mai prost student din grupa sa-ti faca un parser de C++ dupa ce a mers mahmur la prima ora a unui curs in care se mentioneaza tangential programele “flex” si “bison”. Code completion-ul intelege functii, metode si uneori nume de variabile, dar cam atit. Daca ii arati un template intra in fibrilatie. Totusi, in rarele ocazii cind incepi sa scrii numele unei functii si i se pare ca stie ce vrei sa scrii, iti sugereaza functia, cu tot cu parametrii sai:

Daca dai enter sa accepti sugestia, selecteaza definitia primului parametru, astfel incit s-o poti suprascrie cu ce vrei sa pasezi acolo. Urmatoarele defintii nu se mai selecteaza automat, deci trebuie sa le stergi de mina. Daca nu le stergi, ramin acolo in program si poti chiar sa salvezi textul in halul ala:

Daca te uiti in fisier dupa ce-l salvezi, observi urmatoarea aberatie:

int main()
{
    test(5, <#float y#>)
    return 0;
}

Ineficienta in editarea textului ar fi o problema daca ai putea sa ajungi la fisiere ca sa le editezi, ceea ce in general nu e cazul. Un nou focus grup compus din retardati i-a ajutat pe Apple sa decida ca schimbatul intre ferestre din taste este o alta mare sursa de confuzie pentru utilizatori. In OS X, tasta cu care schimbi intre geamurile aceleiasi aplicatii ia ferestrele la rind in ordinea in care au fost create, nu schimba ordinea in functie de ultima accesare, cum face Ctrl+Tab in Windows. Daca ai 10 fisiere deschise dar umbli in momentul de fata in doua, nu poti cicla intre ele cu o tasta. Daca apesi Cmd+`, vei trece de fiecare data prin toate cele 10 surse, plus celelate geamuri ale lui XCode care or mai fi deschise. Daca nu-ti convine asta, exista un mod in care toate sursele se deschid in acelasi geam, dar atunci nu poti sa ciclezi intre ele in nici un fel din taste. In cel mai bun caz iti pui o tasta care deschide dropdown-ul cu lista de fisiere active, dupa care il alegi cu sagetile p-ala pe care il vrei. Daca intrebi un fanboi Apple ce-i cu retardarea asta, iti va spune ori ca nu-i bine sa ai atitea fisiere deschise in acelasi timp, ori ca exista o tasta pentru schimbat intre doua fisiere .h si .cpp cu acelasi nume si e gresit sa vrei sa ciclezi intre doua fisiere care nu respecta regula asta. In orice caz, Apple stiu mai bine decit tine ce vrei sa programezi si cum.

Presupunind ca nu te deranjeaza sa tot duci mina de pe tastatura pe mouse ca sa schimbi intre ferestre de parca ai avea OCD si ti s-ar parea ca mouse-ul nu-i niciodata unde trebuie, tot vei avea o problema: ce geam sa alegi? XCode e un program saritor, care stie ca e bine sa-ti deschida acelasi fisier in cit mai multe geamuri de-odata. Recordul meu e de 5 geamuri diferite cu acelasi fisier, si l-am obtinut in felul urmator:

  1. Click pe fisier in lista din geamul principal al proiectului, se deschide in geamul ala.
  2. Dublu click pe fisier, vine si un geam separat cu el.
  3. Fa-l sa contina o eroare de compilare, da-i build. Da click pe iconu’ cu eroarea, vine un geam cu build status care va contine si el fisierul.
  4. Da find in files dupa ceva din fisierul ala. Click pe rezultat. Ai ghicit.
  5. Pune un breakpoint in fisier, deschide debugger-ul (care e un geam separat, dintr-un motiv ce-mi scapa) si fa-l sa se opreasca in breakpoint. Da, acum il ai si-n debugger.

Sint convins ca se poate depasi recordul, dar mi-e lene acum sa mai caut cacaturi prin meniuri. Mai interesant este sa ne aplecam un pic asupra debugger-ului, daca tot l-am mentionat. Fiind o companie care incearca din rasputeri sa fie altfel decit tot restul lumii doar de dragul de a fi altfel, era de asteptat ca Apple sa copieze ce fac altii fix in locurile in care nu trebuie. In speta, XCode trateaza problema debuggingului in acelasi mod retardat in care o abordeaza toate pocnitorile de IDE-uri facute pe genunchi in baie de adolescenti cuprinsi de febra Linux si open source: vorbeste cu GDB printr-un pipe. Prin viu grai. Ceva de genul write(gdb_pipe[0], “Draga gdb, poti te rog sa pui un breakpoint?”). Ca si in pocnitorile open source, schimbul de amabilitati se incheie abrupt cind gdb nu intelege sonetul expediat prin pipe, de exemplu atunci cind incerci sa faci step in printf().

Desi cu gdb se pot face multe lucruri, XCode e pe undeva pe la nivelul lui Borland C++ 1.0 ca feature-uri expuse. Tastele de step into/over nu merg in disassembly daca dai sa vezi combinat source cu disassembly. Nu exista set next statement (exista o consola unde poti vorbi direct cu gdb, insa, daca te simti poet). Exista un fel de edit & continue, dar merge doar pe programe didactice si oricum e destul de limitat fara set next statement. Daca ai un breakpoint si inserezi linii deasupra lui, uneori nu se muta mai jos, astfel incit sa ramina la codul la care era, ci ramine la linia lui, adica ti se va opri la alt cod. Daca ai un program mai complicat decit hello world, uneori se plictiseste de facut debug la el dupa citeva rulari, si nu se mai opreste in nici un breakpoint pina nu restartezi sistemul. Lista poate continua, dar cred ca s-a inteles cit de impecabil e debugger-ul. Probabil ca daca-i arati problemele unui fanboi, iti va spune sa scrii cod fara buguri, ca sa n-ai nevoie de debugger.

Totusi, ca sa ai la ce sa faci debug, trebuie sa poti intii sa-ti compilezi proiectul. Apple au vazut la Visual Studio manevra cu platforme, configuratii, Debug, Release, cacat, dar n-au inteles nimic. La ei nu poti avea doua target-uri cu configuratii diferite in acelasi proiect. Cretinatatea acestui fapt mi se pare colosala, caci nu-mi dau seama cit de retardat trebuie sa fii ca sa concepi sistemul in acest mod, mai ales cind exista alte IDE-uri care s-au lovit de aceeasi problema si au rezolvat-o deja. Raspunsul la intrebarea retorica este ca Apple au concluzionat ca configuratiile sint ca widget-ul de redimensionat geamuri: prea multe optiuni, prea mare confuzia in focus grup. Iar au stiut mai bine ca tine.

Sa presupunem ca facem doua plug-in-uri pentru doua aplicatii 3rd party. Fiecare plug-in suporta o serie de versiuni ale aplicatiei sale, si pentru fiecare versiune vom avea doua configuratii, Debug si Release. Primul plugin va avea Debug2008, Release2008, Debug2009, Release2009 etc. iar al doilea va avea Debug8.0, Release 8.0, Debug 9.0, Release 9.0 etc. Acum surpriza: facem si un lib cu care linkeaza ambele. Deoarece dezvoltarea lib-ului se intimpla concomitent cu dezvoltarea plug-in-urilor, vrem sa includem proiectul lib-ului in ambele solutii, ca asa ne-am obisnuit din Visual Studio, unde ai voie sa-ti setezi proiectele cum vrei tu, nu cum vrea Steve. Mai mult, lib-ul nu depinde de aplicatia-host, deci va avea doar doua configuratii: Debug si Release.

In primul rind, XCode nu are conceptul de proiect impartit intre doua solutii. Poti ori sa faci un proiect separat cu lib-ul si sa stai cu ambele proiecte deschise si sa dai build de mina in ambele cind schimbi ceva, ori sa faci proiectul lib-ului de doua ori, o data in fiecare proiect de plugin, cu toate fisierele si setarile si muile. Daca alegi metoda asta, vei remarca ca trebuie sa-i faci lib-ului aceleasi configuratii pe care le are plug-in-ul, pentru ca XCode nu vrea sa vada disensiuni intre target-urile aceluiasi proiect. Daca nici asta nu te convinge, o sa te rezolve faptul ca uneori oricum nu linkeaza cum trebuie, ca desi pui dependinte si cacaturi pe-acolo, nu se prinde tot timpul ca trebuie sa linkeze din nou plug-in-ul pentru ca s-a schimbat lib-ul.

Sistemul de muire a programatorilor din XCode are multiple nivele de fallback. Daca nu reuseste sa va futa cu cacaturile astea legate de configuratii si target-uri, tot va gasi un mod in care sa se asigure ca nu-l puteti folosi in nimic mai mare de hello world. De exemplu, daca va trece prin cap sa aveti un lib care se cheama altfel intre configuratii diferite (cacat_debug.lib si cacat_release.lib, sa zicem) si sa-l puneti ca dependinta la alt target din proiect, simpaticul XCode ii va trece numele curent in setarile de link ale target-ului care depinde de el. Cind schimbati configuratia activa, se schimba si numele ala, la linia corespunzatoare in fisierul cu proiectul. Daca dai commit asa si dupa aia da update un nefericit care are alta configuratie activa decit aveai tu, o sa iasa un conflict. Ca bonus, cind il rulezi in command line ca sa faci un batch build, nu-i in stare sa calculeze numele lib-ului asa cum o face cind e in GUI mode, asa ca o sa incerce sa-ti linkeze target-ul cu ce a ramas scris acolo ultima data cind ai inchis GUI-ul. Si in final, daca nici in cazuri d-astea nu va prinde, uneori se decide sa mute random chestii prin fisierul de proiect desi tu nu schimbi nimic cu mina ta, ca sa o puna totusi de un conflict cind dai urmatorul update.

XCode vine si cu o serie de unelte ajutatoare de o calitate la fel de suprema ca el. Preferatul meu este muismul ala de facut installere. Apple au decretat ca nu vor sa vada installere pentru aplicatii, ceea ce e un lucru bun (la ei aplicatiile vin ca niste arhive din care tragi fisierele afara si gata). Totusi, in unele cazuri chiar trebuie sa faci un installer, si atunci ai la dispozitie PackageMaker, cel mai bugos program pe care l-am vazut vreodata. Pe linga faptul ca periodic iti fute proiectul pentru ca asa i s-a sculat, si ca setarea proiectului in sine este un demers sisific, cel mai tare feature al acestui program mi se pare ca isi tine proiectul in format XML, dar XML-ul e tot pe o singura linie. Daca deschizi un proiect de installer, ii dai build si dai sa-l inchizi, iti va spune ca s-a schimbat. Daca faci timpenia sa-i zici sa salveze, in multe cazuri iti va distruge proiectul. Daca incerci sa dai un diff sa vezi ce-a facut, nu vei avea prea mare spor, fiind totul pe o linie. Noroc ca-i XML, acest format care poate fi citit si de oameni, si de masini, si de Silviu Ardelean.

Observ ca s-a facut tirziu si ca m-am intins un pic. As mai avea chestii de zis, dar ma voi screme inspre retorica finala. Inteleg ca pe Steve il doare la pancreas despre XCode si suferinta pe care o provoaca el programatorilor fortati sa faca aplicatii pe OS X. Nu inteleg totusi cum se face ca muistii care lucreaza la gunoiul asta nu au fost inca linsati de colegii lor, care trebuie sa-l foloseasca. Daca eu as fi programator pe plantatia de meri si as sti ca in cladire cu mine traiesc si defeca cod infectii care au abatut asupra mea cel mai rau intentionat IDE din istorie, s-ar lasa cu singe. Aia de la Apple ori au un IDE secret pe care-l folosesc in-house, ori isi cirpesc propriile solutii cu vim, emacs sau alte editoare preistorice (ceea ce nu e neaparat mai bine), ori spalarea pe creier aplicata personal de Steve fiecarui nou angajat chiar are randament 100%.

Tags: , , , , , , , , , , ,

Optimizing QuickSort and staff

Posted in Premiul n00bel, Slagare internationale, Stand-up philosophy on February 3rd, 2011 by jos8cal

Frunzaream recent revista celor de la codexpert.ro. Cum ce revista? Pai si-au lansat revista! E scrisa de un bot. In fine!

Evenimentul lansarii a fost unul sters, ca sa nu mai zic de lipsa totala a fundamentului ideologic. Dat fiind ca sintem familiari cu curentul codexpertian, ne-am zis sa incropim noi doua trei rinduri despre necesitatea si misiunea acestui ziar in lume, si nu numai. Asadar:

Pentru noi, cei care am supravietuit prigoanei, raspunderea pentru idealurile miscarii codexpertiste este atit de mare incit de multe ori simtim ca este o povara care ne copleseste si, daca n-ar fi legatura tainica cu C++-ul, poate demult s-ar fi stins nadejdea in sufletele noastre.

Profesorii Ardelean, Cucu si Bancila ne-au invatat sa gindim. Dupa cum Capitanul Bjarne a despartit hotarele lumii vechi de lumea noua, cei trei au despartit lumea formelor sterpe de lumea cugetarii realiste.

Dar cugetatorii si-au sacrificat darul mintii pentru adincul omenesc.

De cind au luat la cunostinta de C++, limbaj atit de apropiat de conceptia lor de viata, nu s-au putut desparti de drumul lui. Si si-au inceput viata intr-o apoteoza de neinchipuita frumusete morala. Ziarul trebuie sa pastreze nealterat spiritul miscarii si sa-l exprime in linia de cugetare a celor trei.

Un ziar de lupta si de idei, dar fara verbalism si entuziasm usor!
Fiecare cuvint trebuie sa se nasca din adincurile sufletului si sa fie insotit de intreaga raspundere a celui care il scrie.

“Ziarul Codexpert” este o batalie si pornim la ea cu aceeasi incredere in Victorie!

Acum ca avem si sprijinul ideologic, putem porni la drum sa ne scaldam printre titlurile care ne sint supuse atentiei in ziar.

Gasim o optimizare de quicksort. Gasim la autor si justificarea ideologica pentru batutul din taste:

“The idea for doing this is that instead of each new recursion copying the same code and using up more memory, it reuses the same code.”

Deja vu. Zbucium. Silviu Ardelean nu este un om, este o modalitate de a vedea lumea.

Ridicam bezmetici capul si privim codul in ochi. 10 numere sint torturate si fortate sa se alinieze de la mic la mare, in numele unei noi ideologii optime, acest pleonasm minier. Luam codul si-l virim sub nas, linie cu linie. Tragem incet si simtim cum ne ia cu ameteala. Trebuie sa gasim o modalitate de a salva numerele din mina ideologiei mirsave! Pregatim siringa pe care sta scris Array.sort() si batem incet vena. Milioane de numere ne trec prin fata ochilor. Eminescu. Truda. Pierdem vena. Intepam de 10 ori si ne intindem pe spate cu ochii tintiti la tavan. Senzatia de timp dispare. Ceasul numara de la kilometrul zero, over and over:

Optimized QuickSort: 00:00:01.2692034
Array sort: 00:00:01.1664805
Optimized QuickSort: 00:00:01.2490031
Array sort: 00:00:01.1632019
Optimized QuickSort: 00:00:01.2463744
Array sort: 00:00:01.1634480
Optimized QuickSort: 00:00:01.2695249
Array sort: 00:00:01.1568251
Optimized QuickSort: 00:00:01.2440373
Array sort: 00:00:01.2517662
Optimized QuickSort: 00:00:01.2694608
Array sort: 00:00:01.1581570
Optimized QuickSort: 00:00:01.2646042
Array sort: 00:00:01.1657108
Optimized QuickSort: 00:00:01.2499172
Array sort: 00:00:01.1557011
Optimized QuickSort: 00:00:01.2406120
Array sort: 00:00:01.1544470
Optimized QuickSort: 00:00:01.2434693
Array sort: 00:00:01.1576033

Tags: , , , , , , , , , , , , , ,

EXTRONUA. Origins.

Posted in Codare cu premeditare, Slagare internationale, Stand-up philosophy on December 21st, 2010 by jos8cal

Cartea de care va spuneam in postul trecut este pe punctul de a avea un coautor la categoria ASP.NET. Spionii nostri la fata locului au imortalizat momentul:

 

Tags: , , , , , , , , , , ,

Dilema

Posted in Slagare internationale on September 4th, 2010 by Mihnea

In articolul asta voi vorbi despre cea mai proasta echipa de programatori din lume, desi mi se pare foarte nedrept sa nominalizez una singura, cind sint atitia imbecili care ne fac viata mai grea in fiecare zi prin faptul ca nu le trece prin cap ideea sa-si puna streangul de git.

Daca ar fi pe categorii, mi-ar fi mai usor. De exemplu cel mai prost soft facut vreodata de Microsoft este MSI, sau Windows Installer, sau cum mortii lui il mai cheama acum. Asta nici n-ar trebui sa fie un soft. Pina si hipsterii aia labagii ai lu’ Steve Jobs s-au prins ca 99.98% din programe (procent determinat stiintific) au nevoie doar sa-si puna niste fisiere intr-un director. Solutia lor pentru cei cu nevoi speciale este o voma cu mai multe bug-uri decit linii de cod si cu o interfata care l-ar face pe Sisif sa se bucure ca sint altii si mai oropsiti ca el, dar nu conteaza pentru ca nu ajungi in fundatura aia decit in 0.02% din cazuri. Microsoft Installer e fix pe dos: pretinde ca face un miliard de cacaturi care nu-s necesare nimanui (si oricum nu merg), insa nu poti pur si simplu sa pui niste fisiere intr-un director si gata. A fost nevoie de un tip special de prost care sa conceapa un sistem in care ai nevoie de installer-ul original pe disc ca sa poti sa stergi un program instalat. In plus este ridicol de incet si inevitabil se fute la un moment dat, astfel incit exista o tona de tool-uri si blog-uri dedicate aducerii lui pe calea cea dreapta. Daca Aron Pumnu’ ala ar lucra la mine in firma si ar sta sa scrie pe blog despre cautat “return value 3” prin log-uri in loc sa puna mina sa repare muia aia de soft, as incepe sa-mi pun intrebari.

Sau na, daca am vorbi doar de IDE-uri, ar cistiga detasat puroiul de Xcode. Sau daca ne-ar interesa doar soft open source, am putea alege unul la intimplare. Sau daca ne-am uita la site-uri, ar fi twitter sau failbook. Sau daca am cauta prostie concentrata in chestii mici, l-am putea mentiona pe trogloditul care s-a gindit sa sparga calculatorul din Windows 7 in “moduri”, pisa-l-as in ochii lui de muist. Dar nu, aici caut un soft care sa transcenda categoriile, granitele, spatiul si timpul. Ceva de o prostie absoluta, care sa te faca sa-ti doresti sa reaprinzi cuptoarele si sa-i trimiti la dusuri pe toti cei care au lucrat la el.

Stiu ca unii se gindesc deja la 3D Studio Max, dar nu. Ca sa intelegi proportiile ororii intruchipate in Max trebuie sa fii programator, iar eu vreau ceva care nu tine cont de caste, virste si aptitudini.

Cel mai prost soft facut vreodata este Yahoo Messenger. Este incredibil cum un cacat de program de trimis text prin retea poate sa manince atitea resurse si sa aiba atitea bug-uri. Cum pula mea sa ai nevoie de 110 MB de RAM ca sa trimiti text? Altii s-au dus pe Luna cu 4 KB, adica de 28 de mii de ori mai putin. Cum sa te blochezi ma de fiecare data cind pica conexiunea si incerci sa te reconectezi? Cum sa-ti ia atita timp sa pornesti? Cum sa te blochezi cind se stinge calculatoru’ si astfel sa nu lasi calculatoru’ sa se stinga? Cum pula mea sa umbli la setarile globale de window animations cind cineva da minimize la un geam de-al tau? Luati fratilor, sa le trimita cineva link-ul asta, poate la versiunea 11 le iese.

Da, stiu ca pe linga text mai trimite si voice si video prin retea. Mare cacat. Tot n-ai cum sa ajungi la 110 MB de RAM. Photoshop, Starcraft si versiunile mai vechi de Maya incap in mai putin. Muie.

Daca proiectul asta ar fi fost facut pe rentacoder, ar fi costat 50 de dolari, ar fi durat 2 saptamini si probabil ar fi mers mai bine. Orice indian nespalat care maninca de pe jos si bea apa in care se pisa vacile comunitare l-ar fi facut mai bine. Dar nu, Yahoo a reusit cumva sa stringa atitia prosti intr-o singura echipa, incit prostia lor reunita a devenit ceva mai mult decit suma partilor, ca Capitanu’ Planeta.

Problema e ca daca stii sa programezi, nu te intereseaza sa lucrezi la programe de chat, la twitter sau alte mui. Daca loazele care fac astea s-ar fi nascut cu 10 ani mai devreme, ar fi muncit la state de plata in FoxPro si Paradox. Sau la Canal. Din nefericire, e nevoie si de programele astea, ceea ce creeaza o dilema: cum putem face ca si proiectele de cacat sa fie facute totusi de programatori competenti? Asta ar trebui sa-i preocupe pe toti cretinii aia care se duc la conferinte despre project management, nu cum sa pui cite doi prosti la un calculator, ca la cercul de informatica cind erau mai multi boraci decit HC-uri.

Poate ar merge o chestie gen munca in folosul comunitatii. Daca esti programator si o comiti, poti sa alegi intre amenda, puscarie, luat bataie de la mascati sau ce urmeaza sa ti se intimple si contribuit la un proiect de cacat. S-ar putea face un site unde proiectele de cacat isi prezinta nevoile si tu ti-ai alege. Evident, asta n-ar rezolva toate problemele, ca orice om intreg la cap ar prefera sa faca puscarie in aripa de violatori necrofili fani Mac decit sa incerce sa repare ceva in Max, dar rau n-are cum sa fie. Eu de exemplu as face un program de chat daca mi-ar da mai repede carnetu’ inapoi.

Tags: , , , , , , , , , , , , , ,

Despre alegeri

Posted in Slagare internationale, Stand-up philosophy on September 3rd, 2010 by Mihnea

Se facea ca era odata un exporter de geometrie din Max care folosea NVMeshMender ca sa calculeze tangent frame-urile, deoarece Max e vai steaua lui la acest capitol (ca si la toate celelalte capitole imaginabile). Nici biblioteca asta nu era tocmai un exemplu de robustete, caci din cind in cind ii mai scapau citiva indecsi inexistenti prin output, ceea ce ducea la crapatul lui Max si o stare generala de disconfort in rindul artistilor ce-si pierdeau astfel munca. Intra in scena T., programatorul ce a fost rugat sa verifice ce iese din MeshMender si sa anuleze exportul in caz ca o ia prin balarii, pentru a evita crapatul ulterior. Iata mesajul pe care T. s-a gindit sa-l arate utilizatorului cind problema era detectata:

if(MessageBox(NULL,
    "Error in MeshMender.\n"
    "To avoid crash press OK."
    "It'll not export proper mesh",
    "Prepare for crash", MB_YESNO | MB_ICONERROR) == IDOK)
        return;

Rar ne este dat sa vedem o bucata de cod care inglobeaza atitea principii de buna convietuire intre software si utilizator. In primul rind, dialogul ne intimpina cu un titlu primitor, menit sa ne asigure ca totul este sub control: Prepare for crash. Apoi, software-ul da dovada de flexibilitate maxima, intrebindu-ne daca am dori sa crape si sa ne distruga toata munca, sau nu. Mesajul in sine pare la inceput un pic jucaus si dornic sa stimuleze intuitia si inspiratia utilizatorului – lucruri indispensabile pentru un artist – spunindu-i acestuia sa apese OK daca nu e inca pregatit sa crape, dar afisind doar butoane de Yes si No. Noi insa, ce putem vedea dincolo de mesaj si direct in inima codului, observam ca T. chiar verifica daca utilizatorul a apasat OK, desi nu afiseaza acest buton. Cum utilizatorul nu poate apasa OK, exporter-ul crapa intotdeauna, indiferent de alegere.

T. este de fapt un abil invatator, ce ne poarta printr-o iluzie doar pentru a o strivi violent cind ne simtim mai bine, pentru ca socul sa ne deschida ochii: nu exista liber arbitru. Universul este ori determinist, ori aleatoriu si ambele variante exclud vointa si responsabilitatea morala. Nu exista alegeri, exista doar crash-uri.

Sublim.

Tags: , , , , , , , , , ,

Doctor Ciomu

Posted in Codare cu premeditare, Slagare internationale on August 31st, 2010 by Mihnea

E vara, e soare, forumurile sint goale, daca-mi permiteti sa parafrazez un curent ideologic din perioada pre-gregoriana a unui mare cintaret, producator si faraon. Pentru a umple golul lasat de plecarea in vacanta a expertilor m-am gindit sa-i dau cuvintul unui fost coleg, actual pericol public.

Intii si-ntii Peter ne arata ca pentru a accede in sferele superioare ale programarii, trebuie sa te lepezi de ultima expresie din instructiunea for:

for( nFold = 0; nFold++ < nFolders; )
{
    // ...
}

Dupa aceasta tura de incalzire, Peter ridica stacheta. Ca un adevarat Bryukhonenko, el decide sa amputeze si corpul for-ului. Fierastraul se prezinta sub forma operatorului virgula, acest luceafar calauzitor pentru programatorii ce se aventureaza pe drumul conciziei:

for(int x=0; s[x++]=c, Data>>c, !Data.eof() && x<100; ) ;

Scoala Ardeleana ne invata sa folosim virgula cit mai des, mai ales intre subiect si predicat. Peter e de acord. Cind nu putem elimina corpul unui for cu ea, putem macar sa scapam de acolade:

while( x < m_cGrid.GetCols() &&
  nXoff + (nW=m_cGrid[x].Width()) < nCx-3 )
    nXoff += nW,
    iB++,
    x++;

Nici subiectul std::map nu-i este strain lui Peter:

std::map<uint,identitylist_t>::iterator iT =
  m_cStatic.m_cTemplate.find(Type);
identitylist_t& TypeList =
  iT == m_cStatic.m_cTemplate.end() ?
  m_cStatic.m_cTemplate[Type] : iT->second;

Mai departe eroul nostru transeaza o dilema cu care s-a confruntat mai demult si Ovidiu “MVP” Cucu:

int HandlerView::GetNodeIcon( TreeNode* pcNode ) const {
  //! isequal returns 0; is 5, 1 is 2, 2 is 7
  return "\5\2\7"[pcNode->IsEqual2Head()];
}

In urmatorul fragment se introduce un concept revolutionar: crapatul pentru pastrarea datelor:

CString cS;
cS.Format(
  "Error in reading Directory <>,\n"
  "Database may be corrupt.\n"
  "Tide will now crash to preserve data.", GetName() );
AfxMessageBox( cS );
throw;

Peter poarta astazi titlul de “software architect” in firma in care lucreaza (care face soft pentru platforme petroliere). Se impune deci sa dam un exemplu de arhitectura conceputa de el, in care clasa de baza apeleaza explicit metode dintr-o clasa derivata:

class ResourceData_t {
    // ...
    static int GetBackupMode();
};

class SecureResource_t : public ResourceData_t{
    // ...
    static BackupMode_e GetBackupMode();
};

int ResourceData_t::GetBackupMode(){
  return SecureResource_t::GetBackupMode();
}

Inchei acest necrolog cu goatse-ul for-urilor:

for( uchar*p = m_pData; !++*--p; );
Tags: , , , , , , , , , , , ,