J'ai des interrogations de style sur les signatures de fonctions
associées aux types utilisateurs, surtout question de style, pas
vitales, mais bon...
Prenons un type Pile, pile d'entiers.
Je vois 4 familles de définition de ce type
A) struct avec taille+tableau dynamique
typedef struct {
int* tab;
size_t capacite, taille;
} Pile;
B) Ptr sur 1ere element d'une liste chainee
struct cel {
struct cel* next;
int i;
};
typedef struct cel * Pile;
C) TAD pur
typedef Pile; // dans le .h
// version A ou B dans le .c
D) Tableau de taille fixe (pile bornée + délimiteur de fin)
typedef int Pile[CAPA+1];
Quelles signatures donner aux fonctions qui la manipulent ? Pour
alimenter la discution, prenons simplement initialisation et taille.
Moi, ma tendance serait:
A+B+D: // struct, pointeur et tableau
bool initPile(Pile* p);
size_t sizePile(const Pile p);
C: // TAD
Pile* newPile(void);
size_t sizePile(const Pile *p);
Mais (et c'est là que commencent mes interrogations):
Dans A et B (struct et pointeur), le const de size est superflux: de
toute façon on a une copie, c'est pas plus pertinent que
foo(const int);
En plus, pour A, j'entends déjà les critiques de perfs parce que
je passe une structure en paramètre, et que ça va pénaliser les perfs
là où
sizePile(const Pile* p)
aurait été bien meilleur (mais plus mauvais pour B puisqu'on
rajouterais une indirection supplémentaire)
Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
Dans D (tableau), c'est initPile qui rajoute une indirection inutile.
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Des idées, des commentaires, vos pratiques ?
J'ai des interrogations de style sur les signatures de fonctions
associées aux types utilisateurs, surtout question de style, pas
vitales, mais bon...
Prenons un type Pile, pile d'entiers.
Je vois 4 familles de définition de ce type
A) struct avec taille+tableau dynamique
typedef struct {
int* tab;
size_t capacite, taille;
} Pile;
B) Ptr sur 1ere element d'une liste chainee
struct cel {
struct cel* next;
int i;
};
typedef struct cel * Pile;
C) TAD pur
typedef Pile; // dans le .h
// version A ou B dans le .c
D) Tableau de taille fixe (pile bornée + délimiteur de fin)
typedef int Pile[CAPA+1];
Quelles signatures donner aux fonctions qui la manipulent ? Pour
alimenter la discution, prenons simplement initialisation et taille.
Moi, ma tendance serait:
A+B+D: // struct, pointeur et tableau
bool initPile(Pile* p);
size_t sizePile(const Pile p);
C: // TAD
Pile* newPile(void);
size_t sizePile(const Pile *p);
Mais (et c'est là que commencent mes interrogations):
Dans A et B (struct et pointeur), le const de size est superflux: de
toute façon on a une copie, c'est pas plus pertinent que
foo(const int);
En plus, pour A, j'entends déjà les critiques de perfs parce que
je passe une structure en paramètre, et que ça va pénaliser les perfs
là où
sizePile(const Pile* p)
aurait été bien meilleur (mais plus mauvais pour B puisqu'on
rajouterais une indirection supplémentaire)
Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
Dans D (tableau), c'est initPile qui rajoute une indirection inutile.
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Des idées, des commentaires, vos pratiques ?
J'ai des interrogations de style sur les signatures de fonctions
associées aux types utilisateurs, surtout question de style, pas
vitales, mais bon...
Prenons un type Pile, pile d'entiers.
Je vois 4 familles de définition de ce type
A) struct avec taille+tableau dynamique
typedef struct {
int* tab;
size_t capacite, taille;
} Pile;
B) Ptr sur 1ere element d'une liste chainee
struct cel {
struct cel* next;
int i;
};
typedef struct cel * Pile;
C) TAD pur
typedef Pile; // dans le .h
// version A ou B dans le .c
D) Tableau de taille fixe (pile bornée + délimiteur de fin)
typedef int Pile[CAPA+1];
Quelles signatures donner aux fonctions qui la manipulent ? Pour
alimenter la discution, prenons simplement initialisation et taille.
Moi, ma tendance serait:
A+B+D: // struct, pointeur et tableau
bool initPile(Pile* p);
size_t sizePile(const Pile p);
C: // TAD
Pile* newPile(void);
size_t sizePile(const Pile *p);
Mais (et c'est là que commencent mes interrogations):
Dans A et B (struct et pointeur), le const de size est superflux: de
toute façon on a une copie, c'est pas plus pertinent que
foo(const int);
En plus, pour A, j'entends déjà les critiques de perfs parce que
je passe une structure en paramètre, et que ça va pénaliser les perfs
là où
sizePile(const Pile* p)
aurait été bien meilleur (mais plus mauvais pour B puisqu'on
rajouterais une indirection supplémentaire)
Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
Dans D (tableau), c'est initPile qui rajoute une indirection inutile.
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Des idées, des commentaires, vos pratiques ?
Dans A et B (struct et pointeur), le const de size est superflux: de
toute façon on a une copie, c'est pas plus pertinent que
foo(const int);
En plus, pour A, j'entends déjà les critiques de perfs parce que
je passe une structure en paramètre, et que ça va pénaliser les perfs
là où
sizePile(const Pile* p)
aurait été bien meilleur (mais plus mauvais pour B puisqu'on
rajouterais une indirection supplémentaire)
Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
Des idées, des commentaires, vos pratiques ?
Dans A et B (struct et pointeur), le const de size est superflux: de
toute façon on a une copie, c'est pas plus pertinent que
foo(const int);
En plus, pour A, j'entends déjà les critiques de perfs parce que
je passe une structure en paramètre, et que ça va pénaliser les perfs
là où
sizePile(const Pile* p)
aurait été bien meilleur (mais plus mauvais pour B puisqu'on
rajouterais une indirection supplémentaire)
Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
Des idées, des commentaires, vos pratiques ?
Dans A et B (struct et pointeur), le const de size est superflux: de
toute façon on a une copie, c'est pas plus pertinent que
foo(const int);
En plus, pour A, j'entends déjà les critiques de perfs parce que
je passe une structure en paramètre, et que ça va pénaliser les perfs
là où
sizePile(const Pile* p)
aurait été bien meilleur (mais plus mauvais pour B puisqu'on
rajouterais une indirection supplémentaire)
Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
Des idées, des commentaires, vos pratiques ?
Mais comme tu le sais, j'utilise OOC (pas du tout la version que tu as,
on a beaucoup simplifie depuis) ou je ne pratique l'ADT que pour les
classes "final" a cause de la derivation (on ne peut pas deriver d'un
ADT :-). Pour les methodes, elles sont de toutes facon encapsulees dans
la classe et les signatures sont plutot du type
Pile_t* new(struct Pile*, size_t initSize);
size_t size(Pile_t*);
Pile_t* new(const struct Pile*, size_t initSize);
size_t size(const Pile_t*);
Mais comme tu le sais, j'utilise OOC (pas du tout la version que tu as,
on a beaucoup simplifie depuis) ou je ne pratique l'ADT que pour les
classes "final" a cause de la derivation (on ne peut pas deriver d'un
ADT :-). Pour les methodes, elles sont de toutes facon encapsulees dans
la classe et les signatures sont plutot du type
Pile_t* new(struct Pile*, size_t initSize);
size_t size(Pile_t*);
Pile_t* new(const struct Pile*, size_t initSize);
size_t size(const Pile_t*);
Mais comme tu le sais, j'utilise OOC (pas du tout la version que tu as,
on a beaucoup simplifie depuis) ou je ne pratique l'ADT que pour les
classes "final" a cause de la derivation (on ne peut pas deriver d'un
ADT :-). Pour les methodes, elles sont de toutes facon encapsulees dans
la classe et les signatures sont plutot du type
Pile_t* new(struct Pile*, size_t initSize);
size_t size(Pile_t*);
Pile_t* new(const struct Pile*, size_t initSize);
size_t size(const Pile_t*);
C) TAD pur
typedef Pile; // dans le .h
attention, tu peux avoir des surprises avec ca. Pile n'est pas un ADT
mais in int (avec un joli warning du compilo)!
typdedef struct Pile Pile;
serait mieux.// version A ou B dans le .c
D) Tableau de taille fixe (pile bornée + délimiteur de fin)
typedef int Pile[CAPA+1];
pas beau.
si tu veux minimiser les malloc, je suggere
struct Pile {
size_t size, capacity;
int stack[1];
};
dans le .c mais ca a le deavantage de devoir avoir des signatures du type:
Pile* pile_push(Pile* pile, int elem);
et de ne pas oublier de reassigner le pointeur sur pile.
Ou d'integrer le type pointeur dans le typedef et avoir
typedef struct Pile *Pile;
Pile* pile_push(Pile* pile, int elem);
mais ca implique une indirection de plus dans le .c.
Donc supposons que minimiser les malloc n'est pas ta principale
preoccupation, j'utiliserais.
typdedef struct Pile Pile;
dans le .h
et
struct Pile {
size_t size, capacity;
int *stack;
};
dans le .c.
Je prefere ca de loin C. Avec cependant un nommage different
Pile* pile_new(size_t initSize);
size_t pile_size(const Pile *p);
Passer des structures par valeur signifie adopter une semantique par
valeur partout, ce qu'il faut eviter, pas seulement pour des raisons de
perfs.
Il ne peut pas s'il ne connait pas au minimum la definition de la
fonction (ex: appel recursif). Je ne crois pas qu'aucun compilateur ne
se risquerait a changer une semantique par valeur par une semantique par
reference.
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
C) TAD pur
typedef Pile; // dans le .h
attention, tu peux avoir des surprises avec ca. Pile n'est pas un ADT
mais in int (avec un joli warning du compilo)!
typdedef struct Pile Pile;
serait mieux.
// version A ou B dans le .c
D) Tableau de taille fixe (pile bornée + délimiteur de fin)
typedef int Pile[CAPA+1];
pas beau.
si tu veux minimiser les malloc, je suggere
struct Pile {
size_t size, capacity;
int stack[1];
};
dans le .c mais ca a le deavantage de devoir avoir des signatures du type:
Pile* pile_push(Pile* pile, int elem);
et de ne pas oublier de reassigner le pointeur sur pile.
Ou d'integrer le type pointeur dans le typedef et avoir
typedef struct Pile *Pile;
Pile* pile_push(Pile* pile, int elem);
mais ca implique une indirection de plus dans le .c.
Donc supposons que minimiser les malloc n'est pas ta principale
preoccupation, j'utiliserais.
typdedef struct Pile Pile;
dans le .h
et
struct Pile {
size_t size, capacity;
int *stack;
};
dans le .c.
Je prefere ca de loin C. Avec cependant un nommage different
Pile* pile_new(size_t initSize);
size_t pile_size(const Pile *p);
Passer des structures par valeur signifie adopter une semantique par
valeur partout, ce qu'il faut eviter, pas seulement pour des raisons de
perfs.
Il ne peut pas s'il ne connait pas au minimum la definition de la
fonction (ex: appel recursif). Je ne crois pas qu'aucun compilateur ne
se risquerait a changer une semantique par valeur par une semantique par
reference.
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
C) TAD pur
typedef Pile; // dans le .h
attention, tu peux avoir des surprises avec ca. Pile n'est pas un ADT
mais in int (avec un joli warning du compilo)!
typdedef struct Pile Pile;
serait mieux.// version A ou B dans le .c
D) Tableau de taille fixe (pile bornée + délimiteur de fin)
typedef int Pile[CAPA+1];
pas beau.
si tu veux minimiser les malloc, je suggere
struct Pile {
size_t size, capacity;
int stack[1];
};
dans le .c mais ca a le deavantage de devoir avoir des signatures du type:
Pile* pile_push(Pile* pile, int elem);
et de ne pas oublier de reassigner le pointeur sur pile.
Ou d'integrer le type pointeur dans le typedef et avoir
typedef struct Pile *Pile;
Pile* pile_push(Pile* pile, int elem);
mais ca implique une indirection de plus dans le .c.
Donc supposons que minimiser les malloc n'est pas ta principale
preoccupation, j'utiliserais.
typdedef struct Pile Pile;
dans le .h
et
struct Pile {
size_t size, capacity;
int *stack;
};
dans le .c.
Je prefere ca de loin C. Avec cependant un nommage different
Pile* pile_new(size_t initSize);
size_t pile_size(const Pile *p);
Passer des structures par valeur signifie adopter une semantique par
valeur partout, ce qu'il faut eviter, pas seulement pour des raisons de
perfs.
Il ne peut pas s'il ne connait pas au minimum la definition de la
fonction (ex: appel recursif). Je ne crois pas qu'aucun compilateur ne
se risquerait a changer une semantique par valeur par une semantique par
reference.
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
oulà ! malheureux ! et le respect de l'ABI ?!? on en a pendu pour
moins que ça.
Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
oulà ! malheureux ! et le respect de l'ABI ?!? on en a pendu pour
moins que ça.
Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
oulà ! malheureux ! et le respect de l'ABI ?!? on en a pendu pour
moins que ça.
En , DINH Viêt Hoà va escriure:Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
oulà ! malheureux ! et le respect de l'ABI ?!? on en a pendu pour
moins que ça.
Mmmmh. Cela peut justement *être* l'ABI: les structures ne sont pas passées
par valeur, mais toujours par référence. Si la structure est modifiée dans
la fonction appelée, celle-ci est priée d'en faire une copie locale.
En etPan.4088e24d.312be56a.b94@homer, DINH Viêt Hoà va escriure:
Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
oulà ! malheureux ! et le respect de l'ABI ?!? on en a pendu pour
moins que ça.
Mmmmh. Cela peut justement *être* l'ABI: les structures ne sont pas passées
par valeur, mais toujours par référence. Si la structure est modifiée dans
la fonction appelée, celle-ci est priée d'en faire une copie locale.
En , DINH Viêt Hoà va escriure:Sauf si les compilateurs savent remplacer les appels du genre
foo(const T) en foo(const T*) s'ils pensent que ce sera plus
rapide (inline ou cout indirection negligeable par rapport au
gain en copie).
oulà ! malheureux ! et le respect de l'ABI ?!? on en a pendu pour
moins que ça.
Mmmmh. Cela peut justement *être* l'ABI: les structures ne sont pas passées
par valeur, mais toujours par référence. Si la structure est modifiée dans
la fonction appelée, celle-ci est priée d'en faire une copie locale.
In article <c6anl2$gt8$, Laurent Deniau wrote:Ou d'integrer le type pointeur dans le typedef et avoir
typedef struct Pile *Pile;
Pile* pile_push(Pile* pile, int elem);
mais ca implique une indirection de plus dans le .c.
Donc supposons que minimiser les malloc n'est pas ta principale
preoccupation, j'utiliserais.
Surtout que ca n'économise un malloc qu'a chaque creation de pile,
je sais pas si c'est le pire.
typdedef struct Pile Pile;
dans le .h
et
struct Pile {
size_t size, capacity;
int *stack;
};
dans le .c.
Oui, c'est le TAD avec implementation A.Je prefere ca de loin C. Avec cependant un nommage different
Pile* pile_new(size_t initSize);
size_t pile_size(const Pile *p);
Oui, je note le prefixage contre le postfixage...
Des raisons ?
Passer des structures par valeur signifie adopter une semantique par
valeur partout, ce qu'il faut eviter, pas seulement pour des raisons de
perfs.
Tu penses que l'utilisateur sera trop destabilisé si autre chose
qu'un pointeur n'a pas de sémantique de valeur ?
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
Comment ca elle coute rien ?
In article <c6anl2$gt8$1@sunnews.cern.ch>, Laurent Deniau wrote:
Ou d'integrer le type pointeur dans le typedef et avoir
typedef struct Pile *Pile;
Pile* pile_push(Pile* pile, int elem);
mais ca implique une indirection de plus dans le .c.
Donc supposons que minimiser les malloc n'est pas ta principale
preoccupation, j'utiliserais.
Surtout que ca n'économise un malloc qu'a chaque creation de pile,
je sais pas si c'est le pire.
typdedef struct Pile Pile;
dans le .h
et
struct Pile {
size_t size, capacity;
int *stack;
};
dans le .c.
Oui, c'est le TAD avec implementation A.
Je prefere ca de loin C. Avec cependant un nommage different
Pile* pile_new(size_t initSize);
size_t pile_size(const Pile *p);
Oui, je note le prefixage contre le postfixage...
Des raisons ?
Passer des structures par valeur signifie adopter une semantique par
valeur partout, ce qu'il faut eviter, pas seulement pour des raisons de
perfs.
Tu penses que l'utilisateur sera trop destabilisé si autre chose
qu'un pointeur n'a pas de sémantique de valeur ?
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
Comment ca elle coute rien ?
In article <c6anl2$gt8$, Laurent Deniau wrote:Ou d'integrer le type pointeur dans le typedef et avoir
typedef struct Pile *Pile;
Pile* pile_push(Pile* pile, int elem);
mais ca implique une indirection de plus dans le .c.
Donc supposons que minimiser les malloc n'est pas ta principale
preoccupation, j'utiliserais.
Surtout que ca n'économise un malloc qu'a chaque creation de pile,
je sais pas si c'est le pire.
typdedef struct Pile Pile;
dans le .h
et
struct Pile {
size_t size, capacity;
int *stack;
};
dans le .c.
Oui, c'est le TAD avec implementation A.Je prefere ca de loin C. Avec cependant un nommage different
Pile* pile_new(size_t initSize);
size_t pile_size(const Pile *p);
Oui, je note le prefixage contre le postfixage...
Des raisons ?
Passer des structures par valeur signifie adopter une semantique par
valeur partout, ce qu'il faut eviter, pas seulement pour des raisons de
perfs.
Tu penses que l'utilisateur sera trop destabilisé si autre chose
qu'un pointeur n'a pas de sémantique de valeur ?
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
Comment ca elle coute rien ?
Marc Boyer wrote:Surtout que ca n'économise un malloc qu'a chaque creation de pile,
je sais pas si c'est le pire.
c'est vrai, mais ca peut dans certain cas etre significatif (ex: les
strings).
Oui, je note le prefixage contre le postfixage...
Des raisons ?
Question de gout. J'utilise name_ pour creer un espace de nom (une
classe, un module) quand je n'utilise pas OOC. Et j'utilise la
concatenation pour les mots. Par ex:
bool pile_estVide(const Pile*);
me parait simple et lisible.
Je pense que c'est adapte dans certain cas, mais rare. Pour ma part elle
est souvent dictee plutot par une semantique de retour par valeur
combinee avec la volonte de pouvoir cascader les appels. Ex:
complex z = cpx_add(cpx_mul(z1,z2),z3);
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Yep. Un TAD n'est pas seulement une protection/encapsulation, ca
simplifie aussi le design et les interfaces (plus d'inline, ouf).
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
Comment ca elle coute rien ?
p->stack[n] a la meme rapidite que stack soit un tableau ou un pointeur
sur la pluspart des architectures modernes. Surtout depuis que les
languages objets se sont repandus. J'ai mesure des variations de +-5%
suivant les archis et les compilos. Completement negligeable en
comparaison de tes structures passees par valeur!
Marc Boyer wrote:
Surtout que ca n'économise un malloc qu'a chaque creation de pile,
je sais pas si c'est le pire.
c'est vrai, mais ca peut dans certain cas etre significatif (ex: les
strings).
Oui, je note le prefixage contre le postfixage...
Des raisons ?
Question de gout. J'utilise name_ pour creer un espace de nom (une
classe, un module) quand je n'utilise pas OOC. Et j'utilise la
concatenation pour les mots. Par ex:
bool pile_estVide(const Pile*);
me parait simple et lisible.
Je pense que c'est adapte dans certain cas, mais rare. Pour ma part elle
est souvent dictee plutot par une semantique de retour par valeur
combinee avec la volonte de pouvoir cascader les appels. Ex:
complex z = cpx_add(cpx_mul(z1,z2),z3);
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Yep. Un TAD n'est pas seulement une protection/encapsulation, ca
simplifie aussi le design et les interfaces (plus d'inline, ouf).
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
Comment ca elle coute rien ?
p->stack[n] a la meme rapidite que stack soit un tableau ou un pointeur
sur la pluspart des architectures modernes. Surtout depuis que les
languages objets se sont repandus. J'ai mesure des variations de +-5%
suivant les archis et les compilos. Completement negligeable en
comparaison de tes structures passees par valeur!
Marc Boyer wrote:Surtout que ca n'économise un malloc qu'a chaque creation de pile,
je sais pas si c'est le pire.
c'est vrai, mais ca peut dans certain cas etre significatif (ex: les
strings).
Oui, je note le prefixage contre le postfixage...
Des raisons ?
Question de gout. J'utilise name_ pour creer un espace de nom (une
classe, un module) quand je n'utilise pas OOC. Et j'utilise la
concatenation pour les mots. Par ex:
bool pile_estVide(const Pile*);
me parait simple et lisible.
Je pense que c'est adapte dans certain cas, mais rare. Pour ma part elle
est souvent dictee plutot par une semantique de retour par valeur
combinee avec la volonte de pouvoir cascader les appels. Ex:
complex z = cpx_add(cpx_mul(z1,z2),z3);
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Yep. Un TAD n'est pas seulement une protection/encapsulation, ca
simplifie aussi le design et les interfaces (plus d'inline, ouf).
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
Comment ca elle coute rien ?
p->stack[n] a la meme rapidite que stack soit un tableau ou un pointeur
sur la pluspart des architectures modernes. Surtout depuis que les
languages objets se sont repandus. J'ai mesure des variations de +-5%
suivant les archis et les compilos. Completement negligeable en
comparaison de tes structures passees par valeur!
Laurent Deniau wrote:Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Yep. Un TAD n'est pas seulement une protection/encapsulation, ca
simplifie aussi le design et les interfaces (plus d'inline, ouf).
Mais les programmeurs C ne vont-il pas etre reticents à
payer un appel de fonction pour des choses comme l'acces
à la taille ?
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
Comment ca elle coute rien ?
p->stack[n] a la meme rapidite que stack soit un tableau ou un pointeur
sur la pluspart des architectures modernes. Surtout depuis que les
languages objets se sont repandus. J'ai mesure des variations de +-5%
suivant les archis et les compilos. Completement negligeable en
comparaison de tes structures passees par valeur!
Je suis positivement étonné. Je prends note.
Laurent Deniau wrote:
Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Yep. Un TAD n'est pas seulement une protection/encapsulation, ca
simplifie aussi le design et les interfaces (plus d'inline, ouf).
Mais les programmeurs C ne vont-il pas etre reticents à
payer un appel de fonction pour des choses comme l'acces
à la taille ?
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
Comment ca elle coute rien ?
p->stack[n] a la meme rapidite que stack soit un tableau ou un pointeur
sur la pluspart des architectures modernes. Surtout depuis que les
languages objets se sont repandus. J'ai mesure des variations de +-5%
suivant les archis et les compilos. Completement negligeable en
comparaison de tes structures passees par valeur!
Je suis positivement étonné. Je prends note.
Laurent Deniau wrote:Il n'y a que pour le TAD que c'est simple: comme on a décidé par
design de payer une indirection à chaque fois, tout est simple
ensuite.
Yep. Un TAD n'est pas seulement une protection/encapsulation, ca
simplifie aussi le design et les interfaces (plus d'inline, ouf).
Mais les programmeurs C ne vont-il pas etre reticents à
payer un appel de fonction pour des choses comme l'acces
à la taille ?
Cette indirection ne coute rien. Pourquoi se compliquer la vie?
Comment ca elle coute rien ?
p->stack[n] a la meme rapidite que stack soit un tableau ou un pointeur
sur la pluspart des architectures modernes. Surtout depuis que les
languages objets se sont repandus. J'ai mesure des variations de +-5%
suivant les archis et les compilos. Completement negligeable en
comparaison de tes structures passees par valeur!
Je suis positivement étonné. Je prends note.
"Antoine Leca" writes:Mmmmh. Cela peut justement *être* l'ABI: les structures ne sont pas
passées par valeur, mais toujours par référence. Si la structure est
modifiée dans la fonction appelée, celle-ci est priée d'en faire une
copie locale.
Pas uniquement si, toujours a cause de la possibilite d'alias.
"Antoine Leca" <root@localhost.gov> writes:
Mmmmh. Cela peut justement *être* l'ABI: les structures ne sont pas
passées par valeur, mais toujours par référence. Si la structure est
modifiée dans la fonction appelée, celle-ci est priée d'en faire une
copie locale.
Pas uniquement si, toujours a cause de la possibilite d'alias.
"Antoine Leca" writes:Mmmmh. Cela peut justement *être* l'ABI: les structures ne sont pas
passées par valeur, mais toujours par référence. Si la structure est
modifiée dans la fonction appelée, celle-ci est priée d'en faire une
copie locale.
Pas uniquement si, toujours a cause de la possibilite d'alias.