Gilliek
5avr/124

La Metaprogrammation en Ruby

Voici (enfin !) mon premier billet en 2012 ! L'année passée, je vous avais promis quelques articles techniques (notamment sur la programmation). Aujourd'hui, c'est chose promise chose due : on va parler de métaprogrammation en Ruby.

Cet article sera accompagné d'un autre qui traitera de la fonction yield(). Et ces deux articles précéderont une série d'autres à propos du célèbre framework Ruby : Ruby On Rails.

Qu'est-ce que la metaprogrammation ?

J'imagine que beaucoup d'entre-vous ont ouvert de grands yeux à la lecture de ce mot barbare : "la métaprogrammation". En fait, cela n'a rien de bien compliqué : La métaprogrammation consiste à écrire des programmes qui décrivent eux-mêmes des programmes. Autrement dit, ça permet (entre autres) de générer du code directement au runtime. Comme vous allez pouvoir le constater, c'est vraiment très puissant.

La définition de Wikipedia énumère les différentes façons de procéder :

  • l'utilisation de générateur de code (ce que j'ai mentionné juste avant)
  • la programmation avec des templates
  • l'utilisation d'un protocole à méta-objets
  • l'utilisation de macros.

A quoi peut bien servir la metaprogrammation ?

La métaprogrammation peut servir à grandement simplifier la lecture du code (notamment via le principe Don't Repeat Yourself) ou dans le cas de Rails à simplifier la vie du développeur.

Deux petits exemples pour la route !

Admettons que l'on fasse un programme qui gère une base de données client et qui manipule donc plein de chaînes de caractères (String) qui peuvent signifier plein de choses (un numéro de téléphone, une adresse email, un numéro client, etc.). Nous aimerions qu'un objet String dispose de méthodes telephone?, email?, no_client? ... et peut-être même d'autres plus tard ! Comment faire ? Vous vous doutez bien de la réponse : avec la métaprogrammation !

Voici le code : (je vous expliquerai juste après son fonctionnement, donc pas de panique ;-) )

  #!/usr/bin/env ruby

class String
        @@string_checks = {
        'phone?' => %r{^0[\d]{2}\/[\d]{3}\.[\d]{2}\.[\d]{2}$},
            'email?' => %r{^.+@.+\.[a-z]{2,4}$},
            'no_client?' => %r{^[A-Z]{2}[\d]{4}-[\d]{4}-[\d]{4}$}
        }
        def method_missing(method_id)
                kind_of_string = method_id.to_s
                if @@string_checks.has_key?(kind_of_string)
                        if self =~ @@string_checks[kind_of_string]
                                true
                        else
                                false
                        end
                else
                        raise NoMethodError,
                                "Undefined method #{method_id} for \"#{self}\":String"
                end
        end
end

def humanize(bool)
        bool ? "Yes !" : "No !"
end

begin
        # Valid
        tel = "022/123.45.67".phone?
        email = "foo@foo.com".email?
        no_client = "CH1234-4242-4242".no_client?

        puts "Is 022/123.45.67 a valid phone number ? #{humanize(tel)}"
        puts "Is foo@foo.com a valid email address ? #{humanize(email)}"
        puts "Is CH1234-4242-4242 a valid client number ? #{humanize(no_client)}"

        puts "#################################################"

        #Invalid
        tel = "0033/123.12.12.12".phone?
        email = "www.foo.com".email?
        no_client = "42".no_client?

        puts "Is 0033/123.12.12.12 a valid phone number ? #{humanize(tel)}"
        puts "Is www.foo.com a valid email address ? #{humanize(email)}"
        puts "Is 42 a valid client number ? #{humanize(no_client)}"
end

Alors qu'est-ce que j'ai fait ?

Vous noterez tout d'abord que je définis une classe String qui est en fait une classe prédéfinie. En effet, en Ruby il est tout à fait possible de rajouter des choses dans les classes prédéfinies.

Il faut ensuite faire attention à la méthode method_missing que j'ai ajouté à la classe String. Que fait cette méthode ? Et bien si vous appelez une méthode non définie d'un objet String, Ruby va jeter un oeil dans cette méthode pour voir si on y définit quand même la méthode.  Et c'est justement là-dessus que je joue en récupérant l'id de la méthode et cherchant l'expression régulière correspondante au nom de la méthode appelée.

Après, je lève simplement une exception NoMethodFound si la méthode appelée n'est pas correct.

Finalement, je fais une méthode humanize ... qui n'a rien de spéciale :-) Elle ne fait que mettre en forme un booléan. Le reste n'est ensuite que des tests que vous pouvez vous amusez à lancer.

N'est-ce pas beau de pouvoir simplement appeler "foo@foo.com".email? pour vérifier s'il s'agit bien d'un email valide ?

Ainsi, il va gérer la méthode dynamiquement et directement au Runtime :-) De plus, si on veut par la suite ajouter d'autres vérifications pour une String, il suffit d'ajouter ce dont on a besoin dans la hash table @@string_checks.

Il y a également d'autres façons d'utiliser la métaprogrammation. On peut générer des méthodes sous forme de chaînes de caractères et utiliser la méthode class_eval pour l'évaluer. Regardons ça avec un petit exemple.

On aimerait créer une méthode attr_accessor_with_backup qui déclarerait un attribut et ses accesseurs correspondant (getter et setter) et qui garderait un backup de la valeur précédente.

 #!/usr/bin/env ruby

class Class
        # Store a backup of the previous value in attr_name_backup
        def attr_accessor_with_backup(attr_name)
          # Make sure it's a String
          attr_name = attr_name.to_s
          # Create a getter for attr_name and attr_name_backup
          attr_reader attr_name
          attr_reader attr_name+"_backup"
          # Generate setter for attr_name
          class_eval %Q{
                  def #{attr_name}=(value)
                        # backup previous value
                        @#{attr_name}_backup = #{attr_name}
                        # set the new value
                        @#{attr_name} = value
                  end
          }
        end
end

class WeNeedBackup
        attr_accessor_with_backup :foo
end

begin
        spock = WeNeedBackup.new
        spock.foo = "42"
        puts "foo => #{spock.foo} and foo_backup => #{spock.foo_backup}"
        spock.foo = "24"
        puts "foo => #{spock.foo} and foo_backup => #{spock.foo_backup}"
        spock.foo = "128"
        puts "foo => #{spock.foo} and foo_backup => #{spock.foo_backup}"
end

Cette fois on ajoute une méthode attr_accessor_with_backup dans la classe Class (classe dont toutes les classes Ruby sont des sous-classes), qui, rappelons-le, fait exactement comme la méthode prédéfinie attr_accessor en plus de stocker un backup de la valeur précédente de l'attribut.

Dans cette méthode on va alors créer des getters pour le nom de l'attribut (attr_name)  et son backup (attr_name_backup) qui contiendra la valeur précédente de attr_name.

Ensuite, il va nous falloir définir dynamiquement un setter pour l'attribut. On va donc utiliser la méthode class_eval et lui donner en argument une chaîne de caractères contenant le code de notre setter. Cette méthode s'occupera de sauvegarder la valeur courante de l'attribut dans l'attribut _backup et de mettre à jour la valeur de l'attribut.

Finalement, notre méthode attr_accessor_with_backup va générer les méthodes nécessaires directement au runtime.  N'est-ce pas monstrueusement puissant ?

Conclusion

Vous l'aurez sans doute constaté, la métaprogrammation permet de faire des tas de choses bien pratiques et peut ainsi simplifier la vie du développeur.

Le framework Rails utilise la métaprogrammation pour simplifier l'écriture du code par le développeur. Par exemple, il génère des helpers pour gérer les chemins aux ressources. Par exemple, pour accéder au controller products, il suffira d'appeler products_path ou products_url si on veut un chemin absolu. Si on veut accéder à l'action new du controller products, il suffira d'appeler new_product_path ou new_product_url. Ne vous inquiétez pas, nous reviendrons dessus beaucoup plus en détail très prochainement lorsque nous aborderons Rails.

J'espère que ce billet vous aura été utile ;-)

22nov/110

Java et les JFileChooser

Java des fois, tu me fais ch*** !

Je bosse actuellement sur un logiciel de traitement d'image en Java ( coming soon ;-) ) ... et j'ai besoin de manipuler des JFileChooser (pour sélectionner des fichiers et des dossiers).  Avant de pester contre ce foutu Swing d'énoncer mon soucis, je vais dire deux mots sur Swing, pour ceux qui ne connaîtraient pas.

Swing est une bibliothèque graphique pour Java. Elle permet donc de créer des interfaces graphiques. Ce qui est pratique, c'est que ça fonctionne sur n'importe quel système d'exploitation, donc pas mal pour du développement multiplateforme. (petit troll : et c'est beaucoup plus confortable à utiliser que C et GTK ... bien que je préfère largement le C à Java).

Revenons à nos moutons. Qu'est-ce qui me fait actuellement ch*** avec JFileChooser ? Et bien la réponse est simple : son implémentation moisie ! C'est un composant très peu personnalisable. Par exemple, si on veut garder uniquement la fenêtre de navigation sans les boutons, le sélectionneur de type de fichiers et toussa, on s'amuse avec la méthode remove(getComponent(index)) à chercher quel est le bon composant à virer. Ou aussi ce qui est à l'origine de ce post : comment choisir le mode d'affichage par défaut (liste ou détails). Je m'attendais à devoir faire un simple setViewMode(JFileChooser.DETAILS), comme en Qt quoi ... mais non faut faire un hack dégueulasse pour arriver à ses fins ! (Rhaaaaaa)

Après quelques énervements, j'ai trouvé un topic sur un forum qui m'a bien aidé :

La solution consiste à utiliser SwingUtils, téléchargeable sur le site : http://tips4java.wordpress.com/2008/11/13/swing-utils/

Il suffit ensuite de mettre le code trouvé sur le topic mentionné précédemment :

JFileChooser fileChooser = new JFileChooser();
AbstractButton button = SwingUtils.getDescendantOfType(AbstractButton.class,
      fileChooser, "Icon", UIManager.getIcon("FileChooser.detailsViewIcon"));
button.doClick();

Et le tour est joué ! Mais je trouve ça vraiment dégueulasse personnellement ...

De manière plus générale, je trouve leur JFileChooser vraiment très pauvre. En plus de ne pas être très confortable à l'utilisation, il n'est pas personnalisable ... c'est vraiment dommage. Il va peut-être falloir que j'en code un moi-même quand j'en aurai le temps ... à suivre ...

 

Edit : Cette solution foire sur Mac OS (ça ne compile pas ...) . De plus, la façon avec laquelle j'ai encore modifié le JFileChooser (avec la méthode remove) fait foirer l'affichage sous Mac OS ...

Edit 2 : J'ai entamé (et déjà bien avancé) le codage de mon propre FileChooser et pour l'instant ça fonctionne bien :-)

21oct/111

Modulo : la suite

Pour donner suite à mon post précédent : Une histoire de modulo ...

je me suis amusé à tester -5 % 26 dans 17 langages de programmations afin faire une liste des langages qui utilise la première définition et une liste de ceux qui utilisent la seconde.

Pour rappel, voici les deux définitions :

  1. x mod y = x - y * floor(x / y)
  2. x % y = x - y * iPart(x / y)

La définition 1 est cyclique. Ainsi -5 % 26 = 21. La définition 2 donne -5 % 26 = -5

Trève de bavardages, voici donc les deux listes :

Définition 1 Définition 2
Ruby C
Python C++
Lua Java
Octave PHP
MATLAB OCAML
Perl Pascal
Racket (anc. Scheme) Ada
Prolog Javascript
Go
C#
20oct/116

Une histoire de modulo …

Tout développeur connait le fameux (le seul, l'unique) : modulo

Comme je suis d'humeur narrative aujourd'hui,  je vais quand même expliquer de quoi il s'agit :-)

Cours du jour, bonjour

Bon, ouvrez bien vos oreilles yeux, on commence.

Tout le monde se souvient de sa tendre enfance, à l'école, en train d'assister à son premier cours de maths sur les divisions. Plutôt que de s'amuser à calculer le résultat réel (au sens mathématique du terme), on ne s'intéressait qu'à la partie entière et au reste.

Petits exemples :

12 / 5 = 2 et Reste = 2

6 / 2 = 3 et Reste = 0

9 / 2 = 4 et Reste = 1

9 / 3 = 3 et Reste = 0

En somme (notez le jeu de mot :-P ), une division entière.

Et bien le modulo calcule le reste. C'est un nom bien barbare, qui au final, n'est rien de plus qu'un calcul enfantin

Donc si on reprend l'exemple ci-dessus, mais en version informatique cette fois, on a :

(dans beaucoup de langage, l'opérateur du modulo est le %)

12 % 5 = 2

6 % 2 = 0

9 % 2 = 1

9 % 3 = 0

Modulo = reste de la division entière.

On utilise vraiment ça ? A quoi ça peut bien servir une telle chose ??

Et bien, c'est très souvent utilisé lorsqu'on programme. On l'utilise, entre autres nombreuses choses, pour déterminer si un nombre est

pair. Il suffit de faire nombre % 2 Si le résultat est null, donc qu'il n'y a pas de reste, alors le nombre est pair. Sinon il est impair.

Maintenant que tout le monde c'est ce qu'est le modulo, on va pouvoir poursuivre sur le sujet de ce post.

Revenons aux choses serieuses

Faisons un petit exercice. Quel est le résultat de :

-5 % 26 = ?

(bien que nous ne soyons pas directement dans post concernant le domaine de la cryptographie, je vais utiliser les deux fameux protagonistes Bob et Alice, parce que je suis tombé sur cette étrangeté pendant un Travail Pratique de cryptographie)

  • Bob : Pff facile, c'est 21 !
  • Alice : Mais, non pas du tout c'est -5 !
  • Bob : Tu n'as rien compris, un modulo c'est cyclique, ça ne peut pas être être négatif !
  • Alice : Rien du tout. Tu es HS  coco !

Mais qui a raison ? Et bien, ils ont tous les deux raisons. Avant d'en venir aux explications, regardons ce qu'en disent les langages de programmation.

Ruby :

$> ruby -e "puts -5 % 26"
21
$>

Python

$> python -c "print(-5 % 26)"
21
$>

Octave (et MATLAB)

$>octave --eval "mod(-5, 26)"
GNU Octave, version 3.4.2
Copyright (C) 2011 John W. Eaton and others.
This is free software; see the source code for copying conditions.
There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  For details, type `warranty'.

Octave was configured for "x86_64-unknown-linux-gnu".

Additional information about Octave is available at http://www.octave.org.

Please contribute if you find this software useful.
For more information, visit http://www.octave.org/help-wanted.html

Read http://www.octave.org/bugs.html to learn how to submit bug reports.

For information about changes from previous versions, type `news'.

ans =  21
$>

(On peut voir Bob trépigner :-P )

Java

class Main {
    public static void main(String[] argv) {
        System.out.println("-5 % 26 = " + (-5 % 26));
    }
}
$> javac Main.java
$> java Main
-5 % 26 = -5
$>

PHP

$> php -r 'print((-5 % 26) . "n");'
-5
$>

Agné ?! (le sourire de Bob se fane ... Alice est intriguée, comme on peut s'en douter)

Java et PHP ne sont pas les seuls. C, C++ (et d'autres) retournent -5.

Mais pourquoi ?

Et bien, en réalité, il y a deux définitions du modulo !

Regardons un peu ce qu'en dit notre vieil ami Wikipedia : http://fr.wikipedia.org/wiki/Modulo_(informatique)

Dans l'article, on a les deux définitions :

  1. x mod yxy * floor(xy)
  2. xyxy * iPart(xy)

La première définition donne un modulo cyclique, càd compris entre 0 et le diviseur. Je ne connaissais que la première définission et c'est à cause de ça que je me suis énnervé sur mon Travail Pratique de cryptographie (en Java). Pour résumer, je voulais faire un décalage sur chaque caractère d'une String. Bien entendu, je veux que quand on décale, par exemple, a de -4, on ait w. J'ai donc utilisé un modulo. Et comme j'utilisais le résultat comme indice d'un tableau, j'avais un joli OutOfBoundsExceptions à cause d'un indice négatif. Après quelques tests, j'ai enfin trouvé d'où venait le problème.

C'est donc bon à savoir pour tout développeur :-)

Moralité de l'histoire : Vérifier l'évaluation du modulo du langage utilisé afin de ne pas se faire avoir bêtement (comme moi quoi :-P )

Edit : j'ai mis une liste des langages trié en fonctione de la définition utilisée là : http://blog.gilliek.ch/programmation/modulo-la-suite

20mai/110

Quel fou !

Waow ! Complètement taré : Fabrice Bellard a programmé un émulateur de PC en JavaScript qui boot sur Linux ! Vraiment bien fait :-) Il y a même l'éditeur vi, un compilateur pour compiler le programme hello.c présent dans votre home ainsi qu'une partie des commandes (ls, cd, chmod, du, etc.). Et les commandes gèrent même une partie des options (ex. ls -l).

ça se passe par ici : http://bellard.org/jslinux/ (à tester avec Chrome/Chromium ou Firefox 4 ou plus).

Pour les notes techniques, c'est par là : http://bellard.org/jslinux/tech.html

20mai/111

Les regles d’or du developpeur debutant

En lisant cet article de Developpez.com ça me donne envie de réagir ... et je vais donc réagir ! :-)

Pour résumer l'article, c'est le développeur Paul Vick qui énonce les 7 règles d'or du programmeur débutant.

Prenons ces règles une par une :

Règle numéro 1, le programmeur débutant ne doit pas écrire de longues procédures. Une procédure ne devrait pas avoir plus de dix ou douze lignes de code.

Déjà, je pense que ça dépend du langage. On ne peut pas espérer des procédures de même taille en Ruby qu'en C. En C, je dirai que ça tourne plus autour de 24 lignes (cf. Style Coding du Kernel Linux). Mais après, ça dépend de la situation ... D'autant plus si on applique une gestion d'erreurs rigoureuse ...

Deux, chaque procédure doit avoir un objectif clair. Un bon programme doit avoir des procédures claires, sans cumul.

Je suis d'accord là-dessus.

Trois, les programmeurs débutants ne doivent pas utiliser les fonctions fantaisistes du langage. Pour Paul Vick, il est mal pour un débutant d'utiliser autre chose que des déclarations de variables, les appels de procédures, des opérateurs (arithmétiques, comparaisons, etc.) et les fonctions de contrôle de flux. Selon lui, l'utilisation des fonctions simples oblige à réfléchir à ce que l'on écrit.

Je pense au contraire qu'un développeur débutant doit "s'amuser" à utiliser les fonctions du langage. Bien entendu, au début de son apprentissage, il doit veiller à bien connaître et manipuler les structures conditionnelles, les boucles, les types, ... toussa quoi. Mais il faut bien qu'un jour il apprenne à utiliser les fonctions "fantaisistes" du langage ... quoi de mieux que la pratique pour apprendre ?

Règle numéro quatre, ne jamais utiliser les fonctionnalités du langage dont vous n'êtes pas sûr(e) du résultat ou du rôle. Une règle d'or indépassable pour Paul Vick, qui estime que si elle n'est pas respectée par un débutant, il devrait purement et simplement changer de métier

Là encore : il faut tester. Pas sûr du résultat ? Alors hop un petit programme de test pour y voir plus clair ! C'est pas en tournant le dos à tous ce qu'on ne comprend pas qu'on deviendra un "bon" développeur ...

Règle numéro cinq, les débutants doivent à tout prix éviter le copier/coller. Sauf, évidemment, s'ils veulent copier le code d'un programme qu'ils ont écrit.

Je suis assez d'accord. Mais en revanche je pense qu'il est très instructif pour un débutant de s'inspirer de code existants.  En effet, voir un code fait par un développeur avancé peut être instructif. Concernant le copier/coller, je serai un peu pus stricte en évitant de copier/coller son propre code (sauf dans un cas trivial et redondant). A force de tout taper, la syntaxe du langage rentrera plus facilement.

Six, le débutant doit éviter l'abstrait, et toujours opter pour le concret.

Je suis tout à fait d'accord là-dessus. Si le débutant reste trop dans l'abstrait, il va vite se sentir perdu ... et ça devient très décourageant pour lui ...

Et enfin la règle numéro sept : applique les six règles ci-dessus chaque jour pendant au moins six mois.

Alors ça c'est de la règle ! :-) ça ressemble plus à une conclusion qu'à une règle à part entière. En plus, je pense que le temps d'apprentissage d'un langage dépend de la personne. Personnellement, je n'ai jamais eu besoin de 6 mois pour apprendre les bases d'un langage ... (et même pour les mécanismes avancés de ce langage d'ailleurs ...).

Comme c'est présenté, ça fait un peu recette miracle, mais au final,  selon moi, suivre ces règles ne fera pas d'un débutant autre chose qu'un ... débutant. Faire des if/for et print "Hello, World" pendant 6 mois est complètement inutile ! C'est en explorant les "profondeurs" d'un langages qu'on apprend vraiment. Et puis, pour bien apprendre, il me semble important que la période d'apprentissage soit intéressant, attractive. Si on fait la même chose pendant 6 mois, on sera vite dégoûté.

L'article de Developpez.com se conclut comme suit : "Et pourrait même permettre, conclut-il avec humour, de se débarrasser des mauvaises habitudes acquises à l'Université. "

Sur ce point je suis d'accord. Dans les Universités (je ne veux pas généraliser hein :-) , mais je parle selon mon expérience), on apprend pas les bonnes pratiques de programmation ou si on les apprend, ce n'est que trop tard.  C'est fort regrettable. D'un autre côté, avec le nombre impressionnant de de ressources sur Internet et dans les livres (électronique ou papier), l'étudiant peut (et devrait selon moi) apprendre par lui-même et profiter de la mine de savoir auquel on accès de nos jours.

Je rajouterai pour finir, que Paul Vick a omis un élément très important : la documentation du code. Il faut prendre l'habitude de bien documenter son code dès le début. Par exemple, faire de bons commentaires, c'est-à-dire ne pas paraphraser le code mais expliquer ce qu'il fait. Ca évite bien des problèmes de maintenabilité ...

5mar/112

Tetris 3D en HTML5

HTML5 prmet d'offrir d'énormes possibilités. Plus j'en apprends, plus je suis impressioné ! Voici un Tétris en 3D fait en HTML 5 :  Torus

J'espère qu'un jour les jeux en Flash,  seront remplacés par des jeux en HTML 5.  Pareil pour les vidéo. Je commence à envoir vraiment mare de flash :

  • Optimisation nul (pauvre CPU !)
  • Failles de sécurité importantes
  • Technologie propriétaire
  • Lourd
  • ...

J'attends avec impatience la fin des spécifications de HTML5.

PS: Et ne venez pas me parler de Silverlight !

Taggé comme: , , 2 Commentaires
28déc/103

Clang vs GCC

Mon ami Rolinh a déjà posté un billet sur une bizarrerie entre Clang et GCC. Eh bien hier soir, je suis tombé sur une autre assez spéciale.

Pour la petite histoire, j'aidais quelqu'un sur un forum et j'ai compilé son code avec Clang et avec Gcc et le résultat obtenue était différent.

Note : Ici, on ne s'intéresse pas à la qualité du code.

Voici le code :

int triplePointeur(int *pointeurSurNombre);

int main()
{
	int nombre = 5;
	triplePointeur(&nombre);
	printf("%d %dnn", nombre, triplePointeur(&nombre));

	return 0;
}

int triplePointeur(int *pointeurSurNombre)
{
	int a = 4;
	int b = 2;
	*pointeurSurNombre *= 3;
	return a + b;
}

Avec GCC ça donne :

$ gcc -Wall -o test test.c
$ ./test
45 6

On a une première modification de la variable nombre avec le premier appel de la fonction triplePointeur. On a 3 * 5 = 15. Ensuite, on réappelle triplePointeur dans le printf et on a donc  nombre = 3 * 15 = 45.

Et maintenant avec Clang :

$ clang -Wall -o test test.c
$ ./test
15 6

Et là, surprise, on obtient 15 !

Pour la réponse à cette énigme (voir commentaires, et au passage merci au posteur anaonyme) :

En fait, l'ordre de calcul des paramètres n'est pas défini dans le langage C. GCC calcule de droite à gauche, donc la variable nombre est modifiée deux fois. Quant à Clang, lui il les calcule de gauche de à droite. Du coup, nombre est affichée avant d'être modifiée une seconde fois.

9déc/100

Raisonnement naif

NOTE: Pardonnez la faute dans le titre (naif -> naïf), mais mon cher WordPress refuse de me mettre un "ï" ...

Il y a quelques temps de cela, je suis tombé par hasard sur un tutoriel en C qui parlait d'une bibliothèque (dirent, utilisée pour ouvrir/parcourir un répertoire). L'auteur, propose à un moment une fonction pour déterminer si l'élément courant est un répertoire ou un fichier :

int isDir(struct dirent* ent)
{
    if ((strchr(ent->d_name, '.')) == NULL) /* Si le nom du fichier n'a pas de point (une extension). */
        return 1;
    else
        return 0;
}

Je ne vous donne volontairement pas le lien vers le tutoriel. Mon but n'est pas de critiquer l'auteur ni son tutoriel (qui, soit dit en passant, est très bon hormis cette fonction), mais de mettre l'accent sur les "raisonnements naïfs" que peut avoir le programmeur.

Petite précision importante au passage : Ce tutoriel présente la bibliothèque pour GNU/Linux et Windows, en mettant quand même l'accent sur Linux.
Regardons maintenant ce bout de code de plus près !

Vous l'aurez sans doute remarqué, l'auteur propose de vérifier l'extension pour déterminer s'il s'agit d'un répertoire ou d'un fichier. Aïe ! En plus d'être absurde parce qu'on peut très bien mettre un point dans le nom d'un dossier (ex. : les dossiers cachés), les fichiers n'ont pas forcément d'extension (!) ...

Bref, il s'agit d'un raisonnemnt très naïf qui conduit souvent à de gros bugs dans un programme.

Moralité de l'histoire : bien réfléchir à ce qu'on fait et surtout connaître l'OS (Système d'exploitation) sur lequel on travaille.
Et comme je suis gentil (:-P), je vous mets une fonction correcte :

int
is_dir(const char *path)
{
	struct stat buf;

	if (stat(path,&buf) == -1){
		perror("Error ");
		return -1;
	}

	return S_ISDIR(buf.st_mode);
}

Et il faut inclure (en plus des libs "de base"):

sys/types.h
sys/stat.h
unistd.h

Je ne vous mets pas ces #include en entier parce que cet abruti de WordPress me les supprime (sans doute à cause des chevrons ...)

6déc/100

TCPDF, ton sauveur !

Si toi aussi tu en as mare de PDFlib, TCPDF est là pour toi ;-)

En tant que développeur Web, il m'est arrivé de devoir générer des PDF. Le problème étant que la plupart des hébergeurs ne propose que la version gratuite de PDFlib qui est une horreur sans nom (grrr). Mal pensé, mal documenté (et le exemples sont en Java ...) et en plus il est très limité en sa version gratuite ... je vous laisse imaginer la galère pour développer avec ...

Mais ces mauvais moments sont terminés, parce que TCPDF est là (<3) ! En effet, avec TCPDF, pas besoin d'installer quoique ce soit sur le serveur (ce qui aurait posé problème pour de l'hébergement mutualisé), il suffit de l'uploader sur le serveur et d'inclure  ce qu'il faut (avec include, require, etc). En plus, c'est beau parce qu'on met directement du code HTML. Concernant la documentation, sur le site on trouve de beaux exemples (en PHP!) clairs et utiles.