Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

La fin de l'héritage ?

103 réponses
Avatar
Olivier Azeau
J'ai lu avec grand intérêt l'article "Concepts for C++0x" mentionné dans
un thread récent.
Habituellement, je ne m'intéresse pas aux évolutions du langage car
elles ne me concernent qu'à un relativement long terme, mais là, j'ai
l'impression qu'un mouvement de fond relatif aux paradygmes du C++ prend
une certaine ampleur.
Et ce mouvement m'amène à quelques interrogations.

La plupart des développeurs que je cotoie programment en C++ dans un
style orienté objet très proche de ce que l'on trouve en Java.

Je prends un exemple classique de Bridge+Factory pour illustrer mon propos.
On a typiquement une hiérarchie d'implémentations :
| class DocumentDisplay {
| virtual ~DocumentDisplay();
| virtual void drawText( int x, int y, std::string const &text ) = 0;
| virtual void drawLine( int x1, int y1, int x2, int y2 ) = 0;
| };
|
| class GraphicDocumentDisplay : public DocumentDisplay {
| virtual void drawText( int x, int y, std::string const &text );
| virtual void drawLine( int x1, int y1, int x2, int y2 );
| };
|
| class TextDocumentDisplay : public DocumentDisplay {
| ...

une hiérarchie d'abstractions qui utilisent les implémentations :
|
| class Document {
| public:
| Document( DocumentDisplay *display ) : display_(display) {}
| virtual ~Document();
|
| void drawFrame( int x, int y, int w, int h, std::string const &title ) {
| display_->drawLine( x, y, x+w, y );
| display_->drawLine( x, y+h, x+w, y+h );
| display_->drawText( x, y, title );
| }
| private:
| DocumentDisplay *display_;
| };
|
| class Memo : public Document {
| public:
| Memo( DocumentDisplay *display ) : Document(display) {}
| void drawSummary() { drawFrame( 0, 0, 150, 100, "Summary" ); }
| };
|
| class Invoice : public Document {
| ...

une hiérarchie de fabriques pour instancier tout ça :
| class DocumentFactory {
| public:
| virtual ~DocumentFactory() {}
| virtual Memo *createMemo() = 0;
| virtual Invoice *createInvoice() = 0;
| };
|
| class GraphicDocumentFactory : public DocumentFactory {
| public:
| virtual Memo *createMemo() { return new Memo( new
GraphicDocumentDisplay ); }
| virtual Invoice *createInvoice() { return new Invoice( new
GraphicDocumentDisplay ); }
| };

et je rajoute un programme principal pour utiliser le tout :
| class Application {
| public:
| Application( DocumentFactory *factory ) : factory_(factory) {}
|
| void run() {
| Memo *memo = factory_->createMemo();
| memo->drawSummary();
| }
| private:
| DocumentFactory *factory_;
| };
|
| int main() {
| Application app( new GraphicDocumentFactory );
| app.run();
| }

En pratique, ça a une tête un peu différente (avec le RAII par exemple),
mais l'idée des hiérarchie de classes est là.

Depuis quelques temps se répand la programmation générique qui permet de
faire la même chose avec (en général) moins de lignes de code.

Les implémentations deviennent des "policy" et n'ont plus besoin de
classe de base
| class GraphicDocumentDisplay {
| virtual void drawText( int x, int y, std::string const &text );
| virtual void drawLine( int x1, int y1, int x2, int y2 );
| };

Les abstractions sont paramétrées par la policy.
On garde un héritage pour partager l'implémentation :
| template <class DOCDISP>
| class Document {
| public:
| virtual ~Document() {}
|
| void drawFrame( int x, int y, int w, int h, std::string const &title ) {
| display_.drawLine( x, y, x+w, y );
| display_.drawLine( x, y+h, x+w, y+h );
| display_.drawText( x, y, title );
| }
| private:
| DOCDISP display_;
| };
|
| template <class DOCDISP>
| class Memo : public Document<DOCDISP> {
| public:
| void drawSummary() { drawFrame( 0, 0, 150, 100, "Summary" ); }
| };

Dans un cas aussi simple, on oublie la factory et on paramètre
directement l'application :
| template <class DOCDISP>
| class Application {
| public:
| void run() {
| Memo<DOCDISP> memo;
| memo.drawSummary();
| }
| };
|
| int main() {
| Application<GraphicDocumentDisplay> app;
| app.run();
| }

Une telle approche est actuellement plutôt prisée par des personnes qui
connaissent bien le langage et est donc globalement plutôt minoritaire.
Parmi les raisons de cette situation je vois :
- la forte présence de langages comme Java ou C# qui ne proposent pas
cette approche (les versions "Generic" restent anecdotiques) et donc le
grand nombre de personnes qui font du C++ comme ils font du Java
- le faible support des templates par certains compilateurs jusqu'à une
période récente (cf les interrogations récurrentes relatives au support
des templates dans VC6)
- un support méthodologique peu adapté (UML se prête beaucoup plus à
décrire des héritages que de paramétrages)
- une complexité de mise au point d'une approche à base de templates
(typage structurel, définitions statiques, ...)

Sur ce, je découvre les "concepts" (dont je ne pense pas avoir saisi le
dixième des utilisations et implications) qui me laissent supposer que,
dans un avenir plus ou moins proche, on pourrait écrire les choses de
manière plus explicites.

Les policy pourraient être nommées et contrôlées avant usage. Si j'ai
bien compris les notions et syntaxes proposées dans l'article, cela
donnerait quelque chose comme :
| template <DOCDISP>
| concept DisplaysDocuments {
| void DOCDISP::drawText( int x, int y, std::string const &text );
| void DOCDISP::drawLine( int x1, int y1, int x2, int y2 );
| };
|
| class GraphicDocumentDisplay {
| public:
| void drawText( int x, int y, std::string const &text );
| void drawLine( int x1, int y1, int x2, int y2 );
| };
|
| model DisplaysDocuments<GraphicDocumentDisplay>;

| template <class DOCDISP>
| class Application where { DisplaysDocuments<DOCDISP> } {
| public:
| void run() {
| Memo<DOCDISP> memo;
| memo.drawSummary();
| }
| };

Et j'ai même l'impression que des implémentations par défaut définies au
niveau des concepts pourraient rendre obsolète une grand pan de
l'utilisation de l'héritage.

En écrivant par exemple ce qui suit, j'ai l'impression d'avoir
entièrement réécrit l'exemple vu précédemment sans utiliser aucun
héritage C++ mais avec l'impression d'avoir quand même fait de l'"objet"
(s'il est encore possible de définir ce terme...) mais "autrement".
| template <DOC>
| concept IsDocument {
| typename doc_display;
| require DisplaysDocuments<doc_display>;
|
| doc_display &DOC::display();
|
| void DOC::drawFrame( int x, int y, int w, int h, std::string const
&title ) {
| display().drawLine( x, y, x+w, y );
| display().drawLine( x, y+h, x+w, y+h );
| display().drawText( x, y, title );
| }
| };
|
| template <class DOCDISP>
| class Memo {
| public:
| typedef DOCDISP doc_display;
| doc_display &display() { return display_; }
|
| void drawSummary() { drawFrame( 0, 0, 150, 100, "Summary" ); }
| private:
| DOCDISP display_;
| };
|
| template <class DOCDISP>
| model IsDocument< Memo<DOCDISP> >;

Pour ceux qui auront eu le courage de lire jusqu'ici, j'aimerais savoir
s'ils pensent :
- que je n'ai rien compris aux "concepts" ?
- que ces notions vont avoir un impact technique majeur sur l'écriture
de code en C++ ?
- que ces notions vont avoir un impact sociologique majeur sur le
développement en C++ ?

Je suis plus particulièrement intéressé par l'aspect sociologique des
choses.
J'ai l'impression d'être face à une évolution du même ordre de grandeur
que le passage du C au C++.
Pour tout dire, j'ai même l'impression que le terme C++ n'est conservé
que pour des raisons marketing (la "marque" est déja connue, appréciée,
possède une base de consommateurs, ...)

Il est toujours possible d'écrire du C avec un compilateur C++ mais, sur
une période d'environ 10 ans (en gros les années 90) on est passé d'une
approche majoritaire en termes de structures/procédures a une approche
majoritaire classes/héritages/associations.
Cela a impliqué un changement de principes de modélisation, un
changement de techniques d'écriture de code mais surtout un changement
de mentalité.
Et quand je regarde les efforts qu'il a fallu déployer pour que
l'ensemble des intervenants en arrive à penser les développements plus
ou moins de la même manière, j'ai un peu l'impression, en voyant ces
nouvelles notions qui se profilent à l'horizon, que nous ne sommes pas
au bout de nos peines...

10 réponses

1 2 3 4 5
Avatar
xavier
Olivier Azeau a dit le 01/02/2005 00:58:
Pour ceux qui auront eu le courage de lire jusqu'ici, j'aimerais savoir
s'ils pensent :
- que je n'ai rien compris aux "concepts" ?


Je pense que si, ça correspond du moins à ce que j'en ai compris. Les
concepts du document sont un moyen de décrire les Policy/Concept tels
que l'entend la STL.

- que ces notions vont avoir un impact sociologique majeur sur le
développement en C++ ?


Sur ce point, je pense que l'un des freins actuels à une utilisation des
concepts et de la partie correspondante de la STL vient du fait que, en
gros, seule la norme décrit exactement et formellement ce que sont les
concepts, et la norme n'est pas toujours extrèmement facile à lire.
L'autre problème vient des messages d'erreurs à rallonges qui
apparaissent lorsque le concept n'est pas respecté. Avec des
constructions du genre de celle décrite dans le document, ces deux
problèmes devraient être réduits, et donc l'utilisation plus facile d'accès.

Moi, je me pose un question concernant la construction "model", dans le
document. Je vois très bien l'avantage qu'elle peut avoir pour aider à
conformer une classe à un concept, et je trouve l'idée excellente. Ce
que je ne comprend pas, c'est pourquoi le papier indique clairement que
la déclaration des modèles doit être explicite, et, qu'à la fin, il
indique que l'un des problèmes de leur proposition est justement le
besoin d'explicitement déclarer ces "model". Ce que je n'ais pas trouvé
dans le document, c'est la raison pour laquelle la déclaration de base,
n'est pas déclaré implicitement par le compilateur, s'il y a lieu.

Par exemple :

! template<typeid T>
! concept HasFoo {
! void T::foo() const;
! }
!
! template <typeid T>
! where { HaxFoo<T> }
! void bar(T const & v) {
! v.foo();
! }
!
! class my_class {}
!
! #if 0
! model SomeConcept<my_class> {} // <<-- ici
! #endif
!
! int main() {
! my_class t;
! bar(t);
! }

A priori, vu que my_class n'est pas conforme aux contraintes de HasFoo,
l'erreur potentielle passera de :

Error : 'my_class' is not a model of 'HasFoo'

à :

Error : 'my_class' does not validate the constraint 'void T::foo()
const' of 'SomeConcept<T> [ where T = my_class ]'

Bref, la déclaration implicite me semble sans danger.

Je comprend que l'exemple ci-dessus est un peu trop simple, dans le cas
ci-dessous :

! template<typeid T, typeid U>
! concept HasFoo {
! void T::foo(U) const;
! }
!
! template <typeid T, typeid U>
! where { HaxFoo<T, U> }
! void bar(T const & v, U u) {
! v.foo(u);
! }
!
! class my_class {
! void foo(int) const;
! }
!
! int main() {
! my_class t;
! char u;
! bar(t, u);
! }

Il ne me semble pas clair si le modèle devrait être HasFoo<my_class,
int> ou HasFoo<my_class, char>. Dans ce cas, le compilateur pourrait
détecter l'ambiguité et demander à ce qu'elle soit levée par une
déclaration explicite.

xavier

Avatar
Yalbrieux
Bonjour,

Votre long exposé des faits demanderait un très long développement que je ne
suis pas capable de faire. Cependant, pour avoir vécu l'informatique depuis
pas mal d'années il me vient les réflexions suivantes :
- Le problème de l'adaptation est évidemment celui de l'enseignement. De
deux types : ex nihilo et recyclage. Le recyclage est plus délicat dans ce
secteur .
- J'ai moi-même commis quelques langages non pas pour le plaisir de
complexifier mais parcequ'il n'y avait pas l'outil qu'il nous fallait. Il y
a donc deux démarches : le besoin (mon cas) et la création du besoin pour en
tirer un business (je ne fais pas de citation pour cause de risque
d'incendie).

Le C++ correspondait à un besoin. Sans chercher à déclencher de troll nous
faisions de l'objet, j'irai presque à dire sans le savoir, en C. Le C++ nous
a apporté le confort dont nous avions tant besoin.

Les évolutions que vous citez correspondent-elles à un besoin des
développeurs ? si oui, ne vous posez pas trop de questions : elles prendrons
le pas sur celles qui ne correspondent à rien.
Si celà simplifie et accélère c'est gagné.
Sinon il faudra des efforts de persuasion de la part des promoteurs :
marketing, formation, etc. pour l'imposer. Et ce n'est pas impossible (on se
traîne des quantités de grands mots inutiles pour faire bien). Il est clair
qu'un D.I. prenant une option très chère mais choisie par ses pairs des
grandes sociétés d'à côté ne sera jamais remis en question pour avoir choisi
cette option. Par contre un D.I. prenant une option originale devra se
justifier et est condamné à réussir. Vous savez que le standard n'est pas le
meilleur. Mais c'est le standard et personne ne vous reproche de le choisir.

Pour en revenir au point de départ, la formation se doit de couvrir le
standard mais doit aussi et surtout ouvrir sur l'avenir et apprendre à
apprendre, à démêler le vrai besoin du besoin imposé.
J'enfonce les portes ouvertes d'accord. Mais dans vos exemples trouvez-vous
un consensus dans la classification suivant ce critère ? J'ai tendance à me
dire que telle nouveauté est intéressante et à en chercher immédiatement les
applications possibles et à observer ce que les copains vont faire avec ce
que je laisse a priori de côté.

J'espère que ces humbles réflexions pourrons servir à quelquechose.
Cordialement
Yves
Avatar
Ivan Vecerina
"Olivier Azeau" wrote in
message news:HuzLd.19143$
La plupart des développeurs que je cotoie programment en C++ dans un style
orienté objet très proche de ce que l'on trouve en Java.
De même que longtemps, les développeurs C++ on programmé en

procédural, car c'était alors le paradigme dominant.

Je prends un exemple classique de Bridge+Factory pour illustrer mon
propos.
On a typiquement une hiérarchie d'implémentations :
[.....]

En résumé:
Document utilise un polymorphise en phase d'exécution (? en anglais,
run-time polymorphism) via l'interface DocumentDisplay.

Depuis quelques temps se répand la programmation générique qui permet de
faire la même chose avec (en général) moins de lignes de code.


La véritable différence est le passage à un polymorphisme
au moment de l'instantiation d'un objet, utilisant 2 types différents.
Ces 2 types sont générés à partir d'une même classe template,
instanciée soit avec un type de document display, soit avec l'autre.
On remplace le dispatching effectué lors de chaque appel d'une
fonction de DocumentDisplay, à une duplication du code de Document
pour un type d'affichage ou l'autre.

Les implémentations deviennent des "policy" et n'ont plus besoin de classe
de base
| class GraphicDocumentDisplay {
| virtual void drawText( int x, int y, std::string const &text );
| virtual void drawLine( int x1, int y1, int x2, int y2 );
| };
NB: les fonctions membres n'ont ici plus besoin d'être virtuelles

(le 'virtual' devrait être omis).

Dans un cas aussi simple, on oublie la factory et on paramètre directement
l'application :
| template <class DOCDISP>
| class Application {
| public:
| void run() {
| Memo<DOCDISP> memo;
| memo.drawSummary();
| }
| };
|
| int main() {
| Application<GraphicDocumentDisplay> app;
| app.run();
| }
Il faut noter comme le résultat est différent: la version OO produit

une application qui par l'intermédiaire de la Factory pouvait créer
un type de document ou l'autre. Plus de flexibilitté, avec un certain
coût en terme de performance (pas significatif ici).
Avec la version générique, on compile soit un type d'application,
soit l'autre, à partir du même code.

Une telle approche est actuellement plutôt prisée par des personnes qui
connaissent bien le langage et est donc globalement plutôt minoritaire.
Parmi les raisons de cette situation je vois :
- la forte présence de langages comme Java ou C# qui ne proposent pas
cette approche (les versions "Generic" restent anecdotiques) et donc le
grand nombre de personnes qui font du C++ comme ils font du Java
Oui.

- le faible support des templates par certains compilateurs jusqu'à une
période récente (cf les interrogations récurrentes relatives au support
des templates dans VC6)
VC6 imposait quelques contorsions pour des exercices sophistiqués,

mais ces limitations étaient tout sauf redhibitoires. Une STL potable
pouvait y être implémentée (même si celle inclue avait des problèmes).
- un support méthodologique peu adapté (UML se prête beaucoup plus à
décrire des héritages que de paramétrages)
UML inclut des notations pour les paramétrages (certes moins utilisées).

- une complexité de mise au point d'une approche à base de templates
(typage structurel, définitions statiques, ...)
Peut-être, même si des usages de bases des templates ne sont pas

si complexes.
Le problème est surtout d'avoir une méthodologie pour choisir un
type d'approche ou l'autre: savoir d'abord choisir entre un
polymorphisme en cours d'exécution ou de compilation.
Dans "Multi-Paradigm Design for C++", James Coplien essaie
de décrire une méthode. Il y en a d'autres...

Sur ce, je découvre les "concepts" (dont je ne pense pas avoir saisi le
dixième des utilisations et implications) qui me laissent supposer que,
dans un avenir plus ou moins proche, on pourrait écrire les choses de
manière plus explicites.
Il me semble que les concepts ne changent rien en terme du

code qui peut être généré. Il s'agit surtout de rendre l'usage
des templates plus lisible et aisée (ce qui a son importance).

Et j'ai même l'impression que des implémentations par défaut définies au
niveau des concepts pourraient rendre obsolète une grand pan de
l'utilisation de l'héritage.

En écrivant par exemple ce qui suit, j'ai l'impression d'avoir entièrement
réécrit l'exemple vu précédemment sans utiliser aucun héritage C++ mais
avec l'impression d'avoir quand même fait de l'"objet" (s'il est encore
possible de définir ce terme...) mais "autrement".
Les concepts peuvent sembler similaires à de l'orienté-objet.

En fait ils sont juste un habillage différent des templates AMHA.

Pour ceux qui auront eu le courage de lire jusqu'ici, j'aimerais savoir
s'ils pensent :
- que je n'ai rien compris aux "concepts" ?
- que ces notions vont avoir un impact technique majeur sur l'écriture de
code en C++ ?
- que ces notions vont avoir un impact sociologique majeur sur le
développement en C++ ?
Je pense simplement que le C++ est un language complexe car il intègre

nombre de concpets et de paradigmes de programmation. Ceci le rend
puissant, mais aussi complexe et plus difficile à aborder.

Les templates, clairement, ont été abusés pour accéder à de nouvelles
fonctionnalités, et peut-être bien qu'un système de 'concepts'
peut améliorer les choses.

Je suis plus particulièrement intéressé par l'aspect sociologique des
choses.
J'ai l'impression d'être face à une évolution du même ordre de grandeur
que le passage du C au C++.
Pour tout dire, j'ai même l'impression que le terme C++ n'est conservé que
pour des raisons marketing (la "marque" est déja connue, appréciée,
possède une base de consommateurs, ...)
Ce n'est pas que du marketing. Le C++ a bénéficié de l'héritage C

pas seulement à cause du nom similaire, mais grâce à un degré de
compatibilité très élevé.

Il est toujours possible d'écrire du C avec un compilateur C++ mais, sur
une période d'environ 10 ans (en gros les années 90) on est passé d'une
approche majoritaire en termes de structures/procédures a une approche
majoritaire classes/héritages/associations.
Cela a impliqué un changement de principes de modélisation, un changement
de techniques d'écriture de code mais surtout un changement de mentalité.
Et quand je regarde les efforts qu'il a fallu déployer pour que l'ensemble
des intervenants en arrive à penser les développements plus ou moins de la
même manière, j'ai un peu l'impression, en voyant ces nouvelles notions
qui se profilent à l'horizon, que nous ne sommes pas au bout de nos
peines...


Je ne pense pas que les intervenants pensent "plus ou moins de la même
manière". Une librairie de type Blitz est très différente de MFC/OWL.
Le C++ a ceci d'unique qu'il permet une large gamme de techniques
de programmation.
Certes, il faut beaucoup de temps pour qu'un nouveau concept soit
assimilé par la base d'utilisateurs (les STL existent depuis bien
longtemps sous forme standardisée, et certains les évitent encore!).


Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form

Avatar
Olivier Azeau
xavier wrote:
Moi, je me pose un question concernant la construction "model", dans
le

document. Je vois très bien l'avantage qu'elle peut avoir pour aider
à

conformer une classe à un concept, et je trouve l'idée excellente.
Ce

que je ne comprend pas, c'est pourquoi le papier indique clairement
que

la déclaration des modèles doit être explicite, et, qu'à la fin,
il

indique que l'un des problèmes de leur proposition est justement le
besoin d'explicitement déclarer ces "model". Ce que je n'ais pas
trouvé

dans le document, c'est la raison pour laquelle la déclaration de
base,

n'est pas déclaré implicitement par le compilateur, s'il y a lieu.

Par exemple :

! template<typeid T>
! concept HasFoo {
! void T::foo() const;
! }
!
! template <typeid T>
! where { HaxFoo<T> }
! void bar(T const & v) {
! v.foo();
! }
!
! class my_class {}
!
! #if 0
! model SomeConcept<my_class> {} // <<-- ici
! #endif
!
! int main() {
! my_class t;
! bar(t);
! }

A priori, vu que my_class n'est pas conforme aux contraintes de
HasFoo,

l'erreur potentielle passera de :

Error : 'my_class' is not a model of 'HasFoo'

à :

Error : 'my_class' does not validate the constraint 'void T::foo()
const' of 'SomeConcept<T> [ where T = my_class ]'


Sur ce point, je vois surtout l'utilité de 'model' quand un individu 1
a écrit le concept HasFoo, un individu 2 a écrit la fonction bar, un
individu 3 a écrit my_class et model et un individu 4 a écrit le
main.

L'erreur <'my_class' is not a model of 'HasFoo'> sera adressée
(facilement) par le No3 qui ne connait que le code du No1 alors que
l'erreur <'my_class' does not validate the constraint ...> devra etre
prise en charge par le No4 qui connait a coup sur les codes du No2 et
No3 mais qui n'a pas forcément une vision exhaustive du code du No1...

Ce point rejoint effectivement les aspects sociologiques au sens ou le
plus important (pour moi) la dedans n'est pas tellement de savoir si
telle syntaxe est préférable a telle autre mais de savoir si n
individus avec leur propre vécu de C, de C++, de Java... vont arriver
a communiquer efficacement au travers de ces notions.

Avatar
Olivier Azeau
Ivan Vecerina wrote:
"Olivier Azeau"
wrote in

message news:HuzLd.19143$
[snip]

Les implémentations deviennent des "policy" et n'ont plus besoin
de classe


de base
| class GraphicDocumentDisplay {
| virtual void drawText( int x, int y, std::string const &text );
| virtual void drawLine( int x1, int y1, int x2, int y2 );
| };
NB: les fonctions membres n'ont ici plus besoin d'être virtuelles

(le 'virtual' devrait être omis).


Exact ! C'est le probleme du "Copy Paste Programming" :-(

Dans un cas aussi simple, on oublie la factory et on paramètre
directement


l'application :
| template <class DOCDISP>
| class Application {
| public:
| void run() {
| Memo<DOCDISP> memo;
| memo.drawSummary();
| }
| };
|
| int main() {
| Application<GraphicDocumentDisplay> app;
| app.run();
| }
Il faut noter comme le résultat est différent: la version OO

produit

une application qui par l'intermédiaire de la Factory pouvait créer
un type de document ou l'autre. Plus de flexibilitté, avec un
certain

coût en terme de performance (pas significatif ici).
Avec la version générique, on compile soit un type d'application,
soit l'autre, à partir du même code.


Je ne comprends pas. Je peux également choisir dynamiquement le type
d'affichage avec la version template :
| int main() {
| if( graphicDisplayIsAvailable() ) {
| Application<GraphicDocumentDisplay> app;
| app.run();
| } else {
| Application<TextDocumentDisplay> app;
| app.run();
| }
| }

Le problème est surtout d'avoir une méthodologie pour choisir un
type d'approche ou l'autre: savoir d'abord choisir entre un
polymorphisme en cours d'exécution ou de compilation.
Dans "Multi-Paradigm Design for C++", James Coplien essaie
de décrire une méthode. Il y en a d'autres...

Sur ce, je découvre les "concepts" (dont je ne pense pas avoir
saisi le


dixième des utilisations et implications) qui me laissent supposer
que,


dans un avenir plus ou moins proche, on pourrait écrire les choses
de


manière plus explicites.
Il me semble que les concepts ne changent rien en terme du

code qui peut être généré. Il s'agit surtout de rendre l'usage
des templates plus lisible et aisée (ce qui a son importance).


Mais justement, est-ce que la problematique du choix de l'approche
n'est pas principalement due aux difficultés de mise en oeuvre du
"polymorphisme de compilation" dans l'état actuel des templates ?

Et j'ai même l'impression que des implémentations par défaut
définies au


niveau des concepts pourraient rendre obsolète une grand pan de
l'utilisation de l'héritage.

En écrivant par exemple ce qui suit, j'ai l'impression d'avoir
entièrement


réécrit l'exemple vu précédemment sans utiliser aucun héritage
C++ mais


avec l'impression d'avoir quand même fait de l'"objet" (s'il est
encore


possible de définir ce terme...) mais "autrement".
Les concepts peuvent sembler similaires à de l'orienté-objet.

En fait ils sont juste un habillage différent des templates AMHA.


Le terme habillage me parait réducteur.
J'ai l'impression que les concepts "habillent" la programmation
générique en C++ tout comme le C++ a habillé la programmation objet
en C.

Je pense simplement que le C++ est un language complexe car il
intègre

nombre de concpets et de paradigmes de programmation. Ceci le rend
puissant, mais aussi complexe et plus difficile à aborder.


Ok. Donc en rajoutant de nouvelles notions qui simplifient une certaine
approche du développement, on complexifie la mise en oeuvre d'une
utilisation de C++.


Avatar
Olivier Azeau
Yalbrieux wrote:
Les évolutions que vous citez correspondent-elles à un besoin des
développeurs ? si oui, ne vous posez pas trop de questions : elles
prendrons

le pas sur celles qui ne correspondent à rien.
Si celà simplifie et accélère c'est gagné.


Oui... Mais c'est la période transition/cohabitation entre les
approches que je trouve loin d'etre gagnée.
Pour un projet donné, faudra-t-il imposer une approche ? Dans quelle
mesure les notions peuvent cohabiter ? (Qui n'a jamais vu a un certain
moment des projets ou la moitié des personnes écrivait du C++/objet
et l'autre moitié du C++/procédural ?)

Avatar
Jean-Marc Bourguet
"Olivier Azeau" writes:

Pour un projet donné, faudra-t-il imposer une approche ?


Le polymorphisme parametrique (la genericite) et le polymorphisme de
substitution (celui qui resulte de l'heritage) ont des domaines
d'application differents mais qui se recoupent un peu. Oui la mode
pour le moment est d'utiliser plutot le premier que le second.

Quant aux "concepts", je les vois comme une evolution dans le
polymorphisme parametrique. L'objectif premier est a coup sur
d'arriver a exprimer plus simplement (et en cas de probleme avec une
detection d'erreur a plus haut niveau) ce qui est exprimable
maintenant. On aura peut-etre plus une puissance d'expression plus
forte, en particulier si on part vers un sous-jacement theorique
proche de la theorie des categories -- ce qui me semble faire partie
de la reflexion de Gaby.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
xavier
Olivier Azeau a dit le 01/02/2005 14:03:

Sur ce point, je vois surtout l'utilité de 'model' quand un individu 1
a écrit le concept HasFoo, un individu 2 a écrit la fonction bar, un
individu 3 a écrit my_class et model et un individu 4 a écrit le
main.


Actuellement, le problème est du même ordre dans le cas où No4 crée un
std::vector<some_class>, 'some_class' étant écrit par No3 est n'étant
pas CopyConstructible. Sauf que le message d'erreur risque fort de
dérouter No4.

Utiliser un concept, je pense, requiert de connaître un minimum des
contraintes liée à ce concept.

xavier

Avatar
Olivier Azeau
xavier wrote:
Olivier Azeau a dit le 01/02/2005 14:03:

Sur ce point, je vois surtout l'utilité de 'model' quand un
individu 1


a écrit le concept HasFoo, un individu 2 a écrit la fonction bar,
un


individu 3 a écrit my_class et model et un individu 4 a écrit le
main.


Actuellement, le problème est du même ordre dans le cas où No4
crée un

std::vector<some_class>, 'some_class' étant écrit par No3 est
n'étant

pas CopyConstructible. Sauf que le message d'erreur risque fort de
dérouter No4.


Pourquoi ?
Si j'ai bien suivi ton exemple, No4 pourrait avoir quelque chose comme:
Error : 'some_class' is not a model of 'CopyConstructible' required by
std::vector<>

Je ne trouve pas cela déroutant et en tout cas bien mieux que de me
dire qu'il manque je ne sais quel constructeur dans some_class pour une
raison que j'ignore.

Utiliser un concept, je pense, requiert de connaître un minimum des
contraintes liée à ce concept.


Tout dépend ce que l'on entend par 'utiliser un concept'.
Si c'est pour dire que telle classe le modélise, c'est évident.
Si c'est pour déclarer un std::vector<bidule>, tout ce qui m'interesse
c'est de savoir que bidule respecte les contraintes imposées par
std::vector. Si je dois me préoccuper du contenu de ces contraintes,
tout ce systeme n'a plus aucun interet.


Avatar
xavier
Olivier Azeau a dit le 01/02/2005 17:16:
xavier wrote:
Actuellement, le problème est du même ordre dans le cas où No4
crée un
std::vector<some_class>, 'some_class' étant écrit par No3 est
n'étant
pas CopyConstructible. Sauf que le message d'erreur risque fort de
dérouter No4.


Pourquoi ?


J'aurais du être plus clair. J'ai dit actuellement, dans le sens le
langage C++ actuel. L'erreur sera sans doute quelque chose du genre :
'Missing constructor some_class::some_class(some_class const &)'
suivi de la liste des constructeur existant. Je suppose que quiconque
qui, actuellement, débute dans l'utilisation de la STL arrive un moment
ou à un autre à ses messages d'erreur qui s'étendent sur 30 lignes et
qui demandent 10 minutes à décrypter. Et je me rappelle que la première
fois que ça m'est arrivé, ça m'a vraiment refroidi.

Je ne trouve pas cela déroutant et en tout cas bien mieux que de me
dire qu'il manque je ne sais quel constructeur dans some_class pour une
raison que j'ignore.


Actuellement, c'est bien ce qui se passe. Dans les deux cas (model
explicite ou implicite) que j'envisage, l'erreur est plus claire car
elle référence soit le concept, soit la contrainte. L'utilisateur peut
alors regarder dans la documentation du concept pour déterminer ou est
l'erreur.

Si je dois me préoccuper du contenu de ces contraintes,
tout ce systeme n'a plus aucun interet.


Je ne suis pas entièrement d'accord avec ceci, le but du système est, il
me semble, de rendre explicite les contraintes imposées par un concept,
pas de les masquer pour l'utilisateur final.

xavier


1 2 3 4 5