Interrupted System Call
Hier j'essayais de terminer un petit player mp3 sur nos appareils eInk, mais au démarrage du mp3 ça quittait toujours brusquement. Le code récupéré des taiwanais utilise libmad, et pour faire simple, le principe est le suivi :
- On ouvre un flux sur "/dev/dsp"
- On le configure
- Libmad décompresse le mp3 petit à petit, et à chaque morceau décompressé on le balance dans /dev/dsp
- On referme le flux quand on a fini...
Parfois ça quittait dès le départ, et parfois on avait genre 1/4 de seconde du mp3 avant que ça ne quitte... On a rapidement repéré à quel endroit ça quittait : au moment d'écrire dans /dev/dsp (par blocs de 8192 octets), ça s'arrêtait très rapidement avec un message d'erreur : "Interrupted System Call".
Alors, à quoi correspond ce message ? En fait c'est tout simple : on a fait un appel système (ici, l'écriture dans /dev/dsp) qui a été interrompu par un signal... L'écriture dans /dev/dsp n'est en fait pas une opération atomique, donc on n'a aucune garantie qu'elle puisse se dérouler complètement en une seule fois.
La solution a mettre en place est assez basique : au lieu de quitter à la moindre interruption, on note combien d'octets ont déjà été écrits, on incrémente le pointeur d'autant, on décrémente la taille à écrire, et on relance le fwrite... Et ça marche !
Merci au site suivant pour la petite explication claire et précise : http://book.chinaunix.net/special/ebook/addisonWesley/APUE2/0201433079/ch10lev1sec5.html
Note au passage : c'est marrant de voir comment tout est flux sur Unix... Du coup, pour jouer un son, c'est aussi un simple flux dans lequel on envoie les données du son à jouer. On peut même faire un "cat toto.wav > /dev/dsp" et entendre son wav :p
Call Stack en C/C++
Ces derniers temps je me suis attaché au portage d'FBReader sur différentes plateformes, et comme c'est un code assez conséquent et où j'avais besoin de voir un peu quel appel de fonction arrivait d'où (autrement que par le "find reference" d'Eclipse), j'ai cherché rapidement s'il n'y avait pas un moyen en C d'afficher des stack trace. A ma grande surprise, c'est possible !
Voici donc une petite fonction qui peut se révéler utile lorsqu'on fait du debug
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
/* Obtain a backtrace and print it to stdout. */
void
print_trace (void)
{
void *array[10];
size_t size;
char **strings;
size_t i;
size = backtrace (array, 10);
strings = backtrace_symbols (array, size);
printf ("Obtained %zd stack frames.\n", size);
for (i = 0; i < size; i++)
printf ("%s\n", strings[i]);
free (strings);
}
Ca m'a bien dépanné, et même si je ne m'en suis pas tellement servi au final (juste une fois), je pense que ça fait parti des petits bouts de code toujours utiles à avoir sous le coude...
Pour plus de détails, je vous invite à vous rendre sur le site de GNU.
Mandelbrot en parallèle
Aujourd'hui à la fac, c'était programmation parallèle d'un Mandelbrot. C'est du fractal, et ça se prête très bien à de la programmation parallèle, puisqu'en fait on itère N fois sur chaque pixel, indépendamment des voisins, pour savoir si on doit l'afficher ou pas (et avec quelle couleur).

La conception du code s'est faite en plusieurs phases :
1. Version séquentielle
Exécution du code en version séquentielle, histoire de voir le temps que ça prend et d'avoir un résultat qui puisse servir d'étalon... Résultat : entre 7 et 8 secondes pour générer l'image avec les paramètres par défaut.
2. Version parallèle, statique.
Réalisation d'une version pouvant tourner en parallèle avec N processus. La méthode est relativement simple et naïve : au démarrage de chaque processus, on récupère le numéro du processus (de 0 à N-1), ainsi que le nombre total de processus lancés. A partir de là, on découpe l'image en N bandes (on garde toute la largeur, on découpe en hauteur), chaque processus calcule sa portion, puis tout le monde envoie la portion calculée au processus 0, qui enregistre l'image finale (et affiche le temps total).
Et là, surprise, même avec 8 ou 10 processus, le gain est assez faible : on tourne en 2.5 à 3.5 secondes, soit à peine 2 à 3 fois plus vite (alors qu'on a pourtant 4 à 5 fois plus de puissance, normalement !).
La raison est en fait rapidement trouvée : les lignes du milieu mettent beaucoup plus de temps à calculer que les lignes aux extrémités (en fait tout ce qui est noir sur l'image a mis longtemps à être calculé). Du coup, les processus qui se tapent les bords en haut et en bas finissent super vite (en 0.05 secondes !), alors que les processus qui ont le milieu à calculer mettent plus de 2 secondes... C'est du beau gachi de puissance, il convient donc de concevoir un truc un peu plus dynamique qui puisse répartir au mieux la charge...
3. Version parallèle, mais dynamique !
La dernière version à être réalisée va donc avoir une approche beaucoup moins statique : le processus 0 ne va rien calculer, mais va en fait servir de chef d'orchestre, et va dire aux autres quoi calculer. A chaque fois qu'un processus a fini de calculer une portion de l'image, il l'envoie au processus 0, qui va ensuite lui redonner un ensemble de ligne à calculer.
Cette fois-ci, on va donc envoyer aux processus des blocs de 1 à 8 lignes à calculer (limites arbitraires et qu'on peut changer pour optimiser les choses), et donc au pire si un processus met beaucoup de temps pour calculer les 8 lignes du milieu, pendant ce temps les autres processus calculeront peut-être 32 lignes ou plus, puisque la charge est répartie dynamiquement !
Et là, le résultat est sans appel, c'est beaucoup mieux : on arrive sous la barre de la seconde, dès qu'on a 8 processus ou plus ! Et pourtant, le processus 0 ne calcule rien, il ne fait que récupérer le résultat des autres :p On a donc à présent un système qui tient bien la charge, qui peut supporter un grand nombre de processus, et qui s'adapte aux besoins. On peut même avoir un parc hétérogène de machines, ce n'est plus gênant.
4. Pour aller plus loin...
On peut à présent réfléchir à des optimisations pour ce système.
En fait, la faille est que cette fois-ci on passe pas mal de temps dans les communications à cause des transferts de taille relativement faibles. De plus, si tous les processus finissent de calculer à peu près en même temps, alors ils vont tous devoir attendre que le processus 0 ait reçu les messages pour continuer, donc on a tout de même une partie du temps qui est perdu en communication et surtout en attente de communication. Plusieurs stratégies peuvent permettre de limiter ce temps, en voici quelques une en vrac :
- Faire des paquets plus gros : si on fait des paquets plus gros, on fera au total moins de communications, donc en théorie on devrait aller plus vite. Sauf qu'on s'expose au risque d'avoir à attendre à la fin un processus trop lent qui aurait eu un gros bloc à finir... On pourrait donc imaginer faire calculer des blocs de grande taille au début, et réduire la taille et fur et à mesure de l'avancement. Comme ça, à la fin, tout le monde aura de petites taches, et devrait finir sensiblement en même temps...
- Permettre au processus 0 de travailler ! Pourquoi devrait-il perdre son temps à attendre les autres ? En fait, il pourrait s'allouer une ligne à calculer, mais regarder tous les X pixels s'il n'a pas une communication en attente. Comme ça, il participe aussi au calcul, et on devrait gagner un peu (mais plus on aura de processus, moins ça sera intéressant, car plus il devra communiquer de toute façon)
- Dans la version actuelle, les communications sont synchrones : quand on demande à avoir des lignes à calculer, on est bloqué en attendant que le processus maître réponde... Une meilleure stratégie pourrait être d'envoyer la demande de façon asynchrone (ie, non bloquante) quand on arrive au milieu du calcul, de façon à avoir la réponse sous le coude à la fin du calcul. Comme ça, dès que c'est fini, on a de fortes chances d'avoir le message près en réception. Dans le même genre, on pourrait imaginer se voir confier 2 taches à la fois par le processus maître, et donc quand on fini la première on en redemande une, etc... Je pense qu'au final ça revient sensiblement au même, à voir.
- Mieux répartir la charge dès le départ. Cette stratégie est celle préconisée par Mathias, tout le mérite lui en revient ^^ En gros, on peut imaginer calculer au début une version simplifiée (calculée plus rapidement, ou alors plus petite) afin de localiser plus rapidement les zones noires, et donc dès le départ faire des paquets de taille variable selon la charge de travail estimée (avec les options par défaut, le temps pour traiter un pixel varie d'un facteur 10 000 entre les pixels les plus rapides à calculer et les plus longs). Au lieu d'envoyer des paquets de taille relativement fixe (et donc représentant une charge de travail très variable), on peut alors envoyer des paquets dont les charges effectives sont proches.
- Tenter de répartir un peu mieux les temps de communications entre les différents processus : là le facteur ralentissant est que tout le monde communique avec le processus 0. On pourrait imaginer sous-découper les processus et l'image en 2 groupes (voire plus), leur faire se débrouiller avec ça, et puis à la fin rassembler les lignes calculées. On limite alors les communications car 2 processus (voire plus) vont centraliser les appels. Dans cette optique, on pourrait avoir une partie des processus qui remplissent l'image par le haut, et l'autre partie par le bas : quand les 2 groupes se sont rejoints, on s'arrête !
Et alors ?
Bah rien ^^ J'ai trouvé ça sympa comme sujet, donc je me suis dit que j'en ferai un billet :p Bon, c'est sûr que c'est plus facile de faire ce genre de chose à la fac où on peut monopoliser 16 PCs en même temps (dual core, donc potentiellement 32 processus à pleine vitesse), alors que chez soi on a bien moins de matériel... Je devrais peut-être économiser pour installer une ferme de PCs chez moi en fait :p
Non, il n’y a pas de comparaisons ternaires en C !
Un jour en TP à la fac un autre étudiant me demandait pourquoi je tapais le code suivant :
if ((0 < x) && (x < 2)){
...
}
Personnellement, ça ne me parait pas choquant... Mais lui me soutenait qu'en C on peut faire beaucoup plus "joli", avec le code suivant, qui parait effectivement plus naturel :
if (0 < x < 2){
...
}
Bon, je dirais, pourquoi pas... Après tout, ça compile, donc comme il semble le penser, si ça compile, c'est que ça marche ! Mais qu'en est-il en réalité ?
En pratique, l'opérateur de comparaison '<' (ainsi que tous ses acolytes) est un opérateur binaire, qui ne prend donc que 2 opérandes. Alors comment fonctionne cette forme avec 2 opérateurs et 3 opérandes ?
Petit quiz express : qui renvoie le code suivant ?
printf("%i\n", (0 < 3 < 2));
Si vous avez répondu '0', vous avez perdu ! En effet, ça renvoie '1'. En fait, on pourrait changer le 3 par n'importe quelle valeur, on aurait toujours 1. Une fois qu'on a compris que cet opérateur est binaire, et qu'on applique donc le parenthésage par défaut défaut, on voit clairement à quoi correspond ce code :
(0 < 3) < 2
Si ce code compile, c'est uniquement parce qu'on peut effectivement l'évaluer "correctement". (0 < 3) donne un booléen, mais en C un booléen est juste un entier ayant pour valeurs 0 ou 1, donc on peut comparer ce booléen de la même manière avec 2. Or, (0 < 2) et (1 < 2) renvoient toujours "1", quoi qu'il arrive. Donc ce test est juste foireux.
Ceci fait ressortir à nouveau l'importance de mettre des parenthèses, mais aussi les limites du typage en C : comme tout est un peu du même type (tout est plus ou moins entier, sauf les pointeurs, mais si on veut on peut les caster de toute façon), on peut écrire des opérations qui sont parfaitement fausses. Le même code en Java ne compilerait a priori pas (je n'ai pas testé), puisque la comparaison entre un booléen et un entier n'est pas autorisée.
Une fois encore, il ne faut surtout pas partir du principe que ça fonctionne si on n'a pas vérifié que le langage le supporte ! Et une compilation réussie ne garanti en aucun cas que le code va faire ce qu'on pense qu'il va faire !
Importance des parenthèses
J'ai déjà eu le cas d'un programmeur qui ne mettait quasiment jamais de parenthèses, genre "ça sert à rien". Un if peut rapidement avoir une tête du style :
if (a == 2 && b == 3 && c == 4 && ... && z == -2)
Je trouve ça relativement peu lisible, au final, et je rajoute toujours les parenthèses qui vont bien. Voici donc un petit code intéressant pour illustrer les soucis potentiels :
printf("%i %i %i\n", (-2)&0xFF, 254, (-2)&0xFF == 254);
Alors... A votre avis, qu'est-ce qui va être affiché ?
- (-2)&0xFF affiche 254
- 254 affiche... 254 (ok, c'était facile)
- (-2)&0xFF == 254 affiche... 0 !
Et oui, on pourrait s'attendre à ce que ça affiche 1 (puisque (-2)&0xFF vaut 254), mais en pratique ce n'est pas le cas. Alors pourquoi ?
Tout simplement parce que ce qu'on obtient comme résultat est en fait le résultat de (-2) & (0xFF == 254). Comme 0xFF vaut 255, (0xFF == 254) vaut 0, et donc (-2) & 0 vaut 0... Dommage !
En fait, il vaut mieux mettre des parenthèses inutiles que de ne pas en mettre du tout... Car même avec trop de parenthèses, on sait ce qu'on va obtenir, alors que si on les retire cela signifie qu'on se repose complètement sur la priorité décidée par le compilo (ou plutôt la norme). Pas forcémnet une bonne chose à faire si on ne maitrise pas parfaitement le sujet !
Pour rebondir un peu sur ce cas (qui parait un peu improbable à cause du -2 qui traine), voici un que j'ai construit et qui renvoie toujours TRUE :
1&0xFF == 255
Qu’affiche le code suivant ?
Sous ce titre un peu racoleur se cache pourtant un phénomène assez étrange, qui fait qu'en C il n'est pas toujours facile de déterminer le résultat d'une instruction...
Voici donc un petit programme rapide, de quelques lignes ; je vous invite à essayer de deviner ce qu'il affiche...
#include <stdio.h>
int main(int argc, char **argv){
int w = 0, x = 0, y = 0, z = 0;
printf("w : %i %i\n", w++, w);
printf("x : %i %i\n", x++, x+1);
printf("y : %i %i\n", y, y++);
printf("z : %i %i\n", z+1, z++);
return 0;
}
Ca va paraître bizarre, mais en compilant le code sous OSX avec GCC je n'ai pas obtenu le même résultat que sous Windows (pourtant toujours avec GCC, mais probablement avec des options de compilation différentes). En utilisant cygwin et en compilant en ligne de commande (sous Windows, donc), j'obtiens bien le même résultat que sous OSX et Linux... Voici donc les 2 résultats obtenus :
Avec Code::Blocks
w : 0 0 x : 0 1 y : 1 0 z : 2 0
Ici, le résultat parait dans l'ensemble cohérent, si en tient compte d'un élément bizarre : on dirait que les opérations sont effectuées de droite à gauche : si dans la partie de droite on incrémente la variable, alors dans la partie de gauche la valeur sera (valeur+1), comme en témoignent les lignes 3 et 4.
Avec GCC sans option
w : 0 1 x : 0 1 y : 1 0 z : 2 0
Autre résultat, autre constant : cette fois-ci le résultat parait moins cohérent. En effet, les lignes 1 et 2, affichent le même résultat, alors que dans un cas on affiche "w", et dans l'autre "x+1"... L'explication semblerait être qu'on effectue en premier les opérations, de droite à gauche, puis on met les valeurs. Donc pour "w++, w", on incrémente d'abord w, puis on met les valeurs dans les arugments, alors que pour "x++, x+1", on calcule en premier x+1, puis on incremente avec x++, et les 2 valeurs stockées sont utilisées pour les arguments.
Que peut-on déduire de cet exemple foireux ?
- Savoir exactement ce que fait un code n'est pas forcément simple.
- Les warnings ne sont pas là pour rien... En l’occurrence, en activant les warnings on obtient ici "warning: operation on `w' may be undefined"... Pourtant, pas mal de monde semble considérer que "si ça compile, c'est que c'est bon", et on se retrouve avec des programmes bourrés de warnings (et donc potentiellement de bugs dans ce genre).
D'ailleurs, j'utilise souvent Code::Blocks, qui a une manie assez bizarre : si on compile avec "build and run" et qu'il n'y a pas d'erreur, et même s'il y a des centaines de warnings, Clode::Blocks vide le log de compilation et n'affiche rien... Il faut alors recompiler (sans exécuter) pour voir apparaître les warnings (pour peu qu'on ait fait une modification rapide dans un des fichiers pour "forcer" la recompilation...).
Méfiance !
Moteur de lumières en 2d
Je suis tombé ce matin sur un article fort sympathique, qui explique comment faire pour gérer des lumières en 2d : http://gregouar.developpez.com/tutoriels/jeux/moteur-lumieres-dynamiques-2d/
Le code est en C++, et utilise SFML. Et voici le résultat :

C'est pas mal du tout, et l'ensemble a l'air bien expliqué et simple à suivre. Je vous invite aussi à aller voir sur le tuto, car il a mis en ligne une petite vidéo (un gif, mais pas grave) que je trouve assez impressionnant et beaucoup plus parlant que cette image fixe...
Le principe utilisé est qu'une source lumineuse (dans toutes les directions, ou dans une direction précise), peut au final être décomposée en plusieurs triangles. Il "suffit" alors pour chaque triangle de calculer judicieusement les intersections avec les murs du décor pour avoir une lumière qui s'arrête au niveau des murs. Le seul souci est qu'il faudra faire attention aux performances, car il peut rapidement devenir très coûteux de calculer toutes les intersections ^^ Mais le reste, c'est de l'optimisation
Si jamais j'ai l'utilité dans un futur jeu (ou application), je ne manquerai pas d'essayer !
C++ et destructeur non virtual

A un moment j'ai eu un patron qui demandait tout le temps à tout le monde s'il faisait de l'algo... Je trouvais ça bizarre comme critère principal de recrutement, et puis un jour en discutant avec lui j'ai compris : "ce qu'il y a de bien avec l'algo, c'est que tu peux prouver que ton code est juste, sans même avoir besoin de l'exécuter".
Ce qui est dommage avec lui, c'est qu'on a perdu énormément de temps à débugger son code ^^ (il ne semblait pas capable de le faire lui-même). Pour un jeu DS qu'on a eu à faire (et dont je tairais le nom afin de préserver le peu de crédibilité dont je pourrais disposer), il avait mis en place tout un système de widget, façon Java Swing, pour gérer les menus du jeu. Bon, c'est super, je ne suis pas forcément contre, mais le menu en question c'était un écran avec 2 boutons (PLAY et OPTIONS), l'écran des options c'était 2 boutons (SOUND ON/OFF et CREDITS), et l'écran de crédits avec les noms qui défilent. 2-3 mois pour faire ça, je trouve ça long ^^ Surtout qu'au final, je me suis rendu compte que si on faisait la séquence suivante : menu -> jeu -> menu -> jeu, ça plantait par manque de mémoire.
Là, on est sur DS... Donc pas de Valgrind pour analyser la situation. En regardant le code, je ne voyais pas trop d'où venait le souci, tous les objets alloués avaient l'air d'être détruits... Du coup je me suis dit que j'allais tracer toutes les allocations/libérations afin de trouver l'origine du problème. Heureusement pour nous, on n'utilisait pas malloc et free, mais des fonctions Mem_Alloc et Mem_Free persos, qui appelaient des routines d'allocation/libération de la DS, et qui étaient aussi utilisés en C++ par 'new' et compagnie... Je rajoute donc des affichages de debug à en spammer la console :
A 0xXXXXXXXX (NN bytes) pour les allocations F 0xXXXXXXXX pour les libérations
Je me retrouve donc avec une console remplie de milliers d'allocations/libérations. Juste ingérable ^^ Je rajoute donc un premier filtre qui permet de n'afficher que les allocations supérieurs à une certaines taille (genre 1ko je crois), car de toute façon on avait des fuites de l'ordre de 500ko... Ca faisait encore pas mal de données à traiter, du coup j'ai fait une petite appli vite fait qui prend en ligne de commande un fichier, qui le dump ligne par ligne, enregistre les alloc/free, et recrache tout ce qui fuit (en gros le boulot de Valgrind...). Bien pratique tout ça :p Car avec ça j'avais la liste de toutes les allocations qui fuyaient, et comme sur DS on pouvait mettre des breakpoints, j'ai pu mettre des
if (adr == 0xXXXXXXXX){
printf("STOP HERE\n");
}
avec un breakpoint sur le printf...
Et là, je me rends compte que tous les trucs qui fuient sont en fait les widgets... La question est donc : pourquoi ? Parce que dans le code, on fait bien des "delete" sur ces objets...
La réponse a été trouvée sur le net : si un destructeur n'est pas déclaré virtual, on ne passe pas par le destructeur de la classe fille ! C'est tout con, mais il faut le savoir, sinon niveau fuite ça peut aller très vite (comme là).
Problème réglé !! (modulo quelques petites fuites ailleurs mais plus classiques, oublis de libération de la mémoire...)
Moralité : d'une part on peut prouver qu'un algo est juste et avoir une implémentation fausse (car se sont 2 choses différentes), d'autre part, on peut prouver qu'un algo est juste tout en ayant une faille dans le raisonnement, et donc la preuve est en fait fausse... Je me dis que ce n'est pas pour rien que JUnit est utilisé par pas mal de monde...
Et aussi : utiliser les bons outils (Valgrind ou autre) pour résoudre les bons problèmes, c'est top ! Ici, en l'occurence, il aura été plus rapide de coder un petit tool sur mesure plutôt que de chercher vainement partout dans le code...
Utiliser le mot-clé « super » en C++
A force de faire du Java, on prend certains réflexes... L'un d'entre eux est d'utiliser le mot-clé "super" pour accéder spécifiquement à une méthode de la super-classe. Du coup quand en codant en C++ j'ai tapé "super" et que j'ai eu droit à une erreur de compilation, ça m'a fait tout drôle...
D'un point de vue pratique, il existe une raison très simple et pour laquelle super ne serait pas disponible en C++ : l'héritage multiple... A quelle classe accèderait-on ainsi ?
En fait, en C++, la façon d'accéder à une méthode d'une superclasse est simple : on utilise explicitement le nom de cette superclasse.
void Foo::haha(void){
Bar::haha();
}
Ca permet effectivement de résoudre le problème posé par l'héritage multiple, et ça a le mérite d'être très clair.
class Derived : public Base
{
private :
typedef Base super; // note that it could be hidden in protected/private section, instead
// Etc.
} ;
En fait c'est tout simple, on ne fait que définir le type super, pour cette classe, comme étant du type de la superclasse. Un premier avantage de cette méthode est que même avec l'héritage multiple, on peut faire fonctionner le mot-clé super en précisant soi-même quelle est la classe à "privilégier". Par contre, on ne peut en choisir qu'une :p
Cette méthode présente cependant un inconvénient majeur, qui peut être source de bug, et qui explique que super soit déclaré comme private (et non public ou protected) : que se passerait-il si on oubliait de le définir dans une sous-classe, et qu'on utilisait quand même le mot-clé super ? Et bien c'est simple, on n'irait pas du tout à la super-classe directe, mais à la super-classe précisée dans le typedef (donc plusieurs niveaux au-dessus, en gros). Dans le genre bug super galère à retrouver, ça peut être violent. Donc autant ne pas prendre le risque et bloquer ce type en le mettant private. Ca signifie qu'on ne pourra pas faire super::super::maMethode(), mais tant pis, il vaut parfois mieux jouer la sécurité.
Je dois dire que quelque part ça m'a fait plaisir de voir cette "bidouille" en C++, car pour moi le C et le C++ sont des langages où avec un peu d'astuce on peut un peu tout faire
(sauf que le GC, il faut le recoder, mais bon...). En tout cas avec de bonnes petites macros et autres, on peut se simplifier la vie, là où dans d'autres langages (comme le Java) on va se reposer à la place sur l'IDE, ses facilités, et éventuellement en branchant par-dessus des systèmes de macros supplémentaires.
Source : Stack Overflow (probablement le meilleur site de questions/réponses pour les développeurs)
C++0x : l’avenir du C++ ?
Hier je suis tombé un peu par hasard (ou pas d'ailleurs) sur les specs du C++0x (qui devrait maintenant s'appeler C++1x), norme qui devrait remplacer à terme le C++ tel qu'on le connait aujourd'hui...
J'étais assez sceptique car dernièrement je trouve que le C/C++ n'avance pas trop (par rapport à ce qui se fait partout ailleurs), mais je dois admettre que j'ai eu quelques bonnes surprises (et quelques mauvaises, dommage ^^). Voici quelques points que j'ai trouvé intéressants à signaler.
Char – Signed or Unsigned ?
Enigme du jour: le jeu codé marche sur PC, sur iPhone, sur le simulateur Bada, mais plante lamentablement (et sans infos de debug) sur téléphone Bada. Pourquoi ?
Code au rabais ?
Un taiwanais, ça coute pas cher. Mais est-ce que c'est bon ?
Je ne vais pas faire de généralités, mais j'ai trouvé un truc pas mal dans le code tout à l'heure (copié/collé d'un truc "pro" fait à Taiwan...). En fait c'était un bout de code pour convertir un double en 2 ints (un pour la partie entière, un pour la partie décimale). Donc en gros, on obtient le code suivant :
Bizarrement de temps en temps ça bloque le programme... (on notera que la récupération de la partie décimale est aussi super bien pensée... genre 1.01 renvoie 1, tout comme 1.1 et 1.0001 (entre autres...) ^^)