Sujet n°13233
Posté par Ku'rei le 11 Oct - 02:23 (2013)
Titre : Classes Abstraites en Ruby
Bonsoir tout le monde,

J'ai déjà utiliser plusieurs langages de programmation ; des langages souvent plus complexes et complets que Ruby (souvent parce que la fac m'a fait travailler sur Scratch...). Ces langages possède la possibilité de travailler avec des classes abstraites et des interfaces, on ne retrouve pas cela avec Ruby. Voici donc mes questions :

Pourquoi n'y a-t-il pas d'abstraction dans Ruby ?

J'ai écrit un script Ruby simulant les classes abstraites, bien que je me dis que l'écrire en C aurait été plus judicieux, j'aimerais savoir ce que vous en pensez :
Spoiler
Code:
#=========================================================================== 
# *** AbstractClass 
#--------------------------------------------------------------------------- 
# Classe mère définissant une class abstraite dans Ruby 
#=========================================================================== 
class AbstractClass 
  @@abstract_methods = Hash.new(nil) 
   
  #=========================================================================== 
  # *** AbstractClassNotImplementedError < NoMethodError 
  #--------------------------------------------------------------------------- 
  # Erreur quand une méthode de classe abstraite mère n'est pas implémentée   
  # par la classe fille 
  #=========================================================================== 
  class AbstractClassNotImplementedError < NoMethodError 
  end 
   
  #=========================================================================== 
  # *** AbstractClassInstantiationError < TypeError 
  #--------------------------------------------------------------------------- 
  # Erreur quand une classe abstraite est instanciée. 
  #=========================================================================== 
  class AbstractClassInstantiationError < TypeError 
  end 
 
  #=========================================================================== 
  # * initialize
  #--------------------------------------------------------------------------- 
  # Soulève l'erreur d'instantiantion d'une classe abstraite si elle est
  # abstraite.
  #=========================================================================== 
  def initialize
    raise AbstractClassInstantiationError.new("Can't instantiate the abstract class #{self.class}")   if self.class.is_abstract?
  end
 
  #=========================================================================== 
  # * self.is_abstract 
  #--------------------------------------------------------------------------- 
  # Définit la classe comme étant abstraite. Créer un champs dans la liste des 
  # méthodes abstraites 
  #=========================================================================== 
  protected 
  def self.is_abstract 
    @@abstract_methods[self] = []
  end 
   
  #=========================================================================== 
  # * self.is_abstract? 
  #--------------------------------------------------------------------------- 
  # Test si la class est abstraite ou non. 
  # 
  # A placer en première ligne de la classe abstraite. 
  #=========================================================================== 
  protected 
  def self.is_abstract? 
    return (@@abstract_methods[self] != nil) 
  end 
   
  #=========================================================================== 
  # * self.abstract_method(name, *args) 
  #       name : Symbole, nom de la méthode 
  #       *args:  Liste des arguments de la méthode sous forme de Symbole 
  #               ou de tableau [:nom_arg, valeur par défaut] 
  #--------------------------------------------------------------------------- 
  # Indique une méthode abstraite à implémenter par les classes filles 
  #=========================================================================== 
  protected 
  def self.abstract_method(name, *args) 
    if !is_abstract? 
      return 
    end 
     
    tab = [name] 
    for arg in args 
      tab += [arg.type == Symbol ? arg.to_s : arg.join(" = ")] 
    end 
    @@abstract_methods[self].push tab 
  end 
   
  #=========================================================================== 
  # * self.is_implementation 
  #--------------------------------------------------------------------------- 
  # Définis la classe comme étant l'implémentation de ses classes mères étant 
  # obligatoirement abstraites. Lève une erreur en cas d'oubli d'implémentation 
  # 
  # A placer en dernière ligne de la définition de la classe concrète 
  #=========================================================================== 
  protected 
  def self.is_implementation 
    # Check de l'implémentation des methodes abstraites 
    supclass = self.superclass 
    while supclass.is_abstract? 
      for abs_meth in @@abstract_methods[supclass] 
        if !self.method_defined?(abs_meth[0]) 
          raise AbstractClassNotImplementedError.new("#{self} need to implement #{abs_meth[0]}(#{abs_meth[1..-1].join(",")}) from abstract class #{supclass}.") 
        end 
      end 
       
      supclass = supclass.superclass 
    end 
  end 
end 
 
#=============================================================================== 
# *** Classes de test 
#=============================================================================== 
class AbstractTruc < AbstractClass 
  is_abstract 
  abstract_method :truc 
  def meth 
    print "meth" 
  end 
end 
 
class ConcreteTruc < AbstractTruc 
  def truc 
  end 
  is_implementation 
end 
 
class AbstractMachin < AbstractTruc 
  is_abstract 
  abstract_method :methode_machin, [:v1, 100] 
end 
 
class ConcreteMachin < AbstractMachin 
  def initialize 
    print "concrete" 
  end 
  def truc 
  end 
  def methode_machin 
  end 
  def meth2 
    print "meth2" 
  end 
  is_implementation 
end 
 
a = ConcreteMachin.new  # --> concrete 
a.meth                  # --> meth 
a.meth2                 # --> meth2 

a = ConcreteTruc.new
b = AbstractTruc.new    # --> Erreur 


PS:: Il est possible que je ne soit pas dans la bonne section. Si c'est le cas, excusez moi.

Posté par Nuri Yuri le 11 Oct - 16:43 (2013)
Mehr tu es fou @_@
J'ai regardé rapidement ce qu'était une classe abstraite : «En programmation orientée objet (POO), une classe abstraite est une classe dont l'implémentation n'est pas complète et qui n'est pas instanciable. Elle sert de base à d'autres classes dérivées (héritées).»

En ruby, il y a les modules - qui sont bien plus pratiques que les classes abstraites - et pour simuler une classe abstraite plutôt que de coder une mécanisme qui sera hyperlent il suffit de faire un include.
Exemple t'explicitant la chose (et pas besoins d'écrire des choses en plus dans ruby) :

MaClasseAbstraite ne peux être instanciée et sert de base à MaClasse donc selon la définition c'est une classe «abstraite».

J'espère que ça va plus t'aider que ce que tu as codé parce que je vois des variables de classe et ça présage rien de bon @_@

Posté par Ku'rei le 11 Oct - 19:19 (2013)
Oui cependant, on ne retrouve pas la possibilité de créer des méthodes abstraites, ni des interfaces. Et puis, les vérifications se font au début du jeu donc cela peut ne pas être trop génant.

Pour mon script, oui, celui là ne fonctionne pas. J'en ai un autre qui gère aussi la création d'interfaces (ensemble de méthodes abstraites), j'utilise en tout deux variables de classes (des Hash) pour stocker les méthodes abstraites.

Quel est le problème avec les variables de classes ? S'il y a possibilité de les remplacer par des constantes de classe/module, est-ce préférable ?

Voici le deuxième script :
Spoiler
Code:
#=========================================================================== 
# *** AbstractClass 
#--------------------------------------------------------------------------- 
# Classe mère définissant une class abstraite dans Ruby 
#=========================================================================== 
class AbstractClass 
  @@abstract_methods = Hash.new(nil) 
  def self.add_abstract_method(klass, tab)
    @@abstract_methods[klass] += [tab]
  end
  def self.abstract_methods
    return @@abstract_methods.dup
  end
   
  #=========================================================================== 
  # *** AbstractClassNotImplementedError < NoMethodError 
  #--------------------------------------------------------------------------- 
  # Erreur quand une méthode de classe abstraite mère n'est pas implémentée   
  # par la classe fille 
  #=========================================================================== 
  class AbstractClassNotImplementedError < NoMethodError
  end 
   
  #=========================================================================== 
  # *** AbstractClassInstantiationError < TypeError 
  #--------------------------------------------------------------------------- 
  # Erreur quand une classe abstraite est instanciée. 
  #=========================================================================== 
  class AbstractClassInstantiationError < TypeError 
  end 
 
  #=========================================================================== 
  # * initialize
  #--------------------------------------------------------------------------- 
  # Soulève l'erreur d'instantiantion d'une classe abstraite si elle est
  # abstraite.
  #=========================================================================== 
  def initialize
    raise AbstractClassInstantiationError.new("Can't instantiate the abstract class #{self.class}")   if self.class.is_abstract?
  end
 
  #=========================================================================== 
  # * self.is_abstract_class 
  #--------------------------------------------------------------------------- 
  # Définit la classe comme étant abstraite. Créer un champs dans la liste des 
  # méthodes abstraites 
  #=========================================================================== 
  protected 
  def self.is_abstract_class 
    @@abstract_methods[self] = []
  end 
   
  #=========================================================================== 
  # * self.is_abstract? 
  #--------------------------------------------------------------------------- 
  # Test si la class est abstraite ou non. 
  # 
  # A placer en première ligne de la classe abstraite. 
  #=========================================================================== 
  protected 
  def self.is_abstract? 
    return (@@abstract_methods[self] != nil) 
  end 
   
  #=========================================================================== 
  # * self.abstract_method(name, *args) 
  #       name : Symbole, nom de la méthode 
  #       *args:  Liste des arguments de la méthode sous forme de Symbole 
  #               ou de tableau [:nom_arg, valeur par défaut] 
  #--------------------------------------------------------------------------- 
  # Indique une méthode abstraite à implémenter par les classes filles 
  #=========================================================================== 
  protected 
  def self.abstract_method(name, *args) 
    if !is_abstract? 
      return 
    end 
     
    tab = [name] 
    for arg in args 
      tab += [arg.type == Symbol ? arg.to_s : arg.join(" = ")] 
    end 
    @@abstract_methods[self].push tab 
  end 
   
  #=========================================================================== 
  # * self.implement_abstract_class 
  #--------------------------------------------------------------------------- 
  # Définis la classe comme étant l'implémentation de ses classes mères étant 
  # obligatoirement abstraites. Lève une erreur en cas d'oubli d'implémentation 
  # 
  # A placer en dernière ligne de la définition de la classe concrète 
  #=========================================================================== 
  protected 
  def self.implement_abstract_class 
    # Check de l'implémentation des methodes abstraites 
    supclass = self.superclass 
    while supclass.is_abstract? 
      for abs_meth in @@abstract_methods[supclass] 
        if !self.method_defined?(abs_meth[0]) 
          raise AbstractClassNotImplementedError.new("#{self} need to implement #{abs_meth[0]}(#{abs_meth[1..-1].join(",")}) from abstract class #{supclass}.") 
        end 
      end 
       
      supclass = supclass.superclass 
    end 
  end
 
end 


#===========================================================================
# *** AbstractInterface
#---------------------------------------------------------------------------
# Module gérant la définition des interfaces abstraites.
#===========================================================================
module AbstractInterface
  @@abstract_methods = Hash.new([])
  def self.add_abstract_method(interface, tab)
    @@abstract_methods[interface] += [tab]
  end
  def self.abstract_methods
    return @@abstract_methods.dup
  end
 
  #===========================================================================
  # *** InterfaceNotImplementedError < NoMethodError
  #---------------------------------------------------------------------------
  # Erreur quand une méthode d'interface n'est pas implémentée
  #===========================================================================
  class InterfaceNotImplementedError < NoMethodError
  end
 
  #===========================================================================
  # *** AbstractInterface::Definition
  #---------------------------------------------------------------------------
  # Module à inclure dans les modules où sont définis les interfaces.
  # Exemple :
  #   module I_NomInterface
  #     include AbstractInterface::Definition
  #     
  #     abstract_method :nom_method, :nom_arg1, [:nom_arg2, valeur_par_defaut]
  #   end
  #===========================================================================
  module Definition
   
    #===========================================================================
    # * self.included(interface)
    #     interface : Module, module incluant ce module
    #---------------------------------------------------------------------------
    # Etend les methodes de définition d'interface.
    #===========================================================================
    def self.included(interface)
      interface.send(:extend, AbstractInterface::Definition::Methods)
    end
   
    #===========================================================================
    # *** AbstractInterface::Definition::Methods
    #---------------------------------------------------------------------------
    # Méthodes de définitions d'interface.
    #===========================================================================
    module Methods
     
      #=========================================================================== 
      # * abstract_method(name, *args) 
      #       name : Symbole, nom de la méthode abstraite
      #       *args:  Liste des arguments de la méthode sous forme de Symbole 
      #               ou de tableau [:nom_arg, valeur par défaut] 
      #--------------------------------------------------------------------------- 
      # Indique une méthode abstraite.
      #===========================================================================
      def abstract_method(name, *args)
        tab = [name] 
        for arg in args 
          tab += [arg.type == Symbol ? arg.to_s : arg.join(" = ")] 
        end
        AbstractInterface.add_abstract_method(self, tab)
      end
     
    end
   
  end
 
 
  #===========================================================================
  # *** AbstractInterface::Implementation
  #---------------------------------------------------------------------------
  # Module à inclure dans les modules où sont définis les interfaces.
  # Exemple :
  #   class NomClasse
  #     include AbstractInterface::Implementation
  #
  #     # Corps de la classe
  #     
  #     implement_interface I_Interface1, I_Interface2
  #   end
  #===========================================================================
  module Implementation
    #===========================================================================
    # * self.included(klass)
    #     klass : Class, classe incluant le module
    #---------------------------------------------------------------------------
    # Etend les methodes d'implémentation d'interface.
    #===========================================================================
    def self.included(klass)
      klass.send(:extend, AbstractInterface::Implementation::Methods)
    end
   
    #===========================================================================
    # *** AbstractInterface::Implementation::Methods
    #---------------------------------------------------------------------------
    # Méthodes d'implémentation d'interface.
    #===========================================================================
    module Methods
     
      #=========================================================================
      # * implement_interfaces(*args)
      #     *args : Object, liste des interfaces implémentées par l'objet.
      #-------------------------------------------------------------------------
      # Teste si toutes les méthodes définis par les interfaces sont implémentés
      # par la classe.
      #
      # A placer en dernière ligne de la définition de la classe.
      #
      # En cas de classe abstraite implémentant une interface, mettre le
      # implement_interfaces avant le abstractclass_implementation, les méthodes
      # abstraites devant être définie par la classes seront considérées comme
      # des méthodes abstraites de la classe abstraite.
      #=========================================================================
      def implement_interfaces(*args)
        is_abs = (self.ancestors.include?(AbstractClass) and self.is_abstract?)
       
        for interface in args
          for tab_meth in AbstractInterface.abstract_methods[interface]
            if is_abs
              AbstractClass.add_abstract_method(self, tab_meth)
            else
              if !method_defined?(tab_meth[0])
                raise InterfaceNotImplementedError.new("#{self}  need to implement '#{tab_meth[0]}(#{tab_meth[1..-1].join(",")})' for interface  #{interface}")
              end
            end
          end
        end
      end
     
    end
   
  end
 
end

Avec un exemple d'utilisation (temps entre le haut et la fin du script : 0.001)
Spoiler
Code:
#===============================================================================
# Interfaces de test
#===============================================================================
module I_Machin
  include AbstractInterface::Definition
 
  abstract_method :meth_0
  abstract_method :meth_1
end

module I_Truc
  include AbstractInterface::Definition
 
  abstract_method :meth_2
end

#===============================================================================
# Classes abstraites de test
#===============================================================================
class A_Bidule < AbstractClass
  is_abstract_class
 
  abstract_method :meth_a_0
  abstract_method :meth_a_1
end

class A_Machin < AbstractClass
  include AbstractInterface::Implementation

  is_abstract_class
 
  abstract_method :meth_a_2
 
  implement_interfaces I_Truc
end

class A_Machin_Fille < A_Machin
  is_abstract_class
 
  def meth_a_2
  end
 
  abstract_method :meth_a_3
end

#===============================================================================
# Classes de test
#===============================================================================
class Machin
  include AbstractInterface::Implementation
 
  def meth_0
  end
  def meth_1
  end
  def meth_2
  end
 
  implement_interfaces I_Machin, I_Truc
end

class Machin2 < A_Machin_Fille
  def meth_a_3
  end
  def meth_2
  end
 
  implement_abstract_class
end

class Machin3 < Machin2
  include AbstractInterface::Implementation
 
  def meth_0
  end
  def meth_1
  end
 
  implement_interfaces I_Machin
end

#print AbstractInterface.abstract_methods.inspect
#print AbstractClass.abstract_methods.inspect


EDIT:: Je ne pense pas que les modules puissent gérer complètement les classes abstraites : ils ne peuvent pas gérer l'héritage des attributs de la classe abstraite.

Posté par Nuri Yuri le 11 Oct - 19:48 (2013)
Les propriétés des méthodes sont hérités.

Sinon, explique moi plus en détails ce que tu entends par méthode abstraite et interface et je te donne des équivalents. (Pour les attributs, si tu parles de attr_accessor saches que attr_x ne fait que générer des méthodes en ruby il est impossible d'accéder directement à une variable d'instance, même en ayant la structure de ta classe.)

Posté par Ku'rei le 11 Oct - 20:17 (2013)
Je sais que attr_accessor créer les getter et setter des variables passées en paramètres.

Avant que je te donne ma définition, tu es en train de me dire que si je fais :
Code:
module MaClasseAbstraite
  @une_variable = 0
  def ajouter_un
    @une_variable += 1
  end

  def afficher
    print @une_variable
  end
end

class MaClasseConcrete
  include MaClasseAbstraite
end

a = MaClasseConcrete.new
b = MaClasseConcrete.new
5.times do
  a.ajouter_un
end

a.afficher
b.afficher

Ca m'affichera 5 et 0 ?

Méthode abstraite : méthode sans corps, on ne connait que sa spécification (portée, type retourné, nom, paramètres).
Classe abstraite : classe possédant des méthodes abstraites, c'est aux classes filles d'implémenter les méthodes concrète. Ne peut être instanciée.
Interface : ensemble de méthodes abstraites, on peut apparenter ça a une classe possédant uniquement des méthodes abstraites et n'ayant pas d'attributs. Ne peux être instanciée.

La différence entre classe abstraite et interface : une classe représente un type d'objet dont on ne connait pas tout les comportements (exemple : classe mère d'un type Game_Item qui définit la méthode use_it mais ne lui donne aucun corps (on ne peut pas l'utiliser), il faut définir le corps de la méthode dans les classes filles de Game_Item, par exemple Potion, SuperBonbon, ...).

On peut créer une interface pour chaque type d'objet (I_Soin, I_ObjetRare, I_ObjetCOmbat, ...) qui indiqueront aux classes objet quelles méthodes spéciales elles doivent implémenter (pour I_Soin ce serait get_heal_value(), pour I_ObjetCombat ce serait get_increased_stat(), ...)

Ainsi, on obtiendrait des objets descendant de Game_Item, mais ayant les méthodes nécessaire pour répondre aux besoins d'une relation avec une autre classe. Il y a beaucoup de cours sur les interfaces et classes abstraites et je ne pense pas être celui qui explique le mieux ^^

Posté par Nuri Yuri le 11 Oct - 20:42 (2013)
Ainsi tu as ici la raison pour laquelle il n'y a pas de classe et méthode abstraite en ruby : ça ne sert à rien en ruby !
Pour faire simple : Le ruby ne gère pas la pré-définition : c'est un langage de trèèèèèèèèèèèèèèèèèèèèèès haut niveau par conséquent les méthode abstraites ne peuvent pas exister car les méthodes ne se prédéfinissent pas elles se définissent une bonne fois pour toute.
Les méthodes ne prennent pas de types d'objets et ne retournent pas de type d'objet donc, à quoi bon définir les méthodes de manière abstraite ? C'est juste consommer de la mémoire, malmener l'ObjectSpace rendre plus lent le GarbageCollector.

Simuler les interface, classes et méthodes abstraites c'est d'autant plus con car tu vas consommer de la mémoire et des définitions d'objets en plus de segment de bytecode interprétables par ruby enfin bref, c'est une vraie catastrophe.

Lorsque tu crée une classe abstraite ou une fonction abstraite en c++ tu dis juste au compilateur que tu définie ces méthodes et il va te le rappeler pour les classes qui en descendent mais il ne vas jamais allouer d'espace dans le code moteur de ton programme pour ces méthodes, les allocations restent dans la phase de compilation où il dit bon, il y a cette méthode là dans cette classe, il ne lui a pas attribué de code dans la classe enfant, je vais un peu lui gueuler dessus.

Enfin bref. Ne cherche pas à copier des trucs de précompilation en ruby, c'est inutile et con, le ruby ne précompile rien il exécute tout ce que tu lui donnes.

Ceci explique aussi en partie pourquoi je n'utilise pas l'abstract dans les autres langages, dans le fond, ça sert pas à grand chose, c'est juste pour casser les couilles du programmeur et générer des fichiers .h/.cpp plus lourds pour faire des «watchdog». Je vais être un peu brute mais lorsque tu programmes, tu sais ce que tu fais ou tu ne fais rien, quand tu rédige ta copie de Maths il y a pas le prof derrière qui va te dire, Oh non, tu n'as pas le droit d'utiliser le théorème de comparaison par équivalent les termes de ta suite ne sont pas positif, tu dois le savoir et puis c'est tout pas le droit aux antisèches dans la caltosh.

Posté par Ku'rei le 11 Oct - 21:19 (2013)
Okay, j'avais vu que ruby était en fait un programme en C. Donc, il n'y a pas moyen de coder proprement en Ruby. Grrr, j'aime pas ça moi... J'ai envie d'utiliser les Design Pattern en entier, avec l'abstraction et tout et tout ! ^^ Bon, ben va falloir m'y faire.

Mais je ne te rejoins pas sur l'utilité de l'abstraction. Certes, en C++ c'est trés casse-couille (déjà que l'utilisation du langage lui-même est trés chiante) par contre, en C# ou Java, ça s'utilise bien mieux. Sur l'utilité en soi, je ne pense pas que cela apporte réellement quelque chose au programme hormis de la propreté et une certitude de cohérence. Mais du point de vue de la conception même d'une structure, il est quand même plus rapide à comprendre et dire que telles et telles classes implémentent la même interface que de faire sans. Je le ressent en ce moment en réfléchissant au moyen de coder un système complexe de manière souple, propre, adaptable et modulable.

Après l'utilisation de l'abstraction permet aussi d'éviter la manipulation de classes concrètes et à avoir à modifier le code à 20 000 endroits différents au plus petit changement des exigences.

Posté par Nuri Yuri le 11 Oct - 22:13 (2013)
L'interpréteur ruby est codé en C mais jamais dans le programme le code ruby est converti en code C donc il n'hérite de rien du C.
En java les interfaces peut être probablement utile, enfin, je dis ça mais je ne programme pas en Java c'est le langage que je déteste le plus x)
Pour ma part, j'évite d'écrire deux fois la même fonction, si elle est déjà écrite quelque part, je vais juste l'appeler et elle fera le travail qu'il faut, en ruby c'est très simple car il suffit juste d'envoyer un objet, si des variables d'instances sont à modifier on a pas à se soucier de l'alignement des ces variables vu qu'elles n'ont pas d'alignement. Pour le C++ je fais preuve de plus de rigueur et dans le pire des cas, j'utiliserais l'attribut "inline" mais VS 11.0 sait le faire seul comme un grand donc je me pose pas trop de question Gros matou qui ronronne

Faire du code propre en ruby c'est utiliser correctement les héritages et bien manipuler les classes et modules qui fondamentalement ne fonctionnent pas pareil.

Posté par Ku'rei le 11 Oct - 22:53 (2013)
Je ne rechigne a aucun langage sauf C et C++ que je n'aime pas du tout ^^

C'est une bonne idée, j'aime faire pareil pour les fonctions. Par contre, la liberté que laisse Ruby est a double tranchant, une erreur d'inatention, un bidouillage pour arriver à quelque chose et on peu se rendre compte d'un bug qui nécessite de modifier tout le code...

Posté par Nuri Yuri le 11 Oct - 22:57 (2013)
Si tu savais ce que c'était en c++, tu est 1000 fois plus libre qu'en ruby du genre, tu peux faire ce que tu veux Gros matou qui ronronne

Posté par Ku'rei le 11 Oct - 23:45 (2013)
Ca dépend de ce que tu définis comme liberté ^^

Avec C++ tu as 1000 fois plus de possibilités qu'avec Ruby, mais niveau rigueur tu est contraint de tout les côtés.
Citation:
Ben Kenobi : Luke, tu découvrira que beaucoup de vérités auxquelles nous tenons dépendent avant tout de notre propre point de vue.


Je cherche la petit bête, je me suis essayer au C++ en me disant que ça pourrait me plaire, mais coder 2h avant d'avoir le moindre résultat m'a découragé. C'est pourquoi j'aime C# simple et rapide.

Posté par Nuri Yuri le 12 Oct - 09:37 (2013)
Crois tu ?
Par exemple
Code:
classDontToutLesAttributsSontPrives* obj=new classDontToutLesAttributsSontPrives(5); //Disons que ça met 5 au premier attribut.
((int*)obj)[0]=6; //Et boum, le premier attribut est modifié.

Pour le compilateur il se passe rien de méchant, je ne fais que manipuler un pointeur ce qui donnera en assembleur :
Code:
push 1
push classDontToutLesAttributsSontPrives_structure_size
call calloc
mov [ebp-04],eax ;On met le pointeur dans notre variable obj
push 5
push eax
call classDontToutLesAttributsSontPrives_constructeur
mov eax,[ebp-04]
mov [eax],6


Je suis pas vraiment certain pour le syntaxe c++ mais si elle est juste c'est exactement ce qu'il se passera en assembleur et le compilateur c++ ne dira absolument rien on a le droit de caster un type de pointeur en un autre type sinon ça serait une grosse catastrophe avec le void* qui est utilisé partout dans les déclaration standard.

Enfin bref, le c++ est probablement plus lent à coder (normal langage de bas niveau) mais il n'a presque aucune restriction, quand on connait le compilateur par cœur on peut faire de très jolis tours <3 (Après, ça ne garantis pas la propreté du code mais va dire ça à quelqu'un qui a fait de l'elec et qui sait que tout est accessible dans un micro processeur et que private n'est qu'une idée proposée par les «encapsulers compulsifs» x) )

Chaque langage a ses avantage et ses défauts c'est pour ça que je complète le ruby avec le c++ et inversement Gros matou qui ronronne

Posté par Ku'rei le 12 Oct - 10:17 (2013)
Pour la syntaxe tu peux faire ce que tu veux je ne la connais pas mais je comprend ce que tu veux dire. Cependant ça m'étonne que l'on puisse tirer un attribut comme ça. Ca ne dépendant pas de comment tu as définis ton attribut ?

Code:
class Machin {
  public int getAttribut() {
     return mon_attribut;
  }

  private int mon_attribut = 0;
}

Je l'ai fait en C#, mais ça revient au même les syntaxe se ressemble. Je n'ai pas l'impression que tu puisse accéder à mon_attribut.

En fait, ce que je ne comprend pas c'est la manipulation : ((int*)obj)[0]. Ca transforme l'objet en un tableau de valeurs ?

Sinon, j'ai pas encore vu le code assembleur ^^

Posté par Nuri Yuri le 12 Oct - 10:29 (2013)
L'attribut private t'interdit juste de faire obj->mon_attribut mais en aucun cas en c++ ça t'empêche d'y accéder depuis l'extérieur.
((int*)obj) dit juste au compilateur que la valeur considéré est un pointeur d'int par conséquent, je peux modifier tout ce qui se trouve à partir de l'adresse obj jusqu'à obj+classDontToutLesAttributsSontPrives_structure_size. L'objet en lui même ne subit aucune modifications de son intégrité «physique».

Le code assembleur c'est ce que le processeur exécutera (quand ça sera mis sous forme de bytecode), c'est l'équivalent pur de mon code c++ si j'ai défini obj avant toute les autre variables locales. (Après, le compilateur choisira plus ecx pour le pointeur vers la classe mais c'est un détail :b)

En C#, tu ne peux peut être pas faire la même manipulation vu que c'est un langage interprété mais si il y a gestion des pointeurs et la permission de caster un pointeur en void* tu peux caster un pointeur de classe en n'importe quoi.

Posté par Ku'rei le 12 Oct - 12:00 (2013)
Hmm pas top le système d'encaplsulage du C++ alors ^^ Ok pour le code assembleur, je ne connais juste pas le fonctionnement.
Donc si je comprend bien ((int*)obj[0] += 10 ajoutera 10 à l'attibut privé de l'objet ? Ou ça ne modifie pas l'attribut ?

C# est un langage semi-compilé, il y a des pointeurs mais le langage n'est pas fait pour ça. Dac, et bien je préfère m'en tenir aux principes de la POO et ne pas tenter de passer par les chemins de traverses pour contourner les règles.

En tout cas, merci de ta réponse Imbécile heureux

Posté par Nuri Yuri le 12 Oct - 12:29 (2013)
Le système d'encapsulage du c++ est aussi performant que celui de tous les autres langages, tant qu'on reste dans le même type d'objet. Après un objet c'est une structure alloué quelque part dans la mémoire donc forcément modifiable soit par le programme lui même, soit par un autre x)
L'encapsulation c'est comme l'abstraction, c'est juste un modèle qui au plus bas niveau n'a aucune valeur réelle, si je ne pouvais pas faire mon petit bout de code j'aurais fait :
Code:
classDontToutLesAttributsSontPrives* obj=new classDontToutLesAttributsSontPrives(5);
int a=6;
RtlMoveMemory(obj,&a,4);

Aucun problèmes et ça fonctionne dans tous les langages de programmation tournant sur Windows \\o//

Posté par Ku'rei le 12 Oct - 18:26 (2013)
Bon du coup, j'ai coder une autre module Input a partir de celui de cybersam,
tout fonctionne, sauf quand je veux récupérer un caractère.

Il me faut utiliser l'API ToASCII (DOC), mais je ne comprend pas comment on fait.

Win32API.new(dll, fonction, type_param, resultat).new puis on l'appel avec call(*args) ça je l'ai compris, mais que mettre dans type param ('i', 'p', ...) pour la fonction ToASCII ? Que mettre en résultat et à la place de *args ?

Si tu pouvais m'éclairer ce serait parfait Imbécile heureux

Posté par Nuri Yuri le 12 Oct - 18:42 (2013)
Code:
ToASCII=Win32API.new("User32","ToAscii","iippi","i")

Code:
  def to_ascii(uVirtKey,uScanCode,lKeyState=0)
    lpChar="\x00\x00\x00\x00";
    lpKeyState=(lKeyState != 0 ? lKeyState.chr : 0)
    ToASCII.call(uVirtKey,uScanCode,lpKeyState,lpChar,0);
    return lpChar.delete("\x00")
  end

C'est un truc dans le genre là que tu dois avoir.
Enfin, je te conseilles de pas trop abuser de ToASCII ^^' Le mieux est d'avoir un tableau pré fait avec toute les associations en fonction de la VKey et de si t'es en maj ou pas ((lKeyState&0x01)==1).

Posté par Ku'rei le 12 Oct - 21:36 (2013)
D'accord, merci. J'aurais deux questions : c'est quoi uScanCode et lKeyState ? Et que veut dire lKeyState&0x01 (fonction and bit à bit ?) ? Je fais des tests de mon côté

Posté par Nuri Yuri le 12 Oct - 22:59 (2013)
uScanCode ça doit être l'identifiant de la touche sur le clavier physique... J'avais de tel chose quand je jouais avec RawInput mais je ne peux pas te dire comment les obtenir de manière simple... Essaie 0 pour voir ce que ça donne.
(lKeyState&0x01)==1 me permet de savoir si le bit de poids faible est actif ou non.

Posté par Ku'rei le 13 Oct - 11:14 (2013)
Du coup j'ai fait un tableau d'association, tout fonctionne. Et ce n'est pas trop lent.

Posté par Nuri Yuri le 13 Oct - 11:30 (2013)
Bah, non car t'auras juste à faire ça :
Code:
if maj_appuye
  return ASCII_TABLE_MAJ[vk_key]
elsif alt_gr_appuye
  return ASCII_TABLE_ALT[vk_key]
else
  return ASCII_TABLE[vk_key]
end

vk_key sera le code de la touche que ta trouvé appuyé, et ASCII_TABLE sera les tableau contenant les caractères correspondant aux vk_key.
Après tu dois dresser les tables et vérifier les statuts de maj et altgr.

Posté par Ku'rei le 13 Oct - 16:03 (2013)
J'ai pas fait comme ça mais c'est une bonne idée ^^