Accueil Ti-Gen Foire Aux Questions Chat sur le chan #tigcc sur IRC
Liste des membres Rechercher Aide
Bienvenue Invité !   Se connecter             Mes sujets   
Administrer
0 membre(s) et 1 visiteur(s) actif(s) durant les 5 dernières minutes Utilisateurs actifs : Aucun membre + 1 visiteur
Avant de poster sur le forum, il y a des régles de bases à respecter pour une bonne entente et un respect de tous.
Veuillez lire la charte du forum.
  :: Index » Forum Ti68K » Programmation C » Orienté objet avec TIGCC (113 réponse(s))
./REPRISE DU POST PRECEDENT (post n°95)   Marquer comme non lu.
Quésoft Ecrit le: Mardi 24 octobre 2006 à 20:12 Déconnecté(e)    Voir le profil de Quésoft Envoyer un email à Quésoft Visiter le site WEB de Quésoft Envoyer un message privé à Quésoft  

Je pense que dans le cas des exceptions, il faut se garder une liste de pointeurs.

Imaginons l'implémentation suivante pour les objets locaux:


struct ObjList {
void **l; //listes des objets
void (**fcts) (void *obj); // liste des fonctions de finalisation
size_t maxSize; //Taille maximale de la liste dynamique
void **l_top; //Pointe sur le dernier objet.
void **f_top; //Pointe sur la dernière fonction.
};

struct ErrStk {
void ***tries; // Pointeurs vers la liste des objets
size_t maxSize; //Taille maximale de la liste dynamique
void ***top; //Pointe sur le dessus de la pile tries
};

struct ObjList objList;
struct ErrStk errStk;

/* ... */

//initialisation

objList.l = malloc(TAILLE_INITIALE * sizeof(void *));
objList.l_top = objList.l;
objList.fcts = malloc(TAILLE_INITIALE * sizeof(void (*) (void*)));
objList.f_top = objList.fcts;
objList.maxSize = TAILLE_INITIALE;
errStk.tries = malloc(TAILLE_INITIALE * sizeof(void **));
errStk.maxSize = TAILLE_INITIALE;
errStk.top = errStk.tries;

/* ... */

void add_localObject(void *obj, void (*fct) (void *obj)) {
  /*routine de l'allocation dynamique*/
  *(objList.l_top++) = obj;
  *(objList.f_top++) = fct;
}

void remove_localObject() {
  (*(--objList.f_top))(--objList.l_top);//utiliser la fonction pour finaliser le dernier objet dans la liste
}

void stack_try() {
  /*routine de l'allocation dynamique*/

  *(errStk.top++) = objList.l_top;
}

void unstack_try() {
  void **p = *(--errStk.l);

  while (objList.l_top != p) {
    remove_localObject();
  }
}


Le code suivant:


void prem() {
 Object a;
  TRY
    /* code */
    deus();
    /* code */
  ONERR
   /*code*/
  ENDTRY
}

void deus() {

  TRY
    /* code */
    tres();
    /* code */
  FINALLY
   /*code*/
  ENDTRY

tres();
}

void tres() {
 Object o1, o2;

  Er_throw(1);
}


Pourait être converti en:


void prem() {
  Object a;
  add_local_object(&a, desctructeur_de_a);

  TRY
    stack_try();
    /* code */
    deus();
    /* code */
  ONERR
  unstack_try();
   /*code*/
  ENDTRY

  remove_localObject();
}

void deus() {

  TRY
    stack_try();
    /* code */
    tres();
    /* code */
   FINALLY
   /*code*/
  unstack_try();
  ENDTRY

  tres();
}

void tres() {
 Object o1, o2;
  add_local_object(&o1, desctructeur_de_o1);
  add_local_object(&o1, desctructeur_de_o1);

  Er_throw(1);


  remove_localObject();
  remove_localObject();
}


Soyez indulgent pour les erreurs, ça fait un bout que je n'ai pas joué avec les pointeurs vers des fonctions. Le but était de donner l'algorithme que je considère le plus clean.
-Edité le Mardi 24 octobre 2006 à 23:34 par Quésoft-
-Edité le Mardi 24 octobre 2006 à 23:35 par Quésoft-
    
./Post n°96   Marquer comme non lu.
Sasume Ecrit le: Mardi 24 octobre 2006 à 23:07 Déconnecté(e)    Voir le profil de Sasume Envoyer un email à Sasume Visiter le site WEB de Sasume Envoyer un message privé à Sasume  

Kevin> Un compilateur, c'est un programme qui transforme un programme écrit dans un langage vers un programme en langage machine (donc ASM pour les compilo C, et bytecode pour javac), non ?
    
./Post n°97   Marquer comme non lu.
Quésoft Ecrit le: Mardi 24 octobre 2006 à 23:53 Déconnecté(e)    Voir le profil de Quésoft Envoyer un email à Quésoft Visiter le site WEB de Quésoft Envoyer un message privé à Quésoft  

Sasume: comme je l'ai déjà dit ailleurs, il y a longtemps: à la base, le code machine est interprété par le hardware... et quand on songe au microcode et tout le reste, on ne peut pas tracer la ligne entre le code machine et le code tokenisé en disant que l'un roulle sur un vrai matériel.

Par contre, si le bytecode contient des éléments abstraits pour le matériel, comme les objets et les classes, c'est différent (à moins de considérer l'architecture JVM comme très sofistiquée: si des processeurs peuvent gérer des piles, pourquoi un processeur ne pourrait-il pas gérer un modèle oo ? - je parle bien sûr conceptuellement, pas si un tel processeur serait une bonne idée).
-Edité le Mercredi 25 octobre 2006 à 00:02 par Quésoft-
    
./Post n°98   Marquer comme non lu.
Kevin Kofler Ecrit le: Mercredi 25 octobre 2006 à 04:59 Déconnecté(e)    Voir le profil de Kevin Kofler Envoyer un email à Kevin Kofler Visiter le site WEB de Kevin Kofler Envoyer un message privé à Kevin Kofler  


Sasume :
Kevin> Un compilateur, c'est un programme qui transforme un programme écrit dans un langage vers un programme en langage machine (donc ASM pour les compilo C, et bytecode pour javac), non ?

Je suis bien d'accord avec ta définition, mais le bytecode n'est pas un langage machine (du moins pas pour les processeurs courants - il paraît qu'il y a des CPUs spécialisés qui interprètent ça en matériel), c'est un langage qui doit être interprété ou converti en langage machine en temps d'exécution (JIT).
Membre de l'équipe de TIGCC: http://tigcc.ticalc.org
Mainteneur du portage Linux/Unix de TIGCC: http://tigcc.ticalc.org/linux/
Membre de l'équipe de CalcForge: http://www.calcforge.org:70/

Participez à la reprise de Ti-Gen!
    
./Post n°99   Marquer comme non lu.
Quésoft Ecrit le: Mercredi 25 octobre 2006 à 05:29 Déconnecté(e)    Voir le profil de Quésoft Envoyer un email à Quésoft Visiter le site WEB de Quésoft Envoyer un message privé à Quésoft  

Bon -très tot- matin (pour vous, parce que c'est bonne nuit pour moi), Kevin :)

Que pensez-vous de ma solution pour les objets locaux. Est-ce que c'est 'potable', selon vos critères ?
    
./Post n°100   Marquer comme non lu.
Kevin Kofler Ecrit le: Mercredi 25 octobre 2006 à 09:06 Déconnecté(e)    Voir le profil de Kevin Kofler Envoyer un email à Kevin Kofler Visiter le site WEB de Kevin Kofler Envoyer un message privé à Kevin Kofler  


Ça pourrait fonctionner, mais il faut bien vérifier que les structures TRY etc. donnent bien le code voulu (que les appels à tes stack_try et unstack_try ne soient pas déplacés au mauvais endroit, quoi). Attention, dans ton exemple avec ONERR, il manque un unstack_try dans le cas où il n'y a pas d'erreur.
Membre de l'équipe de TIGCC: http://tigcc.ticalc.org
Mainteneur du portage Linux/Unix de TIGCC: http://tigcc.ticalc.org/linux/
Membre de l'équipe de CalcForge: http://www.calcforge.org:70/

Participez à la reprise de Ti-Gen!
    
./Post n°101   Marquer comme non lu.
Quésoft Ecrit le: Mercredi 25 octobre 2006 à 13:56 Déconnecté(e)    Voir le profil de Quésoft Envoyer un email à Quésoft Visiter le site WEB de Quésoft Envoyer un message privé à Quésoft  

Kevin Kofler :
Ça pourrait fonctionner, mais il faut bien vérifier que les structures TRY etc. donnent bien le code voulu (que les appels à tes stack_try et unstack_try ne soient pas déplacés au mauvais endroit, quoi).


Comme ce n'est pas très complexe à implémenter, je pourrai générer ça dans un premier temps et l'on pourra tester par la suite.

Kevin Kofler :Attention, dans ton exemple avec ONERR, il manque un unstack_try dans le cas où il n'y a pas d'erreur.


Vrai... un try est stacké, alors il faut l'enlever de la pile même s'il n'y a pas d'objet à finaliser. Il faudrait toujours remanier les trys pour avoir un finally et ajouter le unstack au finally.

En tk, l'idée était de présenter la meilleure façon de faire à laquelle j'ai pu penser...
    
./Post n°102   Marquer comme non lu.
Pollux Ecrit le: Jeudi 26 octobre 2006 à 00:48 Déconnecté(e)    Voir le profil de Pollux Envoyer un email à Pollux Envoyer un message privé à Pollux  

Kevin Kofler :
Et sinon, je ne suis pas sûr que ça:
struct foo {int toto};
struct foo *global;
void f(struct foo *bar)
{
  global=bar;
}
void g(void)
{
  global->toto=123;
}
void h(void)
{
  struct foo baz;
  f(&baz);
  g();
}

soit valide (en tout cas je vois ça comme un hack grossier et, si le standard C permet ce code, je verrais bien un flag de même type que -ffast-math ou -fmerge-all-constants qui interdit ça dans le but de mieux optimiser).

Un hack grossier ? #confus# Ca n'a rien d'extraordinairement bizarre, et ça pourrait même arriver sans aucune variable globale :
void main() {
  struct window *win = create_window();
  struct windowlabel label = {"hello",0,0};
  window_add(win,&label);
  while (!window_isclosed(win)) {
    label.x++;
    update_window(win);
  }
  destroy_window(win);
}

Bref, tu ne peux absolument pas mettre ça sur le même plan que les options que tu cites...

Donc il vaut mieux se rabattre sur la solution propre : faire une liste chaînée de "paquets de n objets", un paquet correspondant à une fonction -- le coût serait de 4+2n octets à écrire sur la pile plus la lecture/écriture du pointeur de liste chaînée. C'est pas non plus démesuré ^^

La solution propre, ça s'appelle l'unwinding DWARF2. Mais il faut faire un frontend (ou un compilateur à part entière, mais alors adieu les optimisations de GCC!) pour ça, pas un préprocesseur.

Pas besoin de faire un frontend complet, des extensions au compilateur C existant suffiraient (et c'est ce que je dis depuis le début -- l'intérêt c'est que ça permet avec le même préprocesseur de générer du code portable et du code optimisé pour la plateforme)
    
./Post n°103   Marquer comme non lu.
Pollux Ecrit le: Jeudi 26 octobre 2006 à 01:05 Déconnecté(e)    Voir le profil de Pollux Envoyer un email à Pollux Envoyer un message privé à Pollux  

Quésoft :
Oui.

1er appel de fct.
stack des objets locaux et finaliseurs de la fonction dans le tableau
2e appel de fct.
stack des objets locaux et finaliseurs de la fonction dans le tableau
fin 2e fct
liberation des objets locaux de la fonction
fin 1e fct
liberation des objets locaux de la fonction

C'est le scénario classique. Un tableau sera très rapide dans ce cas là. Le problème c'est les exceptions (comme le soulignait Kevin) et les exits (ce dernier est moins problématique: on s'en sort avec une fct atexit).

Mais le problème du tableau c'est l'allocation dynamique, rien que les vérifications de taille ça risque de ralentir énormément par rapport à une bête liste chaînée...


Sinon j'ai la flemme de lire en détail ton code mais je comprends pas bien l'intérêt de s'embêter à stocker la fonction de finalisation séparément : avoir accès à la vtable de l'objet suffit, et ça sera bien plus rapide :) Ensuite tu ne peux pas faire la libération en C après avoir catché une erreur, puisque le compilateur a potentiellement bousillé la pile... Je ne suis pas sûr de bien savoir comment le résoudre : a priori il faudrait impérativement placer la pile en dessous de l'objet à libérer le plus bas en mémoire avant de lancer le finaliseur, mais :
(1) c'est pas forcément suffisant, peut-être que le finaliseur veut accéder à des variables locales à la fonction qui sont en dessous de l'objet à détruire dans la pile -- et là pas moyen de savoir où sont ces variables pour éviter de les écraser :/ peut-être que tu peux éviter ça en amont, mais c'est pas évident
(2) ça exige de faire les suppositions crades que kevin n'aimait pas : le compilo alloue les objets à finaliser sur la pile et pas ailleurs (bon, en pratique il peut qd même les allouer ailleurs, mais pas à une adresse inférieure : c'est pas très satisfaisant)
Bref, je crois que la solution serait non pas de faire un handler pour ER_throw, mais de demander que les erreurs soient lancées autrement (cleanup_and_throw, qui fait la libération *avant* de lancer l'erreur) -- avec un wrapper pour les fonctions qui pourraient lancer une erreur avec ER_throw :/


Sasume :
Kevin> Un compilateur, c'est un programme qui transforme un programme écrit dans un langage vers un programme en langage machine (donc ASM pour les compilo C, et bytecode pour javac), non ?

Pour moi du moment que ça dépasse l'aspect purement syntaxique c'est pas un tokenizer, et je vois pas comment appeler ça autrement qu'un compilateur :) (à condition que le langage de sortie soit plus bas niveau que le langage d'entrée, évidemment)
    
./Post n°104   Marquer comme non lu.
Quésoft Ecrit le: Jeudi 26 octobre 2006 à 03:08 Déconnecté(e)    Voir le profil de Quésoft Envoyer un email à Quésoft Visiter le site WEB de Quésoft Envoyer un message privé à Quésoft  

Pollux :
Mais le problème du tableau c'est l'allocation dynamique, rien que les vérifications de taille ça risque de ralentir énormément par rapport à une bête liste chaînée...


Je vois mal comment une opération if (l.size > l.max) pourrait ralentir énormément le processus. Avec une liste chaînée, ce sera pire, on a qu'à penser aux opérations pour trouver où placer l'objet que l'on ajoute...

Pollux :
Sinon j'ai la flemme de lire en détail ton code mais je comprends pas bien l'intérêt de s'embêter à stocker la fonction de finalisation séparément : avoir accès à la vtable de l'objet suffit, et ça sera bien plus rapide :)


Comment cela pourrait-il être plus rapide, alors que si l'on utilise la vtable, on ne déréférenciera pas moins de fois (avec une liste à part, c'est comme si on avait une vtable 'commune') ? Je pense que le désaventage est plutôt une plus grande signature mémoire, sans être dramatique, par contre.

Pollux :
Ensuite tu ne peux pas faire la libération en C après avoir catché une erreur, puisque le compilateur a potentiellement bousillé la pile...


humm... c'est assez juste. Les objets alloués dans la fonction appelée sont potentiellement altérés. Screwed.

Pollux :
Je ne suis pas sûr de bien savoir comment le résoudre : a priori il faudrait impérativement placer la pile en dessous de l'objet à libérer le plus bas en mémoire avant de lancer le finaliseur, mais :
(1) c'est pas forcément suffisant, peut-être que le finaliseur veut accéder à des variables locales à la fonction qui sont en dessous de l'objet à détruire dans la pile -- et là pas moyen de savoir où sont ces variables pour éviter de les écraser :/ peut-être que tu peux éviter ça en amont, mais c'est pas évident
(2) ça exige de faire les suppositions crades que kevin n'aimait pas : le compilo alloue les objets à finaliser sur la pile et pas ailleurs (bon, en pratique il peut qd même les allouer ailleurs, mais pas à une adresse inférieure : c'est pas très satisfaisant)
Bref, je crois que la solution serait non pas de faire un handler pour ER_throw, mais de demander que les erreurs soient lancées autrement (cleanup_and_throw, qui fait la libération *avant* de lancer l'erreur) -- avec un wrapper pour les fonctions qui pourraient lancer une erreur avec ER_throw :/


Effectivement, ce dernier point est très pertinent. L'idée du 'cleanup_and_throw' résouderais le problème, mais on a toujours la possibilité qu'une erreur soit lancée d'une fonction TIOS.

Pour résoudre tout ça, il reste la possibilité de faire du local comme en Moka: avec malloc. À ce moment, on n'a pas à se soucier de l'allocation sur la pile. Par contre, cela ne permettrait pas de bénéficier de la rapidité de l'allocation sur la pile ... à moins que l'on s'alloue une pile séparée avec malloc pour nos objets ?
-Edité le Jeudi 26 octobre 2006 à 03:23 par Quésoft-
    
./Post n°105   Marquer comme non lu.
Pollux Ecrit le: Jeudi 26 octobre 2006 à 04:01 Déconnecté(e)    Voir le profil de Pollux Envoyer un email à Pollux Envoyer un message privé à Pollux  

Quésoft :
Pollux :
Mais le problème du tableau c'est l'allocation dynamique, rien que les vérifications de taille ça risque de ralentir énormément par rapport à une bête liste chaînée...


Je vois mal comment une opération if (l.size > l.max) pourrait ralentir énormément le processus. Avec une liste chaînée, ce sera pire, on a qu'à penser aux opérations pour trouver où placer l'objet que l'on ajoute...

Gni ?
struct String {
  void *vtable;
  size_t unwind_next;
  char *s;
};
void f() {
  struct {
    void *prev_linked_ptr;
    String x;
    String y;
  } unwind_info;

  unwind_info.prev_linked_ptr = linked_ptr;
  linked_ptr = &unwind_info;

  construct_String(&unwind_info.x);
  ...
  unwind_info.x.unwind_next = sizeof(String);
  construct_String(&unwind_info.y, "hello");
  ...
  unwind();
}

Pas besoin de lire une variable supplémentaire ou de faire des sauts conditionnels, juste deux pointeurs à modifier avant d'exécuter le reste de la fonction :)


Pollux :
Sinon j'ai la flemme de lire en détail ton code mais je comprends pas bien l'intérêt de s'embêter à stocker la fonction de finalisation séparément : avoir accès à la vtable de l'objet suffit, et ça sera bien plus rapide :)


Comment cela pourrait-il être plus rapide, alors que si l'on utilise la vtable, on ne déréférenciera pas moins de fois (avec une liste à part, c'est comme si on avait une vtable 'commune') ? Je pense que le désaventage est plutôt une plus grande signature mémoire, sans être dramatique, par contre.

Ben ça t'évite de devoir copier quelque part l'adresse de la fonction de finalisation : le pointeur est déjà dans la vtable, tu as juste à l'utiliser comme tu le ferais pour n'importe quel appel de fonction... (enfin je dis "vtable", c'est au sens large : peut-être que la fonction est stockée directement dans l'objet, ou peut-être qu'il y a un niveau d'indirection vers une table commune à toute la classe, peu importe)


Effectivement, ce dernier point est très pertinent. L'idée du 'cleanup_and_throw' résouderais le problème, mais on a toujours la possibilité qu'une erreur soit lancée d'une fonction TIOS.

Pour résoudre tout ça, il reste la possibilité de faire du local comme en Moka: avec malloc. À ce moment, on n'a pas à se soucier de l'allocation sur la pile. Par contre, cela ne permettrait pas de bénéficier de la rapiditer de l'allocation sur la pile ... à moins que l'on s'alloue une pile séparée avec malloc pour nos objets ?

Il y a encore d'autres possibilités :
- lancer le destructeur sur une petite pile séparée (ou à une adresse fixe assez basse)
- faire un ER_catch "empoisonné", qui ferait planter ER_throw, reprendrait la main avec le handler d'Adress Error, puis ferait un cleanup_and_throw() normal à partir de l'adresse de la pile courante ; par contre ça implique que tous les TRY pouvant appeler des fonctions "C++" utilisent poison_catch() plutôt que ER_catch, mais ça ne pose pas de pb pour les romcalls (sauf si on leur passe un callback "C++", mais c'est un peu suicidaire -- et même dans ce cas-là il suffit de faire un poison_check() pour s'assurer qu'ER_throw est toujours empoisonné)
-Edité le Jeudi 26 octobre 2006 à 04:04 par Pollux-
    
./Post n°106   Marquer comme non lu.
Kevin Kofler Ecrit le: Jeudi 26 octobre 2006 à 05:53 Déconnecté(e)    Voir le profil de Kevin Kofler Envoyer un email à Kevin Kofler Visiter le site WEB de Kevin Kofler Envoyer un message privé à Kevin Kofler  


Pollux :
Un hack grossier ? #confus# Ca n'a rien d'extraordinairement bizarre, et ça pourrait même arriver sans aucune variable globale :

Cet exemple est déjà plus pertinent que ceux auxquels je pensais. D'un côté, je trouve un peu crade de stocker un pointeur sur une variable locale qui appartient à une autre fonction pour plus tard, mais de l'autre côté, une interface OO pour les dialogues ressemblerait probablement à ça. (Et je suis convaincu que c'est bien du C valide de faire ça maintenant que j'ai vu ton exemple.)

Pas besoin de faire un frontend complet, des extensions au compilateur C existant suffiraient (et c'est ce que je dis depuis le début -- l'intérêt c'est que ça permet avec le même préprocesseur de générer du code portable et du code optimisé pour la plateforme)

Des extensions pour générer du DWARF 2 pour des objets style C++?! Je veux bien voir la tête des extensions C que tu proposes pour faire ça. Sais-tu comment fonctionne le DWARF 2? C'est du bytecode qui est interprété par l'unwinder, et dont le but est de dérouler la pile proprement quel que soit l'endroit où on se trouve dans une fonction. Donc il faut que frontend, backend et émetteur d'informations d'unwinding se mettent tous d'accord. Comment comptes-tu influencer ça dans une source C? Tu penses à un truc comme __attribute__((cleanup(...))) (cf. documentation de GCC 4.1)?

Et sinon, des extensions au compilateur C == modifications du frontend C == faire un nouveau frontend. Sauf que le frontend ne fait que la moitié du travail et l'autre moitié est faite par un préprocesseur, et là franchement je ne vois pas l'intérêt. Générer du C avec ou sans extensions dans le même préprocesseur, ça va te faire essentiellement 2 préprocesseurs différents dans le même tarball source. Et un frontend GCC pourrait justement générer du DWARF 2 de manière portable, voire même permettre le choix entre DWARF 2, setjmp/longjmp ou d'autres solutions, vu que GCC a des abstractions pour les informations d'unwinding.

D'ailleurs, il faut obligatoirement wrapper tous les ROM_CALLs pour convertir les exceptions AMS en exceptions DWARF 2 si on fait du DWARF 2, c'est probablement le principal désavantage.

Pollux :
Ensuite tu ne peux pas faire la libération en C après avoir catché une erreur, puisque le compilateur a potentiellement bousillé la pile... Je ne suis pas sûr de bien savoir comment le résoudre : a priori il faudrait impérativement placer la pile en dessous de l'objet à libérer le plus bas en mémoire avant de lancer le finaliseur

Euh non. Il faut tout simplement un ER_catch dans toute fonction qui a un destructeur local, qu'elle utilise les exceptions elle-même ou pas. C'est bien ça l'overhead de -fexceptions et la raison pour laquelle Qt est normalement compilé en -fno-exceptions.

Quand tu es revenu dans la fonction appelante, c'est trop tard pour détruire les objets de la fonction appelée, et aucun bidouillage sauvage avec le pointeur de pile ne va t'aider. (Par exemple, l'appel au destructeur pousse des trucs sur la pile, donc ça va bousiller l'objet à détruire au moins dans certains cas. Si plusieurs destructeurs sont appelés, c'est encore pire, parce que le destructeur d'avant peut bousiller l'objet après de manière totalement imprévisible.)

Quésoft :
Effectivement, ce dernier point est très pertinent. L'idée du 'cleanup_and_throw' résouderais le problème, mais on a toujours la possibilité qu'une erreur soit lancée d'une fonction TIOS.

-> wrappers pour convertir les exceptions. Il n'y a pas vraiment d'autre choix si tu utilises un système de gestion d'exceptions différent de celui d'AMS, que ce soit ça ou du DWARF 2 ou du setjmp/longjmp.

Pollux :
Il y a encore d'autres possibilités :
- lancer le destructeur sur une petite pile séparée (ou à une adresse fixe assez basse)
- faire un ER_catch "empoisonné", qui ferait planter ER_throw, reprendrait la main avec le handler d'Adress Error, puis ferait un cleanup_and_throw() normal à partir de l'adresse de la pile courante ; par contre ça implique que tous les TRY pouvant appeler des fonctions "C++" utilisent poison_catch() plutôt que ER_catch, mais ça ne pose pas de pb pour les romcalls (sauf si on leur passe un callback "C++", mais c'est un peu suicidaire -- et même dans ce cas-là il suffit de faire un poison_check() pour s'assurer qu'ER_throw est toujours empoisonné)

Beurk! #sick# As-tu des idées encore plus barbares? #roll#
Membre de l'équipe de TIGCC: http://tigcc.ticalc.org
Mainteneur du portage Linux/Unix de TIGCC: http://tigcc.ticalc.org/linux/
Membre de l'équipe de CalcForge: http://www.calcforge.org:70/

Participez à la reprise de Ti-Gen!
    
./Post n°107   Marquer comme non lu.
Quésoft Ecrit le: Jeudi 26 octobre 2006 à 13:54 Déconnecté(e)    Voir le profil de Quésoft Envoyer un email à Quésoft Visiter le site WEB de Quésoft Envoyer un message privé à Quésoft  

Pollux :
Pas besoin de lire une variable supplémentaire ou de faire des sauts conditionnels, juste deux pointeurs à modifier avant d'exécuter le reste de la fonction :)


En effet, en utilisant la pile, comme dans l'exemple, c'est mieux que je propose (et je comprend pourquoi vous parliez de quelque chose de plus rapide). Par contre, ça n'empêche pas que l'on va finaliser des objets qui seront dans une partie de la pile potentiellement bouzillée, non ?

Pollux :
Ben ça t'évite de devoir copier quelque part l'adresse de la fonction de finalisation : le pointeur est déjà dans la vtable, tu as juste à l'utiliser comme tu le ferais pour n'importe quel appel de fonction... (enfin je dis "vtable", c'est au sens large : peut-être que la fonction est stockée directement dans l'objet, ou peut-être qu'il y a un niveau d'indirection vers une table commune à toute la classe, peu importe)


Avec l'exemple, c'est clair.


Pollux :
Il y a encore d'autres possibilités :
- lancer le destructeur sur une petite pile séparée (ou à une adresse fixe assez basse)
- faire un ER_catch "empoisonné", qui ferait planter ER_throw, reprendrait la main avec le handler d'Adress Error, puis ferait un cleanup_and_throw() normal à partir de l'adresse de la pile courante ; par contre ça implique que tous les TRY pouvant appeler des fonctions "C++" utilisent poison_catch() plutôt que ER_catch, mais ça ne pose pas de pb pour les romcalls (sauf si on leur passe un callback "C++", mais c'est un peu suicidaire -- et même dans ce cas-là il suffit de faire un poison_check() pour s'assurer qu'ER_throw est toujours empoisonné)
-Edité le Jeudi 26 octobre 2006 à 04:04 par Pollux-


Qu'est-ce qui serait le plus clean ? Une pile séparée + utilisation de la liste chaînée plus haut pour faire le 'unwinding' ?
-Edité le Jeudi 26 octobre 2006 à 13:55 par Quésoft-
    
./Post n°108   Marquer comme non lu.
Quésoft Ecrit le: Jeudi 26 octobre 2006 à 14:01 Déconnecté(e)    Voir le profil de Quésoft Envoyer un email à Quésoft Visiter le site WEB de Quésoft Envoyer un message privé à Quésoft  

Kevin Kofler :
Beurk! #sick# As-tu des idées encore plus barbares? #roll#


Une pile séparée sur laquelle on a le contrôle ne me parait pas une mauvaise idée. Avec sa solution de liste chainée, ça sera rapide et élégant. Reste l'espace utilisé, mais je ne pense pas que ce sera beaucoup plus important que ce que je proposait (en code exécutable, parce qu'en RAM, il va falloir stocker la nouvelle pile).

EDIT: Oubliez ce que j'ai dit pour la liste chainée sur la pile d'exécution, j'avais oublié le bousillage de celle-ci.
REEDIT: En fait, je peux me servir de la pile d'exécution au lieu de faire une ErrStk à côté. J'aurais donc une pille d'objet de grosseur fixe (comme la pile d'exécution) ou variable - une switch pourrait préciser la dimension de la pile des objets locaux. Un pointeur définit au début de chaque méthode qui a un TRY pointerais vers les objets de la pile à libérer en cas d'erreur:


void toto() {
  PILE_OBJ_PTR ptr_obj = pile_objets_top;
  PTR_TYPE ptr_err;
  Object *a = constructeur_classe_Object(allocation_pile_objets(sizeof(Object))), 
            *b = constructeur_classe_Object(allocation_pile_objets(sizeof(Object)));
  ptr_err = pile_objets_top;
  TRY
     /* code */
  ONERR
    unwind(ptr_err); // finalise tout ce qui a été alloué dans les fonctions appelés dans le try
    /* code */
  ENDTRY

  finaliseur_classe_Object(a);
  finaliseur_classe_Object(b);
  dealloc_pile_objet(ptr_obj); // finalise tout ce qui a été alloué sur la pile dans cette méthode
}

/* Lorsque l'on alloue sur la pile d'objet, on procède comme suit: */

void * allocation_pile_objets(size_t siz) {
  void * adr;

  *(((size_t*)pile_objets_top)++) = siz;
  adr = pile_objets_top;
  ((BYTE*)pile_objets_top) += siz;

  return adr;
}


-Edité le Jeudi 26 octobre 2006 à 18:03 par Quésoft-
    
./Post n°109   Marquer comme non lu.
Kevin Kofler Ecrit le: Jeudi 26 octobre 2006 à 19:50 Déconnecté(e)    Voir le profil de Kevin Kofler Envoyer un email à Kevin Kofler Visiter le site WEB de Kevin Kofler Envoyer un message privé à Kevin Kofler  


En bref, tu proposes une troisième pile "object stack" en plus du CPU stack (stack) et de l'expression stack (estack). Ça pourrait fonctionner, mais il faudra bien garder les piles CPU et objet en synchro pour que ça fonctionne. Ça ressemble un peu au Structured Exception Handling (SEH) de Window$. Mais eux, ils utilisent une liste chaînée de stack frames sur la pile CPU, donc il faudrait un unwinder perso (et donc wrapping de tous les ROM_CALLs) comme pour DWARF 2 ou setjmp/longjmp. Ta solution, elle, évite les risques de bousillage et permet donc de travailler avec ER_catch.
Membre de l'équipe de TIGCC: http://tigcc.ticalc.org
Mainteneur du portage Linux/Unix de TIGCC: http://tigcc.ticalc.org/linux/
Membre de l'équipe de CalcForge: http://www.calcforge.org:70/

Participez à la reprise de Ti-Gen!
    
./Post n°110   Marquer comme non lu.
Quésoft Ecrit le: Vendredi 27 octobre 2006 à 13:57 Déconnecté(e)    Voir le profil de Quésoft Envoyer un email à Quésoft Visiter le site WEB de Quésoft Envoyer un message privé à Quésoft  

Kevin Kofler :
En bref, tu proposes une troisième pile "object stack" en plus du CPU stack (stack) et de l'expression stack (estack). Ça pourrait fonctionner, mais il faudra bien garder les piles CPU et objet en synchro pour que ça fonctionne. Ça ressemble un peu au Structured Exception Handling (SEH) de Window$. Mais eux, ils utilisent une liste chaînée de stack frames sur la pile CPU, donc il faudrait un unwinder perso (et donc wrapping de tous les ROM_CALLs) comme pour DWARF 2 ou setjmp/longjmp.


Que voulez-vous dire par garder cette pile syncro avec celle du CPU ?

Je crois que les ptr_obj (pointe où était le dessus de la pile avant l'allocation des objets locaux) et les ptr_err (pointe où était le dessus de la pile avant d'éventuels objets locaux dans les fonctions appelées) de mon exemple sont suffisant. Est-ce que vous référiez à un procédé analogue ?

Kevin Kofler :
Ta solution, elle, évite les risques de bousillage et permet donc de travailler avec ER_catch.


Inspirée de celle de Pollux, c'est effectivement le but.
    
./Post n°111   Marquer comme non lu.
Sasume Ecrit le: Dimanche 5 novembre 2006 à 16:24 Déconnecté(e)    Voir le profil de Sasume Envoyer un email à Sasume Visiter le site WEB de Sasume Envoyer un message privé à Sasume  

Alors tu t'en sors ?
C'est pour quand la première beta ? :p
    
./Post n°112   Marquer comme non lu.
Quésoft Ecrit le: Mardi 7 novembre 2006 à 16:55 Déconnecté(e)    Voir le profil de Quésoft Envoyer un email à Quésoft Visiter le site WEB de Quésoft Envoyer un message privé à Quésoft  

Sasume :
Alors tu t'en sors ?
C'est pour quand la première beta ? :p


J'en suis à la conversion du code (la partie la plus longue) dans les méthodes et les fonctions.

Pour la date de la première beta, j'en ai aucune idée, mais je vais la sortir dès que j'aurai quelque chose, ne vous en faites pas :)

Cette alpha (plutôt que beta) ne supportera pas tous ce dont nous avons parlé. Par contre, le plus important sera là:

- Classes et interfaces.
- Polymorphisme (reposant principalement sur la sélection dynamique des méthodes).
- Objets locaux (en plus des objets instanciés avec new).

Par la suite, on se penchera sur:

- Packages ou namespaces.
- Surcharge des opérateurs.
- Autoboxing.
- Accesseurs automatiques (properties).
- Meilleur support du polymorphisme paramétrique (je pense parser l'output de CPP pour être en mesure de bien typer les variables).
- Meilleure optimisation (dans un premier temps, l'optimisation sera possiblement suboptimale, bien que présente et assez efficace).

EDIT:

J'oubliais, les types pointeur de fonctions ne seront possiblement pas supportés dans un premier temps. Le cas échéant, pour faire une méthode qui retourne un pointeur vers une fonction ou qui en accepte un en parm, il faudra faire un typedef. Cette situation sera temporaire, c'est juste que parser les déclarations où il y a des pointeurs vers des fonctions complexifie le processus.
-Edité le Mardi 7 novembre 2006 à 17:09 par Quésoft-
    
./Post n°113   Marquer comme non lu.
Quésoft Ecrit le: Lundi 18 décembre 2006 à 22:30 Déconnecté(e)    Voir le profil de Quésoft Envoyer un email à Quésoft Visiter le site WEB de Quésoft Envoyer un message privé à Quésoft  

Juste un petit mot pour dire que je n'ai pas retouché au code depuis mon dernier message, mais que je n'ai pas abandonné le projet. C'est juste que je travaille à plein temps et que ma dernière session d'université à temps partiel n'a pas été aussi peinard que je m'y attendait. Il se peut que je travaille sur un contrat pendant les fêtes, donc ça risque de ne pas avancer, mais dès que j'ai du temps à mettre sur un projet informatique personnel, ce sera sur celui-là.

Joyeuses fêtes et bonne année à tous!
    
  :: Index » Forum Ti68K » Programmation C » Orienté objet avec TIGCC (113 réponse(s))
Pages : 6/6     « 1 2 3 4 5 [6] » »|

.Répondre à ce sujet
Les boutons de code
[B]old[I]talic[U]nderline[S]trikethrough[L]ine Flip Hori[Z]ontallyFlip [V]erticallySha[D]ow[G]low[S]poilerCode [G][C]ite
Bullet [L]istList Item [K] Link [H][E]mail[P]icture SmileysHelp
Couleurs :
Saisissez votre message
Activer les smileys
     

Forum de Ti-Gen v3.0 Copyright ©2004 by Geoffrey ANNEHEIM
Webmaster: Kevin KOFLER, Content Admins: list, Server Admins: Tyler CASSIDY and Kevin KOFLER, DNS Admin: squalyl
Page générée en 42.73ms avec 18 requetes