mercredi, septembre 19, 2007

Message d'horreur

J'ai choisi C++ comme langage pour mes bricoles sur la console Nintendo. Principalement parce que c'est un langage que je ne maitrise pas, en fait. J'aime bien le côté "orienté objet" de la programmation: je trouve ça plus propre. La plupart de mes lignes de codes ont d'abord été cogitées sur papier en UML et petit à petit, je commence à m'y retrouver dans les std:: ...

Mais franchement, je me demande à quoi pensait Bjarne Stroustrup quand il a inventé ce langage. J'aurais tendance à proposer une règle d'or pour la programmation: ne jamais imposer au programmeur d'écrire deux fois quelque-chose à des endroits différents. C'est systématiquement dans ces cas-là qu'on se retrouve à modifier l'un en oubliant l'autre.

I chose C++ to implement my homebrew editors and game engines on NintendoDS, mostly because i'm curious of gaining some experience in that language, and of course, because i tend to think in OO whatever the language. If i was to design a language, my golden rule would be "never require the same thing to be written twice, for coders are lazy and it would produce bugs". It seems that C++ is the exact opposite. Sometimes, it's even worse: you have to declare all your variables in your class "header", but you may not initialize them there. You can give default values in the method prototypes, but not in their declaration. Etc.

Eh bien, C++ donne l'impression d'être construit sur le principe de faire faire systématiquement l'inverse! Pire, une partie de l'information ne peut apparaître que dans une des copies. Résultat: vous devez déclarer vos variables, mais vous ne pouvez pas en profiter pour les initialiser à ce moment-là! Vous pouvez aussi donner des valeurs par défaut à vos arguments, mais attention! pas dans le fichier qui contient le corps de la fonction.

Bref, c'est l'horreur. Et comme si ça ne suffisait pas, le compilateur en rajoute une couche en pondant des messages particulièrement impigeables (du moins, pas avant une longue discussion avec Candy sur ICQ ;)

Je vous le donne en mille:

SpriteSet.h: In copy constructor 'SpritePage::SpritePage(const SpritePage&)':
SpriteSet.h:47: error: invalid conversion from 'const SpritePage*' to 'unsigned int'
SpriteSet.h:47: error: initializing argument 1 of 'std::vector<_tp,>::vector(size_t, const _Tp&, const _Alloc&) [with _Tp = short unsigned int, _Alloc = std::allocator]'
Déjà, les constructeurs de copie sont particulièrement pénibles à manoeuvrer, mais là, j'avais essayé d'en faire un pour une clase qui étend le "vecteur standard". Résultat, un message complètement opaque qui me donne tellement de détails que je n'en vois même plus le message principal.

Unfortunately, the compiler's error messages are cryptic at best. Hopefully enough, a friend of mine (Candy) was here to help with copy constructors ... Or maybe i shouldn't have tried to extend std::vector before i'm grown up to the wizard++ rank ?
main.cpp:485: undefined reference to `vtable for WConsumer'
main.cpp:485: more undefined references to `vtable for WConsumer' follow
(.rodata._ZTV12DumbConsumer[vtable for DumbConsumer]+0x14): undefined reference to `WConsumer::setFile(char*)'
collect2: ld returned 1 exit status
Là c'est encore mieux: les probèmes de fonctions virtuelles. Ici, vous avez la version "simple", où le compilateur détecte qu'il me manque une fonction "setFile" déclarée dans l'interface WConsumer, mais pas implémentée par DumbConsumer (un effet du refactory ;P). Dans sa version plus perverse, le message d'horreur se limite à répéter "undefined reference to 'vtable for WConsumer'" sans rien ajouter d'autre. Bin si c'est le cas, et si WConsumer était supposé être une interface, courrez bien vite vous assurer que toutes vos fonctions virtuelles sont assorties d'un "=0" dans la déclaration de la classe (qui devient du coup une classe virtuelle pure).

Another one that gave me headaches: troubles with virtual methods. Here, you're facing the "simplest" case, where the compiler detects i'm missing the "setFile" function of the WConsumer interface in the DumbConsumer implementation. In its 'advanced' evolution, this horror message just repeats "undefined reference to 'vtable for WConsumer'" without any additional information. If that's the case, and if WConsumer is supposed to be an interface (i guess i've got quite some experience with Java, after all), hurry up to your .h file and double-check you added "=0" to all your pure virtual methods to make your class a pure virtual class.

You guess'd it: i'm feeling like Alice in the rabbit's hole ... completely lost when the Mad Hatter throws at me "Moreover, if you want things to get right, you should declare your destructor to be virtual".

Bref, je suis comme alice dans le terrier du lapin, complètement paumé lorsque le chapelier fou me lache innocemment que "moreover, if you want things to get right, you should declare your destructor to be virtual"
"Alice: My what to be what !?"

2 commentaires:

cyborgjeff a dit…

Au pays.. tagadam tagadam
De Candy.. Tagadam Tagadam
Comme dans tous les pays...

sylvainulg a dit…

ouaip. je ne m'attendais pas à ce que ce soit un post "grand public", évidemment. Mais vu le nombre d'étudiants qui m'ont déjà cassé les pieds pour faire leurs TP en C++ plutôt qu'en C, il fallait que j'en parle :P