IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Préprocesseur Ruby pour C++


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre chevronné
    Avatar de NewbiZ
    Profil pro
    Étudiant
    Inscrit en
    Juillet 2002
    Messages
    184
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juillet 2002
    Messages : 184
    Par défaut Préprocesseur Ruby pour C++
    Bonjour,

    Actuellement je fait un préprocesseur C++ en Ruby.
    L'intéret ? C'est beaucoup plus simple et pratique à utiliser que le préprocesseur C++ normal, et puis Ruby est vraiment un langage très agréable pour faire des DSL. Si vous avez le temps, ce serait sympa de le tester et de me dire ce que vous en pensez, des fonctionnalités que vous aimeriez avoir, etc...
    Ci dessous, quelques exemples pour illustrer l'intéret de la bête.

    Quelques notes préliminaires :
    Vous pouvez insérer du code Ruby à n'importe quel endroit du fichier avec les conventions suivantes :
    <% code %> : Le code Ruby est interprété.
    <%= code %> : Le code Ruby est interprété et son résultat est inséré en place.
    % code : Si le premier caractère de la ligne est %, toute la ligne sera interprétée en Ruby.

    Si vous avez un répertoire rppconf à la racine de votre projet, vous pouvez y insérer des fichiers en langage YAML (un langage simplissime et esthétique pour sérialiser des données). Les données du fichier seront ensuite accessibles n'importe où dans votre source dans la variable <nom du fichier yaml>.!

    Si vous avez un fichier rpphelpers.rb à la racine de votre projet, vous pouvez aussi y insérer des helpers (fonctions pratiques) qui vous permettent de garder un code Ruby propre et clair dans vos sources.

    En Ruby, on dispose de "symboles", ce sont grossièrement l'équivalent de chaines de caractères constantes. Un symbole se préfixe par deux points ":". Par rapport à des chaines de caractères, l'intéret c'est que les symboles sont des singletons.

    Exemple 1:
    Créer un fichier nommé "project.yaml" dans le répertoire "rppconf", et mettre ceci dedans :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    ---
    :name: Ruby pre-processor example
     
    :authors:
    - NewbiZ
     
    :languages:
    - C++
    - Ruby
     
    :website: http://www.speednova.cc
     
    :usage: Available arguments are listed below
     
    :arguments:
    - :char: h
      :text: Display RCPP's help.
    - :char: v
      :text: Display RCPP's version number.
    - :char: d
      :text: Deprecated argument.
     
    :version: 1.2
    YAML est un langage qui fonctionne principalement sur le principe clef : valeur. Dans cet exemple nos clefs sont des symboles. On peut créer des listes en les préfixant par "-".
    On dispose donc maintenant d'une variable "project" accessible partout dans nos sources, et contenant ces valeurs.

    Créons donc un fichier example1.rpp (notez l'extension) pour tester :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    %# Helper function displaying text while generating files (see rpphelpers.rb)
    % echo "Pre-processing \"example1.cpp\""
     
    #include <cstdlib>
    #include <iostream>
     
    int main (int argc, char const* argv[])
    {
      std::cout << "This is "  << "<%= project[:name] %>"
                << " version " << "<%= project[:version] %>"
                << " build date: " << "<%= Time.now.to_s %>"
                << std::endl << std::endl;
     
      std::cout << "Visit us at " << "<%= project[:website] %>" << std::endl << std::endl;
     
      std::cout << "<%= project[:usage] %>" << std::endl;
     
    % project[:arguments].each do |argument|
      std::cout << "<%= argument[:char] %>" << " : " << "<%= argument[:text] %>"  << std::endl;
    % end
     
      return 0;
    }
    Après avoir executé ./rppgen.rb, vous obtiendrez le fichier example1.cpp suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    #include <cstdlib>
    #include <iostream>
     
    int main (int argc, char const* argv[])
    {
      std::cout << "This is "  << "Ruby pre-processor example"
                << " version " << "1.2"
                << " build date: " << "Thu May 08 20:17:38 +0200 2008"
                << std::endl << std::endl;
     
      std::cout << "Visit us at " << "http://www.speednova.cc" << std::endl << std::endl;
     
      std::cout << "Available arguments are listed below" << std::endl;
     
      std::cout << "h" << " : " << "Display RCPP's help."  << std::endl;
      std::cout << "v" << " : " << "Display RCPP's version number."  << std::endl;
      std::cout << "d" << " : " << "Deprecated argument."  << std::endl;
     
      return 0;
    }
    Exemple 2:
    Imaginez que vous vouliez créer un tableau contenant toutes les lettres de l'alphabet. C'est assez simple dynamiquement, mais statiquement c'est un cauchemard avec le préprocesseur C++. Voici ce que celà donne avec RPP :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
      // The old static way
      char alpha1[] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
      // The RPP inline way
      char alpha2[] = {'<% $src << ('a'..'z').to_a.join("','") %>'};
      // The RPP way using an helper function (see rpphelpers.rb)
      char alpha4[] = <%= c_array ('a'..'z') %>;
     
      // The dynamic way
      for (int i=0; i<26; i++)
        std::cout << "letter " << lcase1[i] << std::endl;
     
      // The RPP way
    % ('a'..'z').each do |c|
      std::cout << "letter " << '<%= c %>' << std::endl;
    % end
    On profite des facilités des "range" de Ruby pour générer le tableau.

    Vous obtenez exemple2.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
      // The old static way
      char alpha1[] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
      // The RPP inline way
      char alpha2[] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
      // The RPP way using an helper function (see rpphelpers.rb)
      char alpha4[] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
     
      // The dynamic way
      for (int i=0; i<26; i++)
        std::cout << "letter " << lcase1[i] << std::endl;
     
      // The RPP way
      std::cout << "letter " << 'a' << std::endl;
      std::cout << "letter " << 'b' << std::endl;
      std::cout << "letter " << 'c' << std::endl;
      std::cout << "letter " << 'd' << std::endl;
      std::cout << "letter " << 'e' << std::endl;
      std::cout << "letter " << 'f' << std::endl;
      std::cout << "letter " << 'g' << std::endl;
      std::cout << "letter " << 'h' << std::endl;
      std::cout << "letter " << 'i' << std::endl;
      std::cout << "letter " << 'j' << std::endl;
      std::cout << "letter " << 'k' << std::endl;
      std::cout << "letter " << 'l' << std::endl;
      std::cout << "letter " << 'm' << std::endl;
      std::cout << "letter " << 'n' << std::endl;
      std::cout << "letter " << 'o' << std::endl;
      std::cout << "letter " << 'p' << std::endl;
      std::cout << "letter " << 'q' << std::endl;
      std::cout << "letter " << 'r' << std::endl;
      std::cout << "letter " << 's' << std::endl;
      std::cout << "letter " << 't' << std::endl;
      std::cout << "letter " << 'u' << std::endl;
      std::cout << "letter " << 'v' << std::endl;
      std::cout << "letter " << 'w' << std::endl;
      std::cout << "letter " << 'x' << std::endl;
      std::cout << "letter " << 'y' << std::endl;
      std::cout << "letter " << 'z' << std::endl;
    Exemple 3:
    Il arrive souvent qu'ait des classes à créer et qu'on se retrouve à retaper le même code pour les accesseurs, les classes à hériter, etc... Voici un exemple de ce que celà donne avec RPP :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    class Displayable
    {
      std::string toString() const = 0;
    };
     
    class Starship
    {
    private:
    % declare_fields :serialNumber => :int, :name => :string, :leftEngine => :Engine, :rightEngine => :Engine
     
    public:
    % encapsulate :serialNumber => :int, :name => :string
    % getters :leftEngine => :Engine, :rightEngine => :Engine
    };
     
    % classdef(:Warship,
    %          :implements => [ :Starship, :Displayable ],
    %          :fields => { :ammo => :int, :turret => :Weapon, :target => :Position }) {
     
    public:
      string toString() const
      {
        std::cout << "ammo  =" << get_ammo()   << std::endl;
        std::cout << "turret=" << get_turret() << std::endl;
        std::cout << "target=" << get_target() << std::endl;
      }
     
    % }
    Ici on a créé (en quelques secondes) des helpers pour clarifier notre code.
    "declare_fields" prend en paramètre une suite de noms d'attributs, suivis de leur type, et génère leur déclaration.
    "getters" génère des fonctions de la forme get_<nom attribut> et
    "setters" génère des fonctions de la forme set_<nom_attribut>.
    "encapsulate" génère les getters et les setters en une seule fois.
    On a déjà économisé par mal de recopiage!

    Pour encore plus de rapidité, le dernier exemple est radical, le helper "classdef" prend en paramètre le nom de la classe, les noms des classes à implémenter, et les attributs avec leurs types, et génère directement la classe avec les getters et setters appropriés. Comme on le voit dans l'exemple, vous pouvez tout de même rajouter ce que vous voulez dans le corps de la déclaration de la classe.

    Voici ce que RPP a généré dans exemple3.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
     
     
    class Displayable
    {
      std::string toString() const = 0;
    };
     
    class Starship
    {
    private:
      Engine rightEngine;
      string name;
      int serialNumber;
      Engine leftEngine;
     
    public:
     
      void set_name( const string& value )
      {
        name = value;
      }
     
      void set_serialNumber( const int& value )
      {
        serialNumber = value;
      }
     
      const string& get_name() const
      {
        return name;
      }
     
      const int& get_serialNumber() const
      {
        return serialNumber;
      }
     
      const Engine& get_rightEngine() const
      {
        return rightEngine;
      }
     
      const Engine& get_leftEngine() const
      {
        return leftEngine;
      }
    };
     
    class Warship  : public Starship, public Displayable
    {
    public:
      void set_turret( const Weapon& value )
      {
        turret = value;
      }
     
      void set_target( const Position& value )
      {
        target = value;
      }
     
      void set_ammo( const int& value )
      {
        ammo = value;
      }
     
      const Weapon& get_turret() const
      {
        return turret;
      }
     
      const Position& get_target() const
      {
        return target;
      }
     
      const int& get_ammo() const
      {
        return ammo;
      }
     
    private:
      Weapon turret;
      Position target;
      int ammo;
     
    public:
      string toString() const
      {
        std::cout << "ammo  =" << get_ammo()   << std::endl;
        std::cout << "turret=" << get_turret() << std::endl;
        std::cout << "target=" << get_target() << std::endl;
      }
     
    };
    On pourrait aussi imaginer générer une batterie de constructeurs avec classdef, ce serait très rapide à faire.

    Voilà pour la petite présentation, si vous voulez tester, voici un package comprenant RPP (Ruby PreProcessor) + les codes des exemples + les helpers des exemples + le fichier YAML des exemples.

    J'espère que certains trouveront ca utile Dans tous les cas n'hésitez pas à poster vos avis, demandes, ...

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Je me dis que ce pourrait sans doute être intéressant pour Rüd qui cherchait justement comment faire pour afficher des informations à la compilation...

    Je transmet l'adresse de cette discussion
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre chevronné
    Avatar de NewbiZ
    Profil pro
    Étudiant
    Inscrit en
    Juillet 2002
    Messages
    184
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juillet 2002
    Messages : 184
    Par défaut
    Un petit ajout pour montrer encore l'intéret de la chose (issu d'une discussion avec Thelvyn).
    Le but est d'obtenir une fonctionnalité équivalente à un is_base_of<Base,Derived> de boost. Voici ce que celà donnerait avec RPP:

    Dans rpphelpers.rb, rajouter la ligne suivante au début de la fonction "defclass" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    $classes[name] = options
    Ainsi que la ligne suivante au début du fichier :
    Maintenant, à chaque fois que nous déclarons une classe, celle-ci sera stockée dans une variable globale $classes, avec ses métadonnées, et sera accessible de partout!
    Il ne reste donc plus qu'à rajouter les fonctions :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    def dump(name)
      # Existing class ?
      return if !$classes[name]
      # Recursive call for parents classes
      $classes[name][:implements].each { |c| dump c } if $classes[name][:implements]
      # Iterating through fields
      if $classes[name][:fields] then
        $classes[name][:fields].each_pair do |field_name,field_type|
          $src << %{std:cout << "#{name}.#{field_name} (#{field_type}) = " << #{field_name} << std::endl;\n}
        end
      end
    end
     
    def is_base_of?(base, derived)
      $classes[derived][:implements].include?(base) or base==derived
    end
    L'intéret de la fonction "dump" est assez simple à comprendre, et profite du fait que l'on puisse dorénavant accéder aux métadonnées des classes (attributs, classes parentes...). Elle affiche le contenu de tous les champs (hérités ou non) de la classe.

    Faisons un petit essai de tout celà :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    % classdef(:Warship,
    %          :fields => { :ammo => :int, :turret => :Weapon, :target => :Position }) {
    // ...
    % }
     
    % classdef(:Battleship,
    %          :implements => [ :Warship ],
    %          :fields => { :marines => :int, :firebats => :int, :medics => :int }) {
     
    public:
      string toString() const
      {
        <% dump :Battleship %>
        std::cout << "Battleship inherits from Warship ?" << <%= is_base_of?(:Warship,:Battleship) %> << std::endl;
      }
     
    % }
    Et on obtient :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
     
    class Warship 
    {
    public:
      void set_turret( const Weapon& value )
      {
        turret = value;
      }
     
      void set_target( const Position& value )
      {
        target = value;
      }
     
      void set_ammo( const int& value )
      {
        ammo = value;
      }
     
      const Weapon& get_turret() const
      {
        return turret;
      }
     
      const Position& get_target() const
      {
        return target;
      }
     
      const int& get_ammo() const
      {
        return ammo;
      }
     
    private:
      Weapon turret;
      Position target;
      int ammo;
    // ...
    };
     
    class Battleship  : public Warship
    {
    public:
      void set_firebats( const int& value )
      {
        firebats = value;
      }
     
      void set_medics( const int& value )
      {
        medics = value;
      }
     
      void set_marines( const int& value )
      {
        marines = value;
      }
     
      const int& get_firebats() const
      {
        return firebats;
      }
     
      const int& get_medics() const
      {
        return medics;
      }
     
      const int& get_marines() const
      {
        return marines;
      }
     
    private:
      int firebats;
      int medics;
      int marines;
     
    public:
      string toString() const
      {
        std:cout << "Warship.turret (Weapon) = " << turret << std::endl;
        std:cout << "Warship.target (Position) = " << target << std::endl;
        std:cout << "Warship.ammo (int) = " << ammo << std::endl;
        std:cout << "Battleship.firebats (int) = " << firebats << std::endl;
        std:cout << "Battleship.medics (int) = " << medics << std::endl;
        std:cout << "Battleship.marines (int) = " << marines << std::endl;
     
        std::cout << "Battleship inherits from Warship ?" << true << std::endl;
      }
     
    };

  4. #4
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    N'est-ce pas un peu de la triche ? Tu sais qu'une classe dérive d'une autre uniquement parce que les deux classes ont été déclarées par un mécanisme spécifique et non standard. Si je voulais faire la même chose en C++, je ferais des macros qui enregistrent des chaînes dans une map et utiliserais ces noms comme noms de classes.

    Mais ça ne servirait à rien, car une des contraintes du C++ est de pouvoir mixer du code écrit par des personnes différentes. Donc supposer qu'on peut/veut modifier le code de déclaration des deux classes est un peu irréaliste.

    Comment faire dans ton cas pour avoir un :

    is_base_of?(:std::iostream,:std::ofstream)


    Dit autrement, un pré-processeur écrit dans un langage riche peut présenter de l'intérêt pour moi, mais uniquement à condition qu'il soit non intrusif, et ne demande pas de modifier du code existant pour fournir des résultats corrects. Est-ce le cas de ce que tu proposes ?
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  5. #5
    Membre chevronné
    Avatar de NewbiZ
    Profil pro
    Étudiant
    Inscrit en
    Juillet 2002
    Messages
    184
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juillet 2002
    Messages : 184
    Par défaut
    Effectivement, c'est <le> gros problème du système, et pour l'instant... je ne suis pas arrivé à des réponses réellement satisfaisantes.

    Une solution serait de fournir un mécanisme simple de déclaration de classe (sans génération de code) au préprocesseur, pour qu'il connaisse les classes déclarées en pur C++. Quelquechose du type :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class Foo
    {
      int n;
    };
    // ...
    % classdecl :Foo, :fields => [ :n => :int ]
    % classdef :Bar,
               :implements => [ :Foo ] {
    // ...
    % }
    Cette méthode pourrait être couplée avec une batterie de définitions fournies de base pour la STL. (pour résoudre ton exemple).

    Alors bien sur, ce n'est pas parfaitement satisfaisant. Il semblerait que pour le moment la meilleure solution pour utiliser RPP, ce serait de l'utiliser à fond, et de manière exclusive. (comprendre, un projet avec peu de librairies non standards)

    L'autre solution, ce serait d'intégrer à RPP une FSM basique de reconnaissance de la grammaire du C++, pour en extraire les définitions de classes.
    C'est pas un travail énorme, et ca reste dans l'ordre du réalisable. (Il existe de bons outils de génération de FSM en Ruby, et la grammaire du C++ est bien connue et documentée).

    Dernière solution, de loin la meilleure, mais aussi la plus couteuse en temps de développement :
    Ruby est un langage <entre autres> spécialisé dans la création de DSL, notamment grace à d'énorme capacités de métaprogrammation. On trouve par exemple de nombreuses implémentations de prolog sous forme de DSL en Ruby. Il pourrait être intéressant d'étudier la possibilité d'en faire de même pour un sous-ensemble de C++: l'aspect purement déclaratif.

    Toutes suggestions sont les bienvenues

Discussions similaires

  1. Traduire un script ruby pour vérifier un bug
    Par vinc-mai dans le forum GTK+ avec Python
    Réponses: 4
    Dernier message: 07/03/2010, 21h03
  2. [Associé] Developpeur RUBY pour projet Web détonant!
    Par SylvieM dans le forum Autres
    Réponses: 0
    Dernier message: 29/05/2009, 20h21
  3. [FMOD] Adaptation Ruby pour FMOD
    Par sunmat dans le forum FMOD
    Réponses: 1
    Dernier message: 22/06/2008, 16h15
  4. Utilser Ruby pour du code C/C++
    Par Lutarez dans le forum C++
    Réponses: 1
    Dernier message: 06/02/2008, 15h41
  5. Python ou Ruby pour débuter ?
    Par TallyHo dans le forum Débuter
    Réponses: 17
    Dernier message: 02/08/2007, 22h32

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo