Le blog à Fred

Aller au contenu | Aller au menu | Aller à la recherche

mercredi 10 février 2016

Why you should define your own types

Most of the people coming here know that I'm an Ada enthusiast.
In my job, I'm mostly developing in Java and one of the remarks I got in the past about Ada type system is that it's useless.
A lot of developers are arguing that they never have bugs due to a type error. So, the use of range constraints and so on in Ada are almost seen as useless verbosity.
For once, I've another point of view about this feature and it's not related to bugs but to maintainability.

The reason

It's not really a secret that I'm working on cartographic products. Our product is written in Java and we use OpenMap for displaying some kind of maps.
Few weeks ago, we found that the old version of OpenMap we were using contained a bug about a specific raster format. After looking into the new source code[1], I found that the bug was still there.
One of my colleague fixed it and provided a patch to the project. Then I decided to test the patch on the brand new version of OpenMap by migrating to the last version.

The problem

Migrating is not always as simple as it might seem.
Between the two versions, 4.6.5 and 5.1.13, there has been a lot of changes that were easily detected by the compiler but the main one only seemed to be a package name change.
The LatLonPoint class was in the past inside the package com.bbn.openmap and changed to com.bbn.openmap.proj.coords.
Ok, that's easy but if I show you the beginning of the code, you will see.

/*
* For version 4.6.5
*
*/
public class LatLonPoint implements Cloneable, Serializable {
    // SOUTH_POLE <= phi <= NORTH_POLE
    // -DATELINE <= lambda <= DATELINE
    public final static float NORTH_POLE = 90.0f;
    public final static float SOUTH_POLE = -NORTH_POLE;
    public final static float DATELINE = 180.0f;
    public final static float LON_RANGE = 360.0f;
    // initialize to something sane
    protected float lat_ = 0.0f;
    protected float lon_ = 0.0f;
 
    public final static float EQUIVALENT_TOLERANCE = 0.00001f;

And the newest one

/*
* For version 5.1.13
*
*/
public abstract class LatLonPoint extends Point2D implements Cloneable, Serializable {
 
    /**
     * 
     */
    private static final long serialVersionUID = 4416029542303298672L;
    public final static double NORTH_POLE = 90.0;
    public final static double SOUTH_POLE = -NORTH_POLE;
    public final static double DATELINE = 180.0;
    public final static double LON_RANGE = 360.0;


Do you see ?
First of all, the class is now abstract... You can imagine what happened in my code as this class is really centric :)
So how do you create LatLonPoint ?
Inside the LatLonPoint, you have two inner classes defined as follows

public static class Float extends LatLonPoint {
 
        /**
         * 
         */
        private static final long serialVersionUID = -2447464428275551182L;
        protected float lat;
        protected float lon;
        protected transient float radLat;
        protected transient float radLon;
 
[...]
 
/**
     * Double precision version of LatLonPoint.
     * 
     * @author dietrick
     */
    public static class Double extends LatLonPoint {
        /**
         * 
         */
        private static final long serialVersionUID = -7463055211717523471L;
 
        protected double lat;
        protected double lon;
        protected transient double radLat;
        protected transient double radLon;

This was done to mimic the Point2D class but everywhere I instanciated a LatLonPoint, I now need to choose between Float or Double version.
Ok but what happens if I prefer to keep the higher abstract view ?
Well, for getting latitude in degrees the type returned is float whereas for radians, it's double.
Mixing such kind of types creates a mess inside our application because everything was float in the past.

The Ada solution

First try

A first solution might be to declare types for latitude, longitude and radians.

type Latitude is digits 10 range -90.0 .. 90.0;
type Longitude is digits 11 range -180.0 .. 180.0; -- for 7 digits after dot
type radian is digits 15 range -2*π .. 2*π; -- with π from Ada.numerics
 
type LatLonPoint is private; -- which should define the way we store the coordinates

The problem is that the client code don't have any way to change the precision the same way Java more or less allows it.
It's time to extend the functionalities with generics.

Second try

It would be better for the client code to define the precision.
Here is one solution... But I'm sure there's something clever than this[2]

generic 
	type real is digits <>;
package coords is
   type latitude is new real range -90.0 .. 90.0;
   type longitude is new real range -180.0 .. 180.0;
   type radian is new real range -2*π .. 2*π; -- with π from Ada.numerics
 
type LatLonPoint is private; -- Using the types defined above
end coords;


You should wonder why not using Real types directly. First, because I want the compiler to check as much as possible.
Second, because I don't need the precision of a Java double.
Let me explain :
With 7 digits after the dot for a latitude in decimal degrees, two latitudes in my code will be distant at equator level from 1 centimetre.

Conclusion

By providing the precision on the client side through a generic unit, it is possible to avoid the types mixing we had with the Java version while keeping type safety.
Changing precision is a client decision and not a implementer one. This way, the compilation problem[3] can be avoided.

Notes

[1] Open source is really great for this

[2] Maybe because I wrote it after drinking wine with another Ada enthusiast :D

[3] Around 1500 compilation errors in my software

samedi 15 août 2015

Joujou à la mode

Et voilà, j'ai craqué.
Après avoir lu une série d'articles sur l'utilisation d'Ada dans les microcontrolleurs (comme celui-ci, celui-là ou enfin encore ça), je me suis jeté à l'eau et j'ai fini par acheter une carte Nucleo de chez ST Microelectronics.
Cette petite carte a tout plein de trucs intéressants comme plein de timers, une bonne tripotée d'entrées-sorties digitales et analogiques mais surtout, elle est compatible avec l'Arduino au travers d'un connecteur ad hoc.

Me voici donc parti pour du développement en Ada sur la bête.
J'ai donc téléchargé GNAT pour ARM sur le site d'Adacore et c'était parti.
Sauf que mes premiers essais furent infructueux, jusqu'à ce que je comprenne que le runtime fourni sous le nom ravenscar-sfp-stm32f4 ne correspond pas à la famille STM32F4 complète mais seulement pour la carte STM32F4DISCOVERY. Dammed !!:)
J'ai donc fouillé sur le Grand Ternet à la recherche d'un runtime avec l'appréhension de devoir faire un portage moi-même. Finalement, je n'ai rien eu à faire car j'ai trouvé le Graal sur GitHub[1] !!
J'ai donc été sauvé par Nick Pascucci qui a décidé il y a quelques mois d'effectuer le portage sur une carte Nucleo STM32F411RE, MA carte :)

L'histoire pourrait s'arrêter là si je n'avais commencé à lire le code du runtime... Sauf que c'est plus fort que moi, je lis du code.
Si on revient un peu sur les raisons qui m'ont fait passer à Ada, on trouve :

  • le typage fort,
  • le parallélisme intégré,
  • un super compilateur qui surveille tout ce que l'on fait à bon escient [2] et
  • une grande lisibilité [3]

Et là, dans le runtime, que vois-je ?

RCC.PLLCFGR := 16#2400_3010#;
 
   --  Bit definitions for RCC APB1ENR register
   RCC_APB1ENR_USART2   : constant Word := 16#0002_0000#;
   RCC_APB1ENR_PWR      : constant Word := 16#1000_0000#;

Des opérateurs logiques, des constantes qui nécessitent d'avoir la doc sous les yeux et du coup, des trucs aussi illisibles que si on faisait du C... Beurk !!
J'ai donc fait mon psychorigide et j'ai forké. Bon, j'ai pas dit que c'était mieux mais moi, écrire des trucs comme ce qui suit, ça me correspond plus.

-- Enable clock for USART2
      RCC.RCC_APB1ENR.USART2_Clock_Enable := True;
 
--  Enable clock for GPIOC
      RCC.RCC_AHB1ENR.GPIOC_Clock_Enable := True;
      RCC.RCC_APB2ENR.Sys_Config_Clock_Enable := True;
 
      --  Configure PC13
      GPIOC.MODER (13) := Mode_IN;
      GPIOC.PUPDR (13) := No_Pull;
 
      --  Select PC13 for EXTI13
      -- See Page 139 of the RM0383 datasheet
      SYSCFG.SYSCFG_EXTICR(4).Sources(1) := PORT_C;

Alors oui, ça donne l'impression d'écrire plus de code et effectivement, cela fait plus de code assembleur dans certains cas mais au moins, ça se lit !

Bon, en tout cas, si ça vous intéresse, c'est . Vous verrez que j'ai refait plein de choses, que j'ai redéfini des types et des énumérations pour tous les registres importants et que je continue :)

Notes

[1] Et oui, rien n'est parfait dans ce bas monde

[2] Au départ, je trouvais que c'était trop mais finalement, j'écris tellement de conneries que c'est bien :D

[3] Même si certains diront que c'est vraiment trop verbeux

jeudi 4 juin 2015

A l'abandon

Bon sang !!
Ça fait maintenant plus d'un an que je n'ai rien publié :(
Les raisons sont simples :

  • moins de temps
  • moins de trucs super intéressants à partager
  • moins de développement de code
  • plus de guitare :) [1]

Pourtant, c'est pas faute de lire plein de trucs. En vrac, voici quelques liens qui m'intéressent en ce moment :


Dans les problèmes qui m'empêchent aussi de publier, il y a aussi le fait que mon FreeBSD a fini par rendre l'âme... Par ma faute !
Un passage à FreeBSD 10 et donc au compilateur CLang ont empêché certaines mises à jour de mes ports et quand j'ai voulu revenir à FreeBSD 9, j'ai découvert un bug de DMA Time out sur le driver AHCI.
Finalement, si je m'achète une carte à base de STM32 comme celle-là ou celle-ci.
Mais bon, y a rien de fait :)

Notes

[1] même pas en fait

vendredi 16 mai 2014

Aie confiance !!!

Bon sang !! Une année s'est écoulée depuis ma dernière publication !!
Le temps passe vraiment super vite quand on s'amuse :)

En parlant d'amusement, ma dernière occupation informatique professionnelle un peu nouvelle, c'est la maintenance d'une application Python.
Depuis le temps que je parlais de m'y mettre sérieusement... Enfin, si tant est que faire du Python soit une activité sérieuse :D

Lire la suite...

vendredi 19 avril 2013

Balancing bullshits

After spreading bullshits, our Bullshit Company has been overflown by bullshit requests.
It's time to provide a bigger architecture than the one we used. Let's balance the Bullshit Generator.

For the impatients, this post will deal with several problems that a beginner could encounter or fear : exceptions handling and sequences

Lire la suite...

mardi 2 avril 2013

I've got the power (english version)

This is a translation of an old post in french, see the original here.

No, I've not become Fred Almighty and that's happy, it would be a mess :D
In the scope of non-profit activities, I received a wonderful UPS that I had to configure for FreeBSD.

Lire la suite...

dimanche 24 mars 2013

Spreading bullshits

I decided today to write a small cute tutorial to create the best of the best for scalability, for highly distributed and high reliability while being multi-platform, multi-language and potentially multi-paradigm... Well, the two first ones, ok but for the last, I don't really know and finally, I don't give a damn :D
At this point, with the number of buzzwords I used, your trollmeter should explode !! :D
But, from now, I won't tell you anything else, you'll have to read what follows !!

Lire la suite...

vendredi 22 mars 2013

Distribution de conneries

J'ai décidé aujourd'hui de faire un petit tuto sympa pour réaliser le top du top en matière de scalabilité[1], de haute distribution ainsi que haute reliabilité[2] tout en restant multi-plateforme, multi-langage et potentiellement multi-paradigme... Enfin, les deux premiers, c'est sûr, le troisième, j'en sais rien et je m'en tape :D
Normalement, là, avec le nombre de buzzwords utilisés, vous devriez déjà avoir le trollomètre au taquet !! :D
Mais je ne vous en dirai pas plus, il faut aller voir, LA SUITE !!!

Notes

[1] J'adore cet anglicisme tellement il est foireux

[2] Bien aussi, celui-là

Lire la suite...

mercredi 13 février 2013

Merci de fermer les portes !

J'aurais pu sous-titrer ce billet Quand la confiance ne règne plus :-/

Il m 'avait déjà été demandé par le passé de sécuriser l'accès à un serveur Subversion mais vu les effets de bord, j'étais revenu en arrière, histoire de pas embêter mes petits camarades développeurs.
Comme toutes les choses ont une fin, c'est reparti de plus belle.
Heureusement, cette fois-ci, j'ai réussi à m'y coller, non sans avoir foutu un beau bordel pendant quelques heures.
Je vais quand même tenter de vous expliquer ce qu'il y avait à faire et comment je suis arrivé à mes fins.
Ça vous semblera peut-être très simple mais il y avait des pièges.
Imaginons un référentiel Subversion ayant la structure suivante :

/
├── Projet1
│   ├── branches
│   ├── tags
│   └── trunk
└── Projet2
    ├── branches
    ├── tags
    └── trunk

Un exemple simple de fichier d'accès est le suivant :

[groups]
projet1_devs=john, joe
projet1_admins=john
projet2_devs=jane, joe
projet2_admins=jane

[/]
* = r

[Projet1]
@projet1_devs = r

[Projet1/tags]
@projet1_admins = rw
@projet1_devs = r

[Projet1/branches]
@projet1_devs = rw

#Et ainsi de suite pour chaque projet

Jusque là, c'est pas trop grave mais tous les développeurs peuvent voir le code de chaque projet mais ne peuvent pas y écrire, on est typiquement dans le cas classique tel que décrit dans la doc.
Tant que l'on a confiance, pas de problème mais quand le doute vous habite, ça va plus.
Allons-y, fermons les portes !

Le premier réflexe est de retirer la partie liée au /... C'est une bonne idée mais dont le principal effet de bord est de gêner les outils de navigation dans le référentiel et qui oblige le développeur à connaître l'URL complète vers son projet.
Alors comment faire pour continuer à pouvoir browser tranquillement mais que dans les projets où on a les droits ?
Et bien, au lieu de mettre des droits, on en retire explicitement :

[groups]
projet1_devs=john, joe
projet1_admins=john
projet2_devs=jane, joe
projet2_admins=jane

[/]
* = r

[Projet1]
* =
@projet1_devs = r

[Projet1/tags]
* =
@projet1_admins = rw
@projet1_devs = r

[Projet1/branches]
* = 
@projet1_devs = rw

#Et ainsi de suite pour chaque projet

Vous avez remarqué la subtilité ?
Pour chaque section de projet, il suffit de retirer les droits mis à tous.
Bon, c'est simple mais fallait y penser.

jeudi 20 décembre 2012

Bissection !

non, je n'ai pas décidé de faire des expériences sur des animaux !
Non, je n'ai pas décidé de me faire greffer un deuxième organe sexuel, le mien me suffit[1].
J'ai décidé de faire un petit billet sur cette fonctionnalité que l'on trouve dans certains logiciels de gestion de versions qui n'est pas mal car elle aide à faire la recherche de la version qui a introduit une régression dans un logiciel.
D'ailleurs, il est normal de la trouver dans Monotone, Git et Mercurial vu que les historiques ne sont pas forcément linéaires.

Notes

[1] Plus, ce serait inconvenant :D

Lire la suite...

- page 1 de 21