vendredi 2 décembre 2011

SLIFIS: fuzzy logic C++ library first release

I have just uploaded on sourceforge the SLIFIS library, a long-standing project on which I can say I have spend quite some time... It is a C++ library designed to be used inside scientific software when you need some fuzzy logic stuff (membership functions, fuzzy inference system, ...)

It all started in 2008-2009, when I was in that situation, and had trouble finding what would fit my needs. Since then, the software was more or less in a standby situation, but I fixed some stuff recently, and decided to make a public release. A lot of things still need to be worked on, I hope to clean up things a bit and will keep an eye on this.

Checkout the (reduced) doc here, and the download project page is here.

At present, Windows build is not up to date, as Linux is now my main dev platform, but it shouldn't be too hard to make it work.

As usual with open source, absolutely no warranty is given..., blah blah blah.

Feedback is welcome.

mardi 18 octobre 2011

C++: switch on any data type (part 2)

(See part 1 of this post here.)

In this second version, the idea is to have the same function called on triggering, but with a different argument value for each case.

The class will be very similar to first version. The difference is that here, instead of storing the function in the map, we store the argument value. i.e. instead of having
std::map<KEY, FUNC>, we change to  std::map<KEY, ARG>.

Here is the full class. Don't forget to add the needed headers. And as usual with templated classes, this takes place in a header file.


template < typename KEY, typename FUNC, typename ARG > class SWITCH2
{
   public:
      SWITCH2()
      {
          myFunc = 0;
      }

      void Process( const KEY& key )
      {
         assert( myFunc ); // needed !
         typename std::map<KEY,ARG>::const_iterator it = myMap.find( key );
         if( it != myMap.end() )    // If key exists, call function
            myFunc( it->second );  // with correct argument
         else                       // else, call function with
            myFunc( DefArg );      // default argument
      }

      void AddArgument( const KEY key, ARG value )
      {
         myMap[ key ] = value;
      }
      void SetFunction( FUNC f )
      {
         myFunc = f;
      }
      void SetDefault( ARG def )
      {
         DefArg = def;
      }

   private:
      std::map< KEY, ARG > myMap;
      FUNC myFunc; // the function
      ARG  DefArg; // default value, the type needs
                   //   a default constructor
};

I removed here the uniqueness test, as it seemed less relevant in this situation. But you can write it back if needed.
Also note the 'assert()' checking, to make sure we don't call a function that hasn't been defined. You can easily replace this by some custom error handling function instead (exception, or whatever.)


And again, remember that this trick can only be used in specific cases where either you have to call different functions with the same argument, or the same function with different argument values.

Other solutions to this problem can be found (besides switching to another programming language, I mean...), you could also use function pointers, although this is less natural in C++.

mardi 14 juin 2011

C++: switch on any data type (part 1)

C++ is claimed to be a programming langage that allows Object Oriented programming. But for legacy reasons, it does not always allow "real" objet paradigm in certain situations. Yes, I'm talking about the "switch" statement.
As you might know, you can only switch on data types that can be downcasted to an 'int' type.

This comes of course from its proximity with C (that is sometimes considered as some upgrated assembler), and C++ programmers are now used to it. But from a strict Object Oriented paradigm, I believe this shouldn't be true.

But when would such a feature be needed anyway ? I'll show you an example.

Say you need to read data from a file that can be in two different formats. Say xml, csv, whatever. This will typically be done with two different functions. So the first step is identifying file type (by its extension, lets keep it simple), then call the correct function. So you have the following code:

if( ext == "xml" )
   return ReadFile_xml( ...
else
   if( ext == "csv" )
      return ReadFile_csv( ...
   else
      cerr << "Unrecognised file type !\n"; // or whatever other error handling

Okay, so, yes, this does the job. But what if you need to add two other file types and their associated functions ? The code before will start to look really really ugly and will become error prone. It would be much nicer if one could have this instead:
(for clarity, we have removed the handling of the returned value)

switch( ext )
{
   case "csv" : ReadFile_csv( ... ); break;
   case "xml" : ReadFile_xml( ... ); break;
   case "..." : ...
   ...
   default:
      cerr << "Unrecognized file type !\n";
}
return false;

Unfortunatly, as explained before, you can't do this in C++. No, Nada, Niet.

The example here is for strings, but can be transposed to a lot of similar situations. Say, how about some keyboard shortcut combination that you would want to switch on, or whatever.

What we would like is to have a "generic" solution that can be used for any data type. We present here two class-based solutions, one where you can call different functions for different "cases", the second where it is always the same function called, but with different argument values. They rely on template programming, with a provided class that holds all the necessary logic and can be reused elsewhere, just put the class in some header file and use it anywhere.

These solutions both have limitations at present. For the first flavour, the number of arguments is fixed in the templated class. You can of course edit it, but it is not fully generic at present.

Let's see first how the user code will look using the first solution. We show here an example where the argument to be passed to the function has the type string, but the point here is that this solution is independent of both types used (selector, and argument), as long as they meet certain expectations, see below.

// first, instanciate the class SWITCH with needed types
//                key type        function sig      arg type
    SWITCH mySwitch< string,   void(*)(const string&), string >;
// then, configure the "switch" object
    mySwitch.Add( "csv", ReadFile_csv );
    mySwitch.Add( "xml", ReadFile_xml );
    mySwitch.AddDefault( ReadFile_unknown );

// lets try some keyboard test
    string in;
    do
    {
        cout << "in: ";
        cin >> in;
        mySwitch.Process( in, filename ); // do the switch !
    }
    while( in != "0" );

   ...
}
// this function needs to have the following signature
void ReadFile_csv( const string& filename )
{
 ...
}

Now, lets see the class. It is based on an stl map. It is templated by three types, the key type (the "selector"), the function signature, and the functions argument type:

template < typename KEY, typename FUNC, typename ARG > class SWITCH
{
   public:
      SWITCH()
      {
         Def = 0; // no default function at startup
      }

      void Process( const KEY& key, ARG arg )
      {
         typename std::map< KEY, FUNC >::const_iterator it = my_map.find( key );
         if( it != my_map.end() )  // If key exists, call
             it->second( arg );    // associated function
         else               // else, call
            if( Def )       // default function, if there is one.
               Def( arg );  // Else, do nothing
      }

      void Add( const KEY& key, FUNC my_func )
      {
#ifdef CHECK_FOR_UNIQUENESS
         typename std::map< KEY, FUNC >::const_iterator it = my_map.find( key );
         if( it != my_map.end() )
         {
            cerr << "Already defined !\n";
            throw "Already defined !\n";
         }
#endif
         my_map[ key ] = my_func;
      }

      void AddDefault( FUNC f )
      {
          Def = f;
      }

   private:
      std::map< KEY, FUNC > my_map;
      FUNC Def; // default function
};

Some comments:
  • Please note the CHECK_FOR_UNIQUENESS test. If defined, then execution stops if class user attemps to store twice the same key. You can easily write your own error handler.
  • The KEY and the ARG type need to be copyable. This is a limitation. For instance, you could not use a file stream (std::ifstream for example) as these are not copyable. If you use a non-trivial class, make sure its assignement operator is defined.
  • As you may have noticed, the functions argument can be passed using its reference.

Part 2 is coming soon, in the meanwhile, you can check the following references on the same -or similar- subject:

lundi 30 mai 2011

Stage IUT & soutenance, conseils aux étudiants

Ce billet s'adresse plus spécifiquement aux étudiants GEII de l'IUT, mais peut éventuellement être utile à des étudiants dans d'autres situations. Je couvre ici l'aspect "valorisation" du stage, qui passe par une soutenance orale.

La soutenance est souvent déterminante dans la note finale. Qu'on le veuille ou non, nous vivons dans un monde de communication, et une prestation brillante peut parfois "sauver" un stage qui s'est mal déroulé. Attention, selon les grilles d'évaluation utilisées, ça ne fera pas des miracles, mais ca donnera au moins une image favorable au jury.

Objectifs généraux

La soutenance sert d'une part à montrer votre aptitude à rendre compte devant un auditoire du travail réalisé, d'autre part, à montrer au jury ce travail en en faisant une synthèse. L'objectif n'est pas d'expliquer au jury comment fonctionne telle ou telle technologie, mais de mettre en évidence la plus-value de votre stage. Il faut donc insister sur:
  • A - la situation de départ (introduction, présentation du sujet, objectifs initiaux, contexte, ...),
  • B - la situation à l'arrivée (en conclusion),
  • comment vous êtes passé de A à B.

Ceci tout en prenant en compte le fait que votre auditoire ne connaît ni l'entreprise, ni éventuellement le domaine précis sur lequel vous avez travaillé. Il est très important de prendre du recul par rapport au sujet: montrer le contexte général, l'entreprise / le service, son domaine d'activité, le rôle de ce service par rapport à ses "clients" au sens large, quel était le besoin initial ou le sujet proposé par rapport à tout ca. Vous pourrez ensuite présenter quelques éléments de votre stage (missions réalisées, outils utilisés, difficultés rencontrées et comment vous les avez résolues, ...). Vous ne pourrez pas détailler toutes vos activités, et même si vous jugez que toutes vos missions méritent d'être exposées, vous devrez choisir un angle principal. Prenez l'avis de votre encadrant IUT ou industriel en cas de doute.

Vous pourrez vous attarder sur un point technique, mais sans chercher a être exhaustif: le jury ne s'intéresse pas tant au travail réalisé en lui-même que à la façon dont vous avez traité une situation donnée.

Outre le bilan sur le travail réalisé, la conclusion doit mettre en évidence l'intérêt pour l'entreprise: en quoi votre travail lui a été bénéfique ? Vous pourrez aussi y exposer votre appréciation personnelle sur ce stage, en restant positif. Vous pouvez par exemple expliquer que ce stage vous a permis de découvrir telle ou telle technologie, ou qu'il vous a permis de developper vos capacités relationnelles, ou autre.
Si votre stage s'est mal passé quelqu'en soit la raison, la présentation n'est pas le moment de régler des comptes ni même d'expliquer une situation confictuelle. Ceci pourra se faire lors de l'entretien ultérieur, si besoin.

Si votre stage n'a pas eu de sujet central, comme cela arrive parfois, vous devez alors trouver une autre articulation: montrer que vous vous êtes intéressé plus en détail à l'un des thèmes techniques abordés par l'entreprise et/ou le service dans lequel vous avez été accueilli. En dehors de la présentation de votre travail, vous devrez alors developper une partie qui fera une synthèse autour de ce thème. Parlez-en avec votre encadrant de l'IUT.

Durée de la présentation

Dans notre département, il est demandé une présentation de 15 min maximum. En réalité, il s'agit d'une barrière haute, et à titre personnel je demande plutôt aux étudiants de préparer une présentation de 11, 12, voire 13 mn, mais pas au delà. En pratique, et ayant pas mal l'habitude de ce genre d'exercice, il vaut mieux une présentation courte mais (très) bien préparée, plutôt que quelque chose de soporifique. De toute façon, le jury reviendra lors de la phase des questions sur les points clés.

L'oral est également important. Une articulation claire et fluide, des phrases bien construites qui sortent sans peine, et vous avez gagné un point. Les étudiants sont inégalement doués sur ce terrain, et certains devront particulièrement travailler ce point, par de multiples répétitions.

Pour les diapos: quelques règles

Sur la forme, et encore plus que pour le rapport, la sobriété est payante. Les effets multicolores et autres animations de transition sont souvent du plus mauvais goût. Ils peuvent donner l'impression que vous essayer d'impressionner le jury par votre maîtrise des dernières fonctionnalités de Powerpoint version 12345, alors qu'en réalité, celui-ci pensera que vous cherchez à compenser la légèreté du fond. Mais ceci n'interdit pas d'illustrer ses diapos par des figures, au contraire. Des pages associant texte et illustrations permettent souvent de mieux faire comprendre un concept, et montrent souvent que le candidat a réflechi au message à faire passer. Je vois trop souvent des diapos avec un titre, une liste à puce de 3 items, et c'est tout. Une, ça va, mais s'il y en a 10 comme ça, on a vite compris que l'étudiant a préparé ça assez rapidement...

Ne pas trop (sur)charger quand même, l'équilibre est difficile à trouver, dans le doute demander des conseils à des proches (non-techniciens de préférence).

Faire attention au contraste aussi: les conditions d'éclairement ne sont pas toujours optimales, et un texte jaune sur fond marron se lira alors difficilement. La taille des polices utilisées doit être choisie avec soin. Quand au choix des polices, de la sobriété toujours...

Le nombre de diapos: c'est assez variable, et fonction du contenu en fait. Il faut que le jury ait le temps de lire le contenu, en association avec vos commentaires. Disons qu'on compte en général 1 à 2 mn par diapo.

Un point que beaucoup d'étudiants oublient: la numérotation des pages, très importantes pour que jury et candidat puissent se repérer lors de la séance de questions.

jeudi 3 mars 2011

Two scripts for packaging a source tree from a local repository

I'm currently maintaining a double build of some software projects, on both Windows and Linux. Svn is of course a great help, because it allows me to easily switch from one machine to another and finding my whole tree unchanged (with a network-hosted repository, of course). I could also use some virtual machine, but I happen to have enough machines available: my main laptop recently switched (more on this later!), and I still have Windows at work and at home. So it is kind of easier to work on separate machines, not to mention that virtual machines can have some specific trouble that you need to deal with, instead of focusing on your project.

Part of the job is letting the users try your code, users that don't necessarily have access to the svn repository, nor have/use the same dev tools you do. So the classical way is to upload somewhere the whole tree as a single archive, and let them fool around with the app.

A well-known post from Joel Spolsky (here in french) covers a list of what is required in dev teams to achieve a high quality level. And point n°2 is "Can you make a build in one step?". Besides the build process, this implies an efficient source tree release step, and that's what I'll talk about here. But what exactly does "release" mean ?

"Releasing" means here going from a source tree, with all binaries compiled and unit-tests done, to a single file uploaded somewhere, so users can download it the next morning. We do not cover here producing the more complex "self-installing" files, (linux .deb or .rpm, or windows .msi or .exe), only a regular archive, containing the whole source tree along with the needed binaries.

You can find below the two scripts, one for windows (.bat), and one for Linux (.sh). The Linux version only needs regular tools that should be available on every distribution, while the Windows versions needs 2 additional tools, the archiver and the svn command-line client. Yes, I know what you think: if you are using svn, then you have the command-line svn client ! Well, no, not necessarily. On windows, I use TortoiseSvn,  really nice and with a perfect shell integration. This gui software is not just a wrapper around the svn command-line client, it has its own binaries. So you need to install some other svn client if you're a TortoiseSvn user (they are available here).
For the archiver, I highly recommend 7-zip: has both GUI and CLI, high performance, good documentation, shell integration... and LGPL'ed ! Can't wait to have it on Linux.

The interesting point is that the two scripts here are project-independent: you just drop them in your root folder... and there you go! A simple double-clic will generate an archive (.zip or .tar.gz), containing a copy of the local repository, with the name like:

XXXXX_YYYYMMDD-HHMM_win32.zip for the windows version
XXXXX_YYYYMMDD-HHMM_linux.tar.gz for the linux version
with XXXXX being the project name, simply extracted from the root folder name. You can also run them from the command-line.

The only tweaking you need to do is edit the additional "upload" scripts, where the ftp command is issued: this is the only part that can not be automatic, as there is no way to automatically determine what the host is.

Inside the scripts, nothing really complicated, but I'm quite happy to achieve some bash scripting as well as I am used to do with windows scripting. I miss the 'goto' command, but I must say that bash is more efficient, even though I am more used to cmd.exe. The scripts mainly do an svn export, add the binaries (that shoudn't be versioned), and compress the whole thing, renaming it with the current date/time. Has been tested on XP-SP3 and Ubuntu 10.10, let me know if you experience some trouble.

Download link

Linux/bash version:

#!/bin/bash
echo Packaging linux archive

curpath=$(pwd)
name=$(basename $curpath)
#echo name=$name

now=$(date +%Y%m%d-%H%M)
fn=${name}_${now}_linux

echo "step 1 : export tree to temp path"
svn export . $HOME/tmp/$name

echo "step 2 : add binaries (not versioned)"
cp bin/* /tmp/$name/bin
cp lib/* /tmp/$name/lib

echo "step 3 : archive and compress"
cd /tmp
tar cfz $curpath/$fn.tar.gz $name
cd $curpath

echo "step 4 : cleanup"
rm -r $HOME/tmp/$name

echo "step 5 : upload"
./upload.sh

echo "done, file $fn.tar.gz available !"
read -p "press a key"


Windows version

@echo off
title Packaging windows archive

:: here, little trick to get the folder's name
set curr_path=%cd%
call :sp %curr_path%
::echo name=%name%

set archiver=C:\Program Files\7-Zip\7z.exe
set svnclient=C:\Program Files\Subversion\bin\svn.exe

:: step 0 : make sure all the tools are available
if not exist "%archiver%" goto err1
if not exist "%svnclient%" goto err2

echo step 1 : export tree to temp path
svn export . %temp%\%name% --native-eol CRLF

echo step 2 : add binaries (not versioned)
copy bin %temp%\%name%\bin > nul
copy lib %temp%\%name%\lib > nul

echo step 3 : compress
set year=%date:~6,4%
set month=%date:~3,2%
set day=%date:~0,2%
set hour=%time:~0,2%
if /I %hour% LSS 10 set hour=0%hour:~1,1%
set mn=%time:~3,2%
set now=%year%%month%%day%-%hour%%mn%
echo now=%now%
pause
"%archiver%" a "%name%_%now%_win32.zip" %temp%\%name% > nul

echo step 4 : cleanup
del /Q /S %temp%\%name%\* >nul
rd /S /Q %temp%\%name%

echo step 5 : upload
call upload.bat

echo done, file %name%_%now%_win32.zip available !
pause
goto :eof
============================================================
:err1
echo FAIL: archiver %archiver% not present !
pause
goto :eof
============================================================
:err2
echo FAIL: svnclient %svnclient% not present !
pause
goto :eof
============================================================
:sp
::echo sp, arg1=%1
set name=%~n1
goto :eof
============================================================