Initialisation variable "static" dans bibliothèque statique

Le
David Fleury
Bonjour,
j'essaye de faire une Fabrique stockée dans une bibliothèque statique.
Dans cette même bibliothèque, je veux mettre des Fabriqués et
les enregistrés par une méthode du type :
bool Factory::Register( id, callback );

pour appeler directement, la méthode Register du singleton Factory,
j'utilise un namespace non nommé dans chacun des fichiers contenant
les Fabriqués.

class UnFabriqueConcret {
};

namespace {
bool regUnFab = Factory::Instance()::Register( FAB_1, Callback1 );
}

Mon problème, est que la fonction qui devrait être appelée lors
de l'initialisation de la variable regUnFab, n'est jamais appelé
quand je le fais à partir d'un fichier contenu dans la bibliothèque
statique.

Je pense qu'il me manque des connaissances sur la manière dont sont
lièées les bibliotèques. Je pensais à tort qu'un .a n'était qu'un
ensemble de .o, et que g++ main.cpp data.o ou g++ main.cpp libdata.a
était équivalent (libdata.a contenant data.o)

Voici un exemple complet qui ne fonctionne pas comme je veux :

// BEGIN data.cpp
#include <iostream>
bool Function() {
return std::cout << "Calling Function" << std::endl;
}
namespace { bool staticCall = Function(); }
// END data.cpp

// BEGIN local_data.cpp
#include <iostream>
bool LocalFunction() {
return std::cout << "Local Data Calling Function" << std::endl;
}
namespace {
bool staticCall = LocalFunction();
}
// END local_data.cpp

// BEGIN main.cpp
#include <iostream>
bool Function() {
return std::cout << "Main Calling Function" << std::endl;
}
namespace { bool staticCall = Function(); }
int main()
{}
// END main.cpp

# Makefile
all:libdata.a main

libdata.a:data.o
ar -r $@ $^

main:libdata.a
g++ -o test_data main.cpp local_data.cpp libdata.a

.cpp.o:
g++ -c -o $@ $<

Voilà, c'est un peu long désolé. J'espère que c'est clair au moins.


Merci de votre aide.

David.
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
James Kanze
Le #304450
David Fleury wrote:

j'essaye de faire une Fabrique stockée dans une bibliothèque statique.
Dans cette même bibliothèque, je veux mettre des Fabriqués et
les enregistrés par une méthode du type :
bool Factory::Register( id, callback );

pour appeler directement, la méthode Register du singleton Factory,
j'utilise un namespace non nommé dans chacun des fichiers contenant
les Fabriqués.

class UnFabriqueConcret {
};

namespace {
bool regUnFab = Factory::Instance()::Register( FAB_1, Callback1 );
}


A priori, l'instance de Callback1 est une variable static aussi,
non ? Et la classe ne sert à rien d'autre. Alors, pourquoi pas
faire la régistration dans son constructeur ? (L'idiome
classique, d'ailleur, fait la régistration dans le constructeur
de la classe de base : Factory::Factory.)

Méfie-toi des problèmes de l'ordre de l'initialisation ; il
faut en général que la régistrie soit gérée comme un singleton.

Mon problème, est que la fonction qui devrait être appelée lors
de l'initialisation de la variable regUnFab, n'est jamais appelé
quand je le fais à partir d'un fichier contenu dans la bibliothèque
statique.


Est-ce que tu inclus le fichier dans ton programme ? Mettre un
fichier objet dans une bibliothèque ne force pas son inclusion
dans le programme, même si la bibliothèque sert par ailleurs
dans le programme. (C'est la définition même d'une bibliothèque
en informatique.)

Si tu as des « modules » (unités de traduction, dans le
langage de la norme) que tu veux inclure inconditionnellement
dans le programme, il faut bien que tu le dises à l'éditeur de
liens. Enfin, dans tous les systèmes que je connais. Il faut de
toute façon que tu spécifies quelque part ce qui fait partie du
programme, de façon implicite ou explicite. Selon la définition
générale d'une bibliothèque, quand on spécifie une bibliothèque
lors de l'édition de liens, un élément de la bibliothèque fait
partie du programme si et seulement si il résoud au moins un
symbole externe qui ne serait pas résolu autrement.

Je pense qu'il me manque des connaissances sur la manière dont sont
liées les bibliotèques. Je pensais à tort qu'un .a n'était qu'un
ensemble de .o, et que g++ main.cpp data.o ou g++ main.cpp libdata.a
était équivalent (libdata.a contenant data.o)


Pas du tout. (Heureusement !) Si tu veux tout le contenu de la
bibliothèque, il faut le dire à l'éditeur de liens. Sous Unix,
et tes suffixes laissent penser que tu es sous Unix, la plus
simple, c'est de mettre dans ton fichier make quelque chose du
genre :

program : main.o libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
$(CC) ... $(TEMPDIR)/*.o ...
rm $(TEMPDIR)/*

Mais c'est brutal. À ta place, je chercherais d'autres
solutions, où tu précises bien ce que tu veux, et ce que tu ne
veux pas. Soit en définissant un symbole publique par source
(avec un nom dérivé du nom de la source, d'une façon connuee),
et forçant la présence de ce symbol comme non-défini d'une façon
à l'autre, soit en extrayant les fichiers objets qui
t'intéressent, et en passant leurs noms à l'éditeur de liens.
(Certains éditeurs de liens peuvent avoir des commandes pour
faire ceci automatiquement ; si je faisait un éditeur de liens
Unix, par exemple, je permettrais bien quelque chose du genre :
libx.a(a.o,b.o,c.o) comme nom de fichier, pour dire qu'il faut
inclure a.o, b.o et c.o de la bibliothèque libx.a. Mais j'avoue
ne pas savoir quels éditeurs de liens aujourd'hui supportent une
telle chose.)

En passant, une telle solution est optimale pour générer de
différentes versions de ton programme, quelque chose comme:

restricted : ... libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
objsForRestricted
$(CC) ... $(TEMPDIR)/*.o ... -o restricted
rm $(TEMPDIR)/*

licensed : ... libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
objsForLicensed
$(CC) ... $(TEMPDIR)/*.o ... -o restricted
rm $(TEMPDIR)/*

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

David Fleury
Le #304449
David Fleury wrote:

j'essaye de faire une Fabrique stockée dans une bibliothèque statique.
Dans cette même bibliothèque, je veux mettre des Fabriqués et
les enregistrés par une méthode du type :
bool Factory::Register( id, callback );

pour appeler directement, la méthode Register du singleton Factory,
j'utilise un namespace non nommé dans chacun des fichiers contenant
les Fabriqués.

class UnFabriqueConcret {
};

namespace {
bool regUnFab = Factory::Instance()::Register( FAB_1, Callback1 );
}


A priori, l'instance de Callback1 est une variable static aussi,
non ?


Non,
dans mes fichiers contenant les objets concrets, Callback1 est une
méthode statique pouvant être enregistrée dans la Factory.

L'idée générale vient du livre "Modern C++ Design".J'ai mis dans la
Factory (je fais ça de tête, ça ne compile sûrement
pas) :

// Factory.h
class Factory {
public:
typedef auto_ptr<AbstractObject> (*CreateCallback)();

bool Register( ID i, CreateCallback c ) {
return mapFactory.insert( make_pair( i, c ).second;
}
private:
std::map< ID, CreateCallback > mapFactory_;
};

// ConcreteObject1.h
class ConcreteObject1 {
};

// ConcreteObject1.cpp
#include "ConcreteObject1.h"
auto_ptr<AbstractObject> CreateConcreteObject1() {
return auto_ptr<AbstractObject>( new ConcreteObject1();}
}

namespace {
bool registerConcrete1 Factory::Instance().Register(ID_CONCRETE1,
CreateContreteObject1 );
}

Et la classe ne sert à rien d'autre. Alors, pourquoi pas
faire la régistration dans son constructeur ? (L'idiome
classique, d'ailleur, fait la régistration dans le constructeur
de la classe de base : Factory::Factory.)


Je crois que je me suis mal exprimé.
La "registration" se fait à l'aide d'une initialisation d'un bool
global dans un "unnamed namespace" dans le fichier contenant
l'objet concret lui-même.
La class UnFabriqueConcret pourra par exemple être créée
(instancié) à partir de la lecture d'un fichier texte sans
que l'utilisateur de la librairie n'ait spécialement à connaitre
la nature exact de la classe concrète créée par la Fabrique.
Par exemple, il suffirait juste à savoir qu'elle possède une méthode
Afficher() ( définie en virtuel (pure) dans la class AbstractObject.


Méfie-toi des problèmes de l'ordre de l'initialisation ; il
faut en général que la régistrie soit gérée comme un singleton.


C'est fait.
J'ai utilisé la méthode que tu as posté dernièrement.


Mon problème, est que la fonction qui devrait être appelée lors
de l'initialisation de la variable regUnFab, n'est jamais appelé
quand je le fais à partir d'un fichier contenu dans la bibliothèque
statique.


Est-ce que tu inclus le fichier dans ton programme ?


Non, j'ai fais les deux essais. en incluant directement le .cpp
dans lors du link (et j'ai le comportement attendu - appel
à la "registration" de l'objet concrete dans la Factory ),
et l'essai avec une bibliothèque externe (et il ne se passe rien
si je ne fais rien )

Mettre un
fichier objet dans une bibliothèque ne force pas son inclusion
dans le programme, même si la bibliothèque sert par ailleurs
dans le programme. (C'est la définition même d'une bibliothèque
en informatique.)


Malheureusement, ici, il y a un effet de bord qui est d'enregistrer
la callback de création d'un object concret dans mon singleton
Factory.


Si tu as des « modules » (unités de traduction, dans le
langage de la norme) que tu veux inclure inconditionnellement
dans le programme, il faut bien que tu le dises à l'éditeur de
liens. Enfin, dans tous les systèmes que je connais. Il faut de
toute façon que tu spécifies quelque part ce qui fait partie du
programme, de façon implicite ou explicite. Selon la définition
générale d'une bibliothèque, quand on spécifie une bibliothèque
lors de l'édition de liens, un élément de la bibliothèque fait
partie du programme si et seulement si il résoud au moins un
symbole externe qui ne serait pas résolu autrement.


Ok. La condition "si il résoud au moins" me manquait alors.
Je pensais que strip permettait justement de supprimer les symboles
non utilisés des binaires.


Je pense qu'il me manque des connaissances sur la manière dont sont
liées les bibliotèques. Je pensais à tort qu'un .a n'était qu'un
ensemble de .o, et que g++ main.cpp data.o ou g++ main.cpp libdata.a
était équivalent (libdata.a contenant data.o)


Pas du tout. (Heureusement !) Si tu veux tout le contenu de la
bibliothèque, il faut le dire à l'éditeur de liens. Sous Unix,


Je suis plus à l'aise sous Unix mais j'ai le même symptome
sous Windows. Je verrais plus tard pour le régler.

et tes suffixes laissent penser que tu es sous Unix, la plus
simple, c'est de mettre dans ton fichier make quelque chose du
genre :

program : main.o libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
$(CC) ... $(TEMPDIR)/*.o ...
rm $(TEMPDIR)/*

Mais c'est brutal.


En gros, redécouper la libraire .a en .o et les linker directement
lors de l'édition de lien, si j'ai bien tout suivi.
Peut être un peu lourd en effet, et surtout, dur à transposer sous
Windows (où j'ai le même problème)

À ta place, je chercherais d'autres
solutions, où tu précises bien ce que tu veux, et ce que tu ne
veux pas. Soit en définissant un symbole publique par source
(avec un nom dérivé du nom de la source, d'une façon connue),
et forçant la présence de ce symbol comme non-défini d'une façon
à l'autre, soit en extrayant les fichiers objets qui
t'intéressent, et en passant leurs noms à l'éditeur de liens.


J'avais pensé à une constante globale dans un include pour forcer
la présence du symbole. Même si j'espère par la suite ne pas avoir
de conflit de nom.

Pour un exemple similaire, et sous windows et une DLL, ça a l'air de
fonctionner en ajoutant dans un .h quelque chose comme:

namespace MaLibFactory {
auto_ptr<AbstractObject> CreateConcreteObject1();

const bool registerConcrete1 Factory::Instance().Register(ID_CONCRETE1,
CreateContreteObject1 );
}

Ensuite, l'idée est d'avoir à toucher le moins de chose possible
pour l'ajout d'un nouveau composant concret dans la bibliothèque.

(Certains éditeurs de liens peuvent avoir des commandes pour
faire ceci automatiquement ; si je faisait un éditeur de liens
Unix, par exemple, je permettrais bien quelque chose du genre :
libx.a(a.o,b.o,c.o) comme nom de fichier, pour dire qu'il faut
inclure a.o, b.o et c.o de la bibliothèque libx.a. Mais j'avoue
ne pas savoir quels éditeurs de liens aujourd'hui supportent une
telle chose.)

En passant, une telle solution est optimale pour générer de
différentes versions de ton programme, quelque chose comme:

restricted : ... libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
objsForRestricted
$(CC) ... $(TEMPDIR)/*.o ... -o restricted
rm $(TEMPDIR)/*

licensed : ... libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
objsForLicensed
$(CC) ... $(TEMPDIR)/*.o ... -o restricted
rm $(TEMPDIR)/*


Ok, je mets ça de côté.
L'idée est bien là mais j'aurais aimé que cela se fasse de
manière plus transparente pour la compilation
et surtout pour un client potentiel de la bibliothèque).

Je crois me souvenir que j'utilisais une bibliothèque qui
forçait l'utilisateur de leur bibliothèque à inclure
tous les fichiers .h contenant les classes concrètes à enregistrer
dans leur factory pour permettre de les utiliser.

J'aurais aimé éviter ça.

D'autant plus que de mon côté (le .a), je préférais avoir
l'enregistrement du composant dans la Factory au plus proche de la
déclaration du composant concret (le même .cpp était l'idéal).

David


James Kanze
Le #304448
On Mar 17, 1:17 pm, David Fleury
David Fleury wrote:

j'essaye de faire une Fabrique stockée dans une bibliothèque stati que.
Dans cette même bibliothèque, je veux mettre des Fabriqués et
les enregistrés par une méthode du type :
bool Factory::Register( id, callback );

pour appeler directement, la méthode Register du singleton Factory,
j'utilise un namespace non nommé dans chacun des fichiers contenant
les Fabriqués.

class UnFabriqueConcret {
};

namespace {
bool regUnFab = Factory::Instance()::Register( FAB_1, Callback1 );
}


A priori, l'instance de Callback1 est une variable static aussi,
non ?


Non,
dans mes fichiers contenant les objets concrets, Callback1 est une
méthode statique pouvant être enregistrée dans la Factory.


Pourquoi la classe, alors ?

L'idée générale vient du livre "Modern C++ Design".J'ai mis dans la
Factory (je fais ça de tête, ça ne compile sûrement
pas) :

// Factory.h
class Factory {
public:
typedef auto_ptr<AbstractObject> (*CreateCallback)();

bool Register( ID i, CreateCallback c ) {
return mapFactory.insert( make_pair( i, c ).second;}

private:
std::map< ID, CreateCallback > mapFactory_;
};

// ConcreteObject1.h
class ConcreteObject1 {

};

// ConcreteObject1.cpp
#include "ConcreteObject1.h"
auto_ptr<AbstractObject> CreateConcreteObject1() {
return auto_ptr<AbstractObject>( new ConcreteObject1();}
}

namespace {
bool registerConcrete1 =
Factory::Instance().Register(ID_CONCRETE1,
CreateContreteObject1 );
}


Entendu. C'est un autre variant du modèle.

Et la classe ne sert à rien d'autre. Alors, pourquoi pas
faire la régistration dans son constructeur ? (L'idiome
classique, d'ailleur, fait la régistration dans le constructeur
de la classe de base : Factory::Factory.)


Je crois que je me suis mal exprimé.


Pas vraiment. J'ai bien compris ce que tu faisais (sauf que
j'imaginais que tu inscrivais des objets fonctionnels, plutôt
que les fonctions même). Je suggérais simplement un alternatif
que j'ai trouvé plus souple dans la passée.

La "registration" se fait à l'aide d'une initialisation d'un bool
global dans un "unnamed namespace" dans le fichier contenant
l'objet concret lui-même.
La class UnFabriqueConcret pourra par exemple être créée
(instancié) à partir de la lecture d'un fichier texte sans
que l'utilisateur de la librairie n'ait spécialement à connaitre
la nature exact de la classe concrète créée par la Fabrique.
Par exemple, il suffirait juste à savoir qu'elle possède une méthode
Afficher() ( définie en virtuel (pure) dans la class AbstractObject.


Tout à fait. Comme j'ai dit, c'est un idiome bien connu. (C'est
un variant du Factory Method dans Design Patterns, mais je m'en
servais bien avant.)

Méfie-toi des problèmes de l'ordre de l'initialisation ; il
faut en général que la régistrie soit gérée comme un singleto n.


C'est fait.
J'ai utilisé la méthode que tu as posté dernièrement.

Mon problème, est que la fonction qui devrait être appelée lors
de l'initialisation de la variable regUnFab, n'est jamais appelé
quand je le fais à partir d'un fichier contenu dans la bibliothèque
statique.


Est-ce que tu inclus le fichier dans ton programme ?


Non, j'ai fais les deux essais. en incluant directement le .cpp
dans lors du link (et j'ai le comportement attendu - appel
à la "registration" de l'objet concrete dans la Factory ),
et l'essai avec une bibliothèque externe (et il ne se passe rien
si je ne fais rien)

Mettre un
fichier objet dans une bibliothèque ne force pas son inclusion
dans le programme, même si la bibliothèque sert par ailleurs
dans le programme. (C'est la définition même d'une bibliothèque
en informatique.)


Malheureusement, ici, il y a un effet de bord qui est d'enregistrer
la callback de création d'un object concret dans mon singleton
Factory.


Je ne suis pas sûr de suivre. Évidemment qu'il y a un « effet
de bord ». Le comportement de ton programme dépend bien de ce
qui en fait partie. Et ce n'est pas « malheureusement » ;
c'est le comportement voulu, le comportement pour lequel les
bibliothèques ont été conçues. Tu as dit au système de n'y
incorpore que des fichiers objets qui résoudent un externe non
résolut. C'est ce qu'il a fait.

Si tu as des « modules » (unités de traduction, dans le
langage de la norme) que tu veux inclure inconditionnellement
dans le programme, il faut bien que tu le dises à l'éditeur de
liens. Enfin, dans tous les systèmes que je connais. Il faut de
toute façon que tu spécifies quelque part ce qui fait partie du
programme, de façon implicite ou explicite. Selon la définition
générale d'une bibliothèque, quand on spécifie une bibliothèq ue
lors de l'édition de liens, un élément de la bibliothèque fait
partie du programme si et seulement si il résoud au moins un
symbole externe qui ne serait pas résolu autrement.


Ok. La condition "si il résoud au moins" me manquait alors.
Je pensais que strip permettait justement de supprimer les symboles
non utilisés des binaires.


Les symboles. Non le code. Le but d'utiliser une bibliothèque
(originalement), c'est de pouvoir n'incorporer que ce qui sert
réelement, sans avoir à tout lister explicitement. Le problème
ici, c'est qu'on a du code qui ne sert pas, sauf s'il est
réelement incorporé dans le programme. Chose que l'éditeur de
lien ne saurait reconnaître. D'un certain côté, une bibliothèque
n'est pas l'outil qui convient.

Je pense qu'il me manque des connaissances sur la manière dont sont
liées les bibliotèques. Je pensais à tort qu'un .a n'était qu' un
ensemble de .o, et que g++ main.cpp data.o ou g++ main.cpp libdata.a
était équivalent (libdata.a contenant data.o)


Pas du tout. (Heureusement !) Si tu veux tout le contenu de la
bibliothèque, il faut le dire à l'éditeur de liens. Sous Unix,


Je suis plus à l'aise sous Unix mais j'ai le même symptome
sous Windows. Je verrais plus tard pour le régler.


Les comportements des deux s'y rapprochent. De toute façon,
Windows ne change pas la signification de ce que c'est qu'une
bibliothèque, même s'il y a des différences dans la façon de les
traiter.

et tes suffixes laissent penser que tu es sous Unix, la plus
simple, c'est de mettre dans ton fichier make quelque chose du
genre :

program : main.o libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
$(CC) ... $(TEMPDIR)/*.o ...
rm $(TEMPDIR)/*

Mais c'est brutal.


En gros, redécouper la libraire .a en .o et les linker directement
lors de l'édition de lien, si j'ai bien tout suivi.


Tout à fait. Vu que ce que tu veux, ce n'est pas linker une
bibliothèque, mais linker des fichiers objet, explicitement.

Peut être un peu lourd en effet, et surtout, dur à transposer sous
Windows (où j'ai le même problème)


Ça dépend. Ça marche sous Windows chez moi:-). Il suffit
d'installer MSys, UWin ou CygWin (ou d'autres outils, payant).
J'ai des choses bien plus complexe dans mes fichiers make, et
j'arrive à les faire marcher sous Windows. Mais ça exige
l'installation des outils qui conviennent.

Une question, cependant : si tu ne veux pas traiter les
fichiers comme une bibliothèque, pourquoi les mettre dans une
bibliothèque ? Tu les génères d'abord comme des fichiers
objets. Il suffit de les laisser come ça, et ça marche.

À ta place, je chercherais d'autres
solutions, où tu précises bien ce que tu veux, et ce que tu ne
veux pas. Soit en définissant un symbole publique par source
(avec un nom dérivé du nom de la source, d'une façon connue),
et forçant la présence de ce symbol comme non-défini d'une façon
à l'autre, soit en extrayant les fichiers objets qui
t'intéressent, et en passant leurs noms à l'éditeur de liens.


J'avais pensé à une constante globale dans un include pour forcer
la présence du symbole. Même si j'espère par la suite ne pas avoir
de conflit de nom.

Pour un exemple similaire, et sous windows et une DLL,


Attention : sous Windows, malgré le nom, une DLL n'est pas une
bibliothèque ; elle se comporte exactement comme un objet, du
point de vue de l'édition de liens. C'est pareil, d'ailleurs,
sous Unix, avec des .so ; tu mets tes fichiers objet dans un
.so, tu le linkes en appelant dlopen, et les constructeurs des
objets statiques seront tous appelés.

ça a l'air de
fonctionner en ajoutant dans un .h quelque chose comme:

namespace MaLibFactory {
auto_ptr<AbstractObject> CreateConcreteObject1();

const bool registerConcrete1 =
Factory::Instance().Register(ID_CONCRETE1,
CreateContreteObject1 );

}

Ensuite, l'idée est d'avoir à toucher le moins de chose possible
pour l'ajout d'un nouveau composant concret dans la bibliothèque.


Il n'y a même pas besoin d'autant. Tu appelles LoadLibrary() (je
crois qu'elle s'appelle sous Windows), et le tour est joué. Met
chaque objet avec sa Factory dans une .dll/.so à part, et tu
peux avoir un programme qui se configure à partir du contenu
d'un fichier de configuration. Ajoute une dérégistration dans le
déstructeur d'un objet statique (d'où l'intérêt des objets
fonctionnels, plutôt que des fonctions pûres), et tu peux
changer la configuration dynamiquement, voir faire une mise à
jour d'une fonction sans arrêter l'application. Il y a plus de
souplesse qu'il n'en faut.

(Certains éditeurs de liens peuvent avoir des commandes pour
faire ceci automatiquement ; si je faisait un éditeur de liens
Unix, par exemple, je permettrais bien quelque chose du genre :
libx.a(a.o,b.o,c.o) comme nom de fichier, pour dire qu'il faut
inclure a.o, b.o et c.o de la bibliothèque libx.a. Mais j'avoue
ne pas savoir quels éditeurs de liens aujourd'hui supportent une
telle chose.)

En passant, une telle solution est optimale pour générer de
différentes versions de ton programme, quelque chose comme:

restricted : ... libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
objsForRestricted
$(CC) ... $(TEMPDIR)/*.o ... -o restricted
rm $(TEMPDIR)/*

licensed : ... libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
objsForLicensed
$(CC) ... $(TEMPDIR)/*.o ... -o restricted
rm $(TEMPDIR)/*


Ok, je mets ça de côté.
L'idée est bien là mais j'aurais aimé que cela se fasse de
manière plus transparente pour la compilation
et surtout pour un client potentiel de la bibliothèque).


Plus transparente, mais moins souple, non ? Tu veux que le
client soit obligé à incorporer toutes les commandes, y compris
celle dont il n'a pas besoin.

Je crois que certains éditeurs de liens permet des éditions de
liens statiques partielles, c-à-d qu'à partir d'un certain
nombre de fichiers objet, on en crée un seul, mais qui est
toujours un fichier objet. Si j'ai bien compris, c'est ce que tu
veux. Malheureusement, la fonctionnalité n'est pas très
répandue. (De même, la plupart des d'éditeurs de liens ne savent
pas traiter une vraie archive, disons quelque chose comme un
fichier tar, en y incorporant tous les fichiers objet qu'il
contient.) Alors, ce que je te conseille, c'est :

1. Définir un symbole globale dans chaque fichier source, de
préférence dans un espace référentiel spécial, et avec un
nom qui dérive d'une façon simple du nom du fichier même.

2. Dans ton fichier de make, génère une source qui utilise tous
ces noms. Le plus simple ici, c'est de générer un tableau
avec les adresses, quelque chose du genre (en admettant que
le symbol ait chaque fois le type bool) :

namespace SpecialForLinking {
extern bool fichier1_cc ;
extern bool fichier2_cc ;
// ...

bool const* forceInclusion[] =
{
&fichier1_cc,
&fichier2_cc,
// ..
} ;
}

C'est extrèmement simple à générer quelque chose de ce genre
avec AWK. (Les utilisateurs n'ont pas à le faire, et toi,
évidemment, ça ne te gène pas à installer une version d'AWK
sur ton PC Windows.)

3. Dans une des sources que l'utilisateur sait, et va
référencer, réfère à SpecialForLinking::forceInclusion.

Comme ça, l'utilisateur ne voit que dalle, et même chez toi,
tout ce qu'il te faut maintenir, c'est la liste de fichiers
concernés.

Je crois me souvenir que j'utilisais une bibliothèque qui
forçait l'utilisateur de leur bibliothèque à inclure
tous les fichiers .h contenant les classes concrètes à enregistrer
dans leur factory pour permettre de les utiliser.

J'aurais aimé éviter ça.


Là, je te comprends tout à fait.

D'autant plus que de mon côté (le .a), je préférais avoir
l'enregistrement du composant dans la Factory au plus proche de la
déclaration du composant concret (le même .cpp était l'idéal).


Je crois la solution ci-dessus pourrait te faire l'affaire.

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



David Fleury
Le #304442
On Mar 17, 1:17 pm, David Fleury
David Fleury wrote:

j'essaye de faire une Fabrique stockée dans une bibliothèque statique.
Dans cette même bibliothèque, je veux mettre des Fabriqués et
les enregistrés par une méthode du type :
bool Factory::Register( id, callback );

pour appeler directement, la méthode Register du singleton Factory,
j'utilise un namespace non nommé dans chacun des fichiers contenant
les Fabriqués.

class UnFabriqueConcret {
};

namespace {
bool regUnFab = Factory::Instance()::Register( FAB_1, Callback1 );
}


A priori, l'instance de Callback1 est une variable static aussi,
non ?


Non,
dans mes fichiers contenant les objets concrets, Callback1 est une
méthode statique pouvant être enregistrée dans la Factory.


Pourquoi la classe, alors ?


On parle de quelle classe ? la classe UnFabriqueConcret ?
Elle était en fait une dérivée de AbstractObject.
Comme j'ai changé de nom entre chaque réponse ce n'est pas évident.

...

Et la classe ne sert à rien d'autre. Alors, pourquoi pas
faire la régistration dans son constructeur ? (L'idiome
classique, d'ailleur, fait la régistration dans le constructeur
de la classe de base : Factory::Factory.)


Je crois que je me suis mal exprimé.


Pas vraiment. J'ai bien compris ce que tu faisais (sauf que
j'imaginais que tu inscrivais des objets fonctionnels, plutôt
que les fonctions même). Je suggérais simplement un alternatif
que j'ai trouvé plus souple dans la passée.


Ok.

..

Mettre un
fichier objet dans une bibliothèque ne force pas son inclusion
dans le programme, même si la bibliothèque sert par ailleurs
dans le programme. (C'est la définition même d'une bibliothèque
en informatique.)




Je n'avais jamais rencontré de problème de non inclusion.
Mes bibliothèques étant utilisées de manière plus simple.
(des objets et fonctions qui sont utilisées dans le programme
appelant donc toujours inclus)


Malheureusement, ici, il y a un effet de bord qui est d'enregistrer
la callback de création d'un object concret dans mon singleton
Factory.


Je ne suis pas sûr de suivre. Évidemment qu'il y a un « effet
de bord ». Le comportement de ton programme dépend bien de ce
qui en fait partie. Et ce n'est pas « malheureusement » ;
c'est le comportement voulu, le comportement pour lequel les
bibliothèques ont été conçues. Tu as dit au système de n'y
incorpore que des fichiers objets qui résoudent un externe non
résolut. C'est ce qu'il a fait.


C'est le sens qui me manquait. j'aurais appris ça en plus.

Si tu as des « modules » (unités de traduction, dans le
langage de la norme) que tu veux inclure inconditionnellement
dans le programme, il faut bien que tu le dises à l'éditeur de
liens. Enfin, dans tous les systèmes que je connais. Il faut de
toute façon que tu spécifies quelque part ce qui fait partie du
programme, de façon implicite ou explicite. Selon la définition
générale d'une bibliothèque, quand on spécifie une bibliothèque
lors de l'édition de liens, un élément de la bibliothèque fait
partie du programme si et seulement si il résoud au moins un
symbole externe qui ne serait pas résolu autrement.


Ok. La condition "si il résoud au moins" me manquait alors.
Je pensais que strip permettait justement de supprimer les symboles
non utilisés des binaires.


Les symboles. Non le code. Le but d'utiliser une bibliothèque
(originalement), c'est de pouvoir n'incorporer que ce qui sert
réelement, sans avoir à tout lister explicitement. Le problème
ici, c'est qu'on a du code qui ne sert pas, sauf s'il est
réelement incorporé dans le programme. Chose que l'éditeur de
lien ne saurait reconnaître. D'un certain côté, une bibliothèque
n'est pas l'outil qui convient.


En effet.

Je pense qu'il me manque des connaissances sur la manière dont sont
liées les bibliotèques. Je pensais à tort qu'un .a n'était qu'un
ensemble de .o, et que g++ main.cpp data.o ou g++ main.cpp libdata.a
était équivalent (libdata.a contenant data.o)


Pas du tout. (Heureusement !) Si tu veux tout le contenu de la
bibliothèque, il faut le dire à l'éditeur de liens. Sous Unix,


Je suis plus à l'aise sous Unix mais j'ai le même symptome
sous Windows. Je verrais plus tard pour le régler.


Les comportements des deux s'y rapprochent. De toute façon,
Windows ne change pas la signification de ce que c'est qu'une
bibliothèque, même s'il y a des différences dans la façon de les
traiter.

et tes suffixes laissent penser que tu es sous Unix, la plus
simple, c'est de mettre dans ton fichier make quelque chose du
genre :

program : main.o libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
$(CC) ... $(TEMPDIR)/*.o ...
rm $(TEMPDIR)/*

Mais c'est brutal.


En gros, redécouper la libraire .a en .o et les linker directement
lors de l'édition de lien, si j'ai bien tout suivi.


Tout à fait. Vu que ce que tu veux, ce n'est pas linker une
bibliothèque, mais linker des fichiers objet, explicitement.


J'ai trouvé l'option whole-archive/no-whole-archive qui permet
de faire l'équivalent. Même si je ne compte pas l'utiliser je pense.


Peut être un peu lourd en effet, et surtout, dur à transposer sous
Windows (où j'ai le même problème)


Ça dépend. Ça marche sous Windows chez moi:-). Il suffit
d'installer MSys, UWin ou CygWin (ou d'autres outils, payant).
J'ai des choses bien plus complexe dans mes fichiers make, et
j'arrive à les faire marcher sous Windows. Mais ça exige
l'installation des outils qui conviennent.


J'essaye de m'adapter au milieu auquel est destiné la librairie.
Imposer un makefile à un windowsien, on va m'accuser de torture.
Mais en effet, j'ai réussi à faire tout ça sous cygwin.


Une question, cependant : si tu ne veux pas traiter les
fichiers comme une bibliothèque, pourquoi les mettre dans une
bibliothèque ? Tu les génères d'abord comme des fichiers
objets. Il suffit de les laisser comme ça, et ça marche.



L'idée étant de distribuer uniquement un .a et le .h associés.
L'utilisateur inclut disons un .h qui inclut tous les autres.
Il a ainsi des composants déjà enregistrés dans la Factory
qui sont normalement nécessaire et suffisant pour le fonctionnement de
la librairie et, libre à lui d'en ajouter par la suite.


À ta place, je chercherais d'autres
solutions, où tu précises bien ce que tu veux, et ce que tu ne
veux pas. Soit en définissant un symbole publique par source
(avec un nom dérivé du nom de la source, d'une façon connue),
et forçant la présence de ce symbol comme non-défini d'une façon
à l'autre, soit en extrayant les fichiers objets qui
t'intéressent, et en passant leurs noms à l'éditeur de liens.


J'avais pensé à une constante globale dans un include pour forcer
la présence du symbole. Même si j'espère par la suite ne pas avoir
de conflit de nom.

Pour un exemple similaire, et sous windows et une DLL,


Attention : sous Windows, malgré le nom, une DLL n'est pas une
bibliothèque ; elle se comporte exactement comme un objet, du
point de vue de l'édition de liens. C'est pareil, d'ailleurs,
sous Unix, avec des .so ; tu mets tes fichiers objet dans un
..so, tu le linkes en appelant dlopen, et les constructeurs des
objets statiques seront tous appelés.


en effet, j'ai l'effet voulu avec un dlopen dans le main.


ça a l'air de
fonctionner en ajoutant dans un .h quelque chose comme:

namespace MaLibFactory {
auto_ptr<AbstractObject> CreateConcreteObject1();

const bool registerConcrete1 >> Factory::Instance().Register(ID_CONCRETE1,
CreateContreteObject1 );

}

Ensuite, l'idée est d'avoir à toucher le moins de chose possible
pour l'ajout d'un nouveau composant concret dans la bibliothèque.


Il n'y a même pas besoin d'autant. Tu appelles LoadLibrary() (je
crois qu'elle s'appelle sous Windows), et le tour est joué. Met
chaque objet avec sa Factory dans une .dll/.so à part, et tu
peux avoir un programme qui se configure à partir du contenu
d'un fichier de configuration. Ajoute une dérégistration dans le
déstructeur d'un objet statique (d'où l'intérêt des objets
fonctionnels, plutôt que des fonctions pûres), et tu peux
changer la configuration dynamiquement, voir faire une mise à
jour d'une fonction sans arrêter l'application. Il y a plus de
souplesse qu'il n'en faut.


Disons que l'enregistrement dynamique de composant serait
une version luxe qui n'est pas la cible pour le moment.
Une évolution future est quelque chose comme ça.


(Certains éditeurs de liens peuvent avoir des commandes pour
faire ceci automatiquement ; si je faisait un éditeur de liens
Unix, par exemple, je permettrais bien quelque chose du genre :
libx.a(a.o,b.o,c.o) comme nom de fichier, pour dire qu'il faut
inclure a.o, b.o et c.o de la bibliothèque libx.a. Mais j'avoue
ne pas savoir quels éditeurs de liens aujourd'hui supportent une
telle chose.)




Ld le fait donc avec whole-archive mais ce ne me semble pas une
solution portable pereinee.


En passant, une telle solution est optimale pour générer de
différentes versions de ton programme, quelque chose comme:

restricted : ... libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
objsForRestricted
$(CC) ... $(TEMPDIR)/*.o ... -o restricted
rm $(TEMPDIR)/*

licensed : ... libx.a ...
ici=` pwd ` ; cd $(TEMPDIR) ; ar -xf - $$ici/libx.a
objsForLicensed
$(CC) ... $(TEMPDIR)/*.o ... -o restricted
rm $(TEMPDIR)/*


Ok, je mets ça de côté.
L'idée est bien là mais j'aurais aimé que cela se fasse de
manière plus transparente pour la compilation
et surtout pour un client potentiel de la bibliothèque).


Plus transparente, mais moins souple, non ? Tu veux que le
client soit obligé à incorporer toutes les commandes, y compris
celle dont il n'a pas besoin.


Le client ne sait pas à priori ce dont il a besoin.
Ces objets sont des composants nécessaires à la communication
avec un système externe dont l'utilisateur ne voit rien ou presque.



Je crois que certains éditeurs de liens permet des éditions de
liens statiques partielles, c-à-d qu'à partir d'un certain
nombre de fichiers objet, on en crée un seul, mais qui est
toujours un fichier objet. Si j'ai bien compris, c'est ce que tu
veux. Malheureusement, la fonctionnalité n'est pas très
répandue. (De même, la plupart des d'éditeurs de liens ne savent
pas traiter une vraie archive, disons quelque chose comme un
fichier tar, en y incorporant tous les fichiers objet qu'il
contient.) Alors, ce que je te conseille, c'est :

1. Définir un symbole globale dans chaque fichier source, de
préférence dans un espace référentiel spécial, et avec un
nom qui dérive d'une façon simple du nom du fichier même.

2. Dans ton fichier de make, génère une source qui utilise tous
ces noms. Le plus simple ici, c'est de générer un tableau
avec les adresses, quelque chose du genre (en admettant que
le symbol ait chaque fois le type bool) :

namespace SpecialForLinking {
extern bool fichier1_cc ;
extern bool fichier2_cc ;
// ...

bool const* forceInclusion[] > {
&fichier1_cc,
&fichier2_cc,
// ..
} ;
}

C'est extrèmement simple à générer quelque chose de ce genre
avec AWK. (Les utilisateurs n'ont pas à le faire, et toi,
évidemment, ça ne te gène pas à installer une version d'AWK
sur ton PC Windows.)

3. Dans une des sources que l'utilisateur sait, et va
référencer, réfère à SpecialForLinking::forceInclusion.

Comme ça, l'utilisateur ne voit que dalle, et même chez toi,
tout ce qu'il te faut maintenir, c'est la liste de fichiers
concernés.

...
D'autant plus que de mon côté (le .a), je préférais avoir
l'enregistrement du composant dans la Factory au plus proche de la
déclaration du composant concret (le même .cpp était l'idéal).


Je crois la solution ci-dessus pourrait te faire l'affaire.



Oui, je crois que je vais faire quelque chose comme ça.
Bon, le awk je ne le sens pas trop. J'ai pas envie d'imposer ça
aux collègues.

Après quelques essais, il semble que quelque chose comme ça dans un .h
caché mais inclut par l'utilisateur fasse l'affaire.

// A meetre dans le fichier .h de l'AbstracComposant
#define REGISTER_COMPOSANT( Name, Callback )
const bool register_##Name = Callback();

// A mettre dans MonComposant.h
namespace Factory {
class MonComposant : public AbstractComposant {
...
};

bool FunctionCreation();

REGISTER_COMPOSANT( monComposant, Function );
}

Bon, l'avantage du awk est qu'il n'y a pas à gérer soi-même l'inclusion
du fichier MonComposant.h dans un fichier qui sera inclut par
l'utilisateur sans le savoir. Normalement, comme la liste des composants
concrets ne bouge pas souvent ça pourrait aller.

David.




James Kanze
Le #304405
On Mar 17, 10:49 pm, David Fleury
On Mar 17, 1:17 pm, David Fleury
David Fleury wrote:

j'essaye de faire une Fabrique stockée dans une bibliothèque sta tique.
Dans cette même bibliothèque, je veux mettre des Fabriqués et
les enregistrés par une méthode du type :
bool Factory::Register( id, callback );

pour appeler directement, la méthode Register du singleton Factory,
j'utilise un namespace non nommé dans chacun des fichiers contenant
les Fabriqués.

class UnFabriqueConcret {
};

namespace {
bool regUnFab = Factory::Instance()::Register( FAB_1, Callback1 );
}


A priori, l'instance de Callback1 est une variable static aussi,
non ?


Non,
dans mes fichiers contenant les objets concrets, Callback1 est une
méthode statique pouvant être enregistrée dans la Factory.


Pourquoi la classe, alors ?


On parle de quelle classe ?


J'ai perdu la contexte moi-même:-). Je ne sais plus ce que je
voulais dire. De toute façon, je crois bien comprendre ce que tu
essaies de faire. J'ai fait des choses semblables dans certains
programmes à moi (sauf dans dans mon cas, ce que j'inscrivais,
c'était toujours un objet fonctionnel, et non une fonction pûre
et simple).

Mets toi aux années 1960s. Quand même un mainframe, souvent,
avait moins de 1 Mo de mémoire, et que des ordinateurs plus
petit... (On dit que le premier compilateur C tournait sur une
machine avec seulement 12 Ko.) Si tu fais des programmes
mathématiques, tu veux bien disposer d'une bibliothèque avec
toutes les fonctions immaginables, même les plus exotiques. Mais
vue les contraints de mémoire, tu ne veux sûrement pas que ton
programme incorpore le code pour cosh() ou gamma() si tu
n'appelles ces fonctions nulle part ; tu n'as déjà pas assez de
mémoire pour tes tableaux, sans que du code non utilisé n'en
bouffe encore.

Aujourd'hui, évidemment, la situation est un peu moins critique,
mais quand même. Est-ce que tu as une idée de la taille totale
de certaines bibliothèques (surtout graphiques) ?

..

Mettre un
fichier objet dans une bibliothèque ne force pas son inclusion
dans le programme, même si la bibliothèque sert par ailleurs
dans le programme. (C'est la définition même d'une bibliothèque
en informatique.)




Je n'avais jamais rencontré de problème de non inclusion.
Mes bibliothèques étant utilisées de manière plus simple.
(des objets et fonctions qui sont utilisées dans le programme
appelant donc toujours inclus)


Certes. Il faut se rappeller que la notion de bibliothèque est
bien plus ancienne que toutes ces techniques modernes. Et
qu'elle répond très bien au problème pour lequel elle a été
conçue.

Le problème, c'est que les outils n'ont pas vraiment évolués
beaucoup depuis. Pour de nombreux raisons, on ne veut pas
changer le comportement de base d'une bibliothèque ; il reste
essentiel même aujourd'hui. Mais ajouter des comportements
supplémentaires ne serait pas une mauvaise idée. Soit avec des
options qui se trouvent soit dans la bibliothèque, soit dans un
des fichiers membre de la bilbliothèque, soit en introduisant
d'autres types d'ensembles de fichiers. (Je verais bien un
éditeur de liens qui accepte un fichier d'archive, type tar, par
exemple, en incorporant tout ce qu'il contient, des objets comme
des objets, et des bibliothèques comme des bibliothèques. Et
qu'on pourrait aussi spécifier un tel fichier dans le chemin des
includes pour le compilateur, de façon à ce que tu mettes les
includes, les objets et les bibliothèques tous ensemble, et ne
livres qu'un seul fichier, qui n'a même pas besoin d'être
deballé.)

Malheureusement, ici, il y a un effet de bord qui est d'enregistrer
la callback de création d'un object concret dans mon singleton
Factory.


Je ne suis pas sûr de suivre. Évidemment qu'il y a un « effet
de bord ». Le comportement de ton programme dépend bien de ce
qui en fait partie. Et ce n'est pas « malheureusement » ;
c'est le comportement voulu, le comportement pour lequel les
bibliothèques ont été conçues. Tu as dit au système de n'y
incorpore que des fichiers objets qui résoudent un externe non
résolut. C'est ce qu'il a fait.


C'est le sens qui me manquait. j'aurais appris ça en plus.


Ce que tu as, en fait, c'est un cercle vicieux. L'objet ne
serait inclu que s'il sert. Mais il ne servira qu'une fois
inclu. Ton problème, c'est de briser le cercle quelque part.

[...]
En gros, redécouper la libraire .a en .o et les linker directement
lors de l'édition de lien, si j'ai bien tout suivi.


Tout à fait. Vu que ce que tu veux, ce n'est pas linker une
bibliothèque, mais linker des fichiers objet, explicitement.


J'ai trouvé l'option whole-archive/no-whole-archive qui permet
de faire l'équivalent. Même si je ne compte pas l'utiliser je pense.


En effet. Je doute que tu la retrouves partout.

Peut être un peu lourd en effet, et surtout, dur à transposer sous
Windows (où j'ai le même problème)


Ça dépend. Ça marche sous Windows chez moi:-). Il suffit
d'installer MSys, UWin ou CygWin (ou d'autres outils, payant).
J'ai des choses bien plus complexe dans mes fichiers make, et
j'arrive à les faire marcher sous Windows. Mais ça exige
l'installation des outils qui conviennent.


J'essaye de m'adapter au milieu auquel est destiné la librairie.
Imposer un makefile à un windowsien, on va m'accuser de torture.
Mais en effet, j'ai réussi à faire tout ça sous cygwin.


Je le comprends bien. Fais comme tout le monde : livre une
version source qui exige les outils pour générer la
bibliothèque, mais aussi une bibliothèque tout compilée pour
Windows (VC++, etc.). L'utilisateur beta sous Windows télécharge
le fichier zip précompiler, mais les .obj, les .lib et les .hpp
où il faut, et c'est tout. (La question reste : est-ce qu'on
peut dire dans le IDE qu'il faut linker tous les .obj dans un
répertoire donné ? Si oui, ça résoud ton problème. Sinon, il
faut que tu adoptes une autre stratégie que de livrer des .obj.)

Une question, cependant : si tu ne veux pas traiter les
fichiers comme une bibliothèque, pourquoi les mettre dans une
bibliothèque ? Tu les génères d'abord comme des fichiers
objets. Il suffit de les laisser comme ça, et ça marche.


L'idée étant de distribuer uniquement un .a et le .h associés.
L'utilisateur inclut disons un .h qui inclut tous les autres.
Il a ainsi des composants déjà enregistrés dans la Factory
qui sont normalement nécessaire et suffisant pour le fonctionnement de
la librairie et, libre à lui d'en ajouter par la suite.


Entendu. En somme, il y a une configuration par défaut pour
celui qui ne veut pas lire la doc:-).

[...]
(Certains éditeurs de liens peuvent avoir des commandes pour
faire ceci automatiquement ; si je faisait un éditeur de liens
Unix, par exemple, je permettrais bien quelque chose du genre :
libx.a(a.o,b.o,c.o) comme nom de fichier, pour dire qu'il faut
inclure a.o, b.o et c.o de la bibliothèque libx.a. Mais j'avoue
ne pas savoir quels éditeurs de liens aujourd'hui supportent une
telle chose.)




Ld le fait donc avec whole-archive mais ce ne me semble pas une
solution portable pereinee.


Pas du tout.

[...]
...
D'autant plus que de mon côté (le .a), je préférais avoir
l'enregistrement du composant dans la Factory au plus proche de la
déclaration du composant concret (le même .cpp était l'idéal).


Je crois la solution ci-dessus pourrait te faire l'affaire.


Oui, je crois que je vais faire quelque chose comme ça.
Bon, le awk je ne le sens pas trop. J'ai pas envie d'imposer ça
aux collègues.


J'ai dit AWK, parce que c'est ce que je connais. D'autres
préfère perl, ou python. C'est simplement qu'il existe des
langages qui sont conçue pour ce genre de chose, et qui sont
très facile à mettre en oeuvre.

Et note bien que tu ne l'impose pas aux utilisateurs de ta
bibliothèque.

Après quelques essais, il semble que quelque chose comme ça dans un .h
caché mais inclut par l'utilisateur fasse l'affaire.

// A meetre dans le fichier .h de l'AbstracComposant
#define REGISTER_COMPOSANT( Name, Callback )
const bool register_##Name = Callback();

// A mettre dans MonComposant.h
namespace Factory {
class MonComposant : public AbstractComposant {
...
};

bool FunctionCreation();

REGISTER_COMPOSANT( monComposant, Function );

}


Attention. Si c'est dans un .h, et que l'utilisateur l'inclut
dans plus d'une unité de compilation, tu auras une inscription
par unité de compilation. Et du code à exécuter lors du
démarrage dans chaque unité aussi -- ça peut être un problème
dans certains cas particulier.

Personnellement, je mettrais plutôt tout ça dans un fichier
source, dans un constructeur d'un objet statique, et avec un
seul symbol public que j'utilise (par exemple, en en prenant
l'adresse) dans un source qui serait réelement directement
utilisée par l'utilisateur.

Bon, l'avantage du awk est qu'il n'y a pas à gérer soi-même l'inclu sion
du fichier MonComposant.h dans un fichier qui sera inclut par
l'utilisateur sans le savoir.


L'avantage du awk, aussi, c'est de ne pas avoir besoin du
macro:-), puisqu'awk peut facilement générer le tout
directement.

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34





David Fleury
Le #304402
...
Pourquoi la classe, alors ?


On parle de quelle classe ?


J'ai perdu la contexte moi-même:-). Je ne sais plus ce que je
voulais dire. De toute façon, je crois bien comprendre ce que tu
essaies de faire. J'ai fait des choses semblables dans certains
programmes à moi (sauf dans dans mon cas, ce que j'inscrivais,
c'était toujours un objet fonctionnel, et non une fonction pûre
et simple).

Mets toi aux années 1960s. Quand même un mainframe, souvent,
avait moins de 1 Mo de mémoire, et que des ordinateurs plus
petit... (On dit que le premier compilateur C tournait sur une
machine avec seulement 12 Ko.) Si tu fais des programmes
mathématiques, tu veux bien disposer d'une bibliothèque avec
toutes les fonctions immaginables, même les plus exotiques. Mais
vue les contraints de mémoire, tu ne veux sûrement pas que ton
programme incorpore le code pour cosh() ou gamma() si tu
n'appelles ces fonctions nulle part ; tu n'as déjà pas assez de
mémoire pour tes tableaux, sans que du code non utilisé n'en
bouffe encore.

Aujourd'hui, évidemment, la situation est un peu moins critique,
mais quand même. Est-ce que tu as une idée de la taille totale
de certaines bibliothèques (surtout graphiques) ?



En effet, je n'avais pas vu cette angle là.

Je n'avais jamais rencontré de problème de non inclusion.
Mes bibliothèques étant utilisées de manière plus simple.
(des objets et fonctions qui sont utilisées dans le programme
appelant donc toujours inclus)


Certes. Il faut se rappeler que la notion de bibliothèque est
bien plus ancienne que toutes ces techniques modernes. Et
qu'elle répond très bien au problème pour lequel elle a été
conçue.

Le problème, c'est que les outils n'ont pas vraiment évolués
beaucoup depuis. Pour de nombreux raisons, on ne veut pas
changer le comportement de base d'une bibliothèque ; il reste
essentiel même aujourd'hui. Mais ajouter des comportements
supplémentaires ne serait pas une mauvaise idée. Soit avec des
options qui se trouvent soit dans la bibliothèque, soit dans un
des fichiers membre de la bibliothèque, soit en introduisant
d'autres types d'ensembles de fichiers. (Je verrais bien un
éditeur de liens qui accepte un fichier d'archive, type tar, par
exemple, en incorporant tout ce qu'il contient, des objets comme
des objets, et des bibliothèques comme des bibliothèques. Et
qu'on pourrait aussi spécifier un tel fichier dans le chemin des
includes pour le compilateur, de façon à ce que tu mettes les
includes, les objets et les bibliothèques tous ensemble, et ne
livres qu'un seul fichier, qui n'a même pas besoin d'être
déballé.)

...
Je ne suis pas sûr de suivre. Évidemment qu'il y a un « effet
de bord ». Le comportement de ton programme dépend bien de ce
qui en fait partie. Et ce n'est pas « malheureusement » ;
c'est le comportement voulu, le comportement pour lequel les
bibliothèques ont été conçues. Tu as dit au système de n'y
incorpore que des fichiers objets qui résoudent un externe non
résolut. C'est ce qu'il a fait.


C'est le sens qui me manquait. j'aurais appris ça en plus.


Ce que tu as, en fait, c'est un cercle vicieux. L'objet ne
serait inclut que s'il sert. Mais il ne servira qu'une fois
inclut. Ton problème, c'est de briser le cercle quelque part.


Oui.

[...]

J'essaye de m'adapter au milieu auquel est destiné la librairie.
Imposer un makefile à un windowsien, on va m'accuser de torture.
Mais en effet, j'ai réussi à faire tout ça sous cygwin.


Je le comprends bien. Fais comme tout le monde : livre une
version source qui exige les outils pour générer la
bibliothèque, mais aussi une bibliothèque tout compilée pour
Windows (VC++, etc.). L'utilisateur beta sous Windows télécharge
le fichier zip précompiler, mais les .obj, les .lib et les .hpp
où il faut, et c'est tout. (La question reste : est-ce qu'on
peut dire dans le IDE qu'il faut linker tous les .obj dans un
répertoire donné ? Si oui, ça résout ton problème. Sinon, il
faut que tu adoptes une autre stratégie que de livrer des .obj.)


Je vais essayer plusieurs solutions à voir la moins contraignante
pour l'utilisateur final. La partie développement s'effectuant
que de mon côté.


Une question, cependant : si tu ne veux pas traiter les
fichiers comme une bibliothèque, pourquoi les mettre dans une
bibliothèque ? Tu les génères d'abord comme des fichiers
objets. Il suffit de les laisser comme ça, et ça marche.


L'idée étant de distribuer uniquement un .a et le .h associés.
L'utilisateur inclut disons un .h qui inclut tous les autres.
Il a ainsi des composants déjà enregistrés dans la Factory
qui sont normalement nécessaire et suffisant pour le fonctionnement de
la librairie et, libre à lui d'en ajouter par la suite.


Entendu. En somme, il y a une configuration par défaut pour
celui qui ne veut pas lire la doc:-).

[...]
Oui, je crois que je vais faire quelque chose comme ça.
Bon, le awk je ne le sens pas trop. J'ai pas envie d'imposer ça
aux collègues.


J'ai dit AWK, parce que c'est ce que je connais. D'autres
préfère perl, ou python. C'est simplement qu'il existe des
langages qui sont conçue pour ce genre de chose, et qui sont
très facile à mettre en oeuvre.

Et note bien que tu ne l'impose pas aux utilisateurs de ta
bibliothèque.


Je vais chercher une solution qui fasse au mieux.
Je proposerai plusieurs choix et on verra ce qui sera choisi ensuite.

Après quelques essais, il semble que quelque chose comme ça dans un .h
caché mais inclut par l'utilisateur fasse l'affaire.

// A meetre dans le fichier .h de l'AbstracComposant
#define REGISTER_COMPOSANT( Name, Callback )
const bool register_##Name = Callback();

// A mettre dans MonComposant.h
namespace Factory {
class MonComposant : public AbstractComposant {
...
};

bool FunctionCreation();

REGISTER_COMPOSANT( monComposant, Function );

}


Attention. Si c'est dans un .h, et que l'utilisateur l'inclut
dans plus d'une unité de compilation, tu auras une inscription
par unité de compilation. Et du code à exécuter lors du
démarrage dans chaque unité aussi -- ça peut être un problème
dans certains cas particulier.



Ca c'est un point qui m'intéresse particulièrement.
Je pense d'avoir des problèmes de link sous windows en utilisant
une autre librairie liée à ce problème.

Sinon, tant que la Factory Singleton est disponible, le composant
n'y sera inscrit qu'une seule fois. ça me va.
Il faut que je regarde plus en détail se point.

Il semble que le problème sur les unités de compilation soit
plus complexe à appréhender que prévu. Je n'en avais vu que la partie
simple.

Personnellement, je mettrais plutôt tout ça dans un fichier
source, dans un constructeur d'un objet statique, et avec un
seul symbole public que j'utilise (par exemple, en en prenant
l'adresse) dans un source qui serait réellement directement
utilisée par l'utilisateur.



Ok,
je ferai attention à ça.
Cette méthode me paraît intéressante.

Bon, l'avantage du awk est qu'il n'y a pas à gérer soi-même l'inclusion
du fichier MonComposant.h dans un fichier qui sera inclut par
l'utilisateur sans le savoir.


L'avantage du awk, aussi, c'est de ne pas avoir besoin du
macro:-), puisqu'awk peut facilement générer le tout
directement.



awk (perl,...) ou macro ... mon coeur balance.

Merci pour ton aide et tes éclaircissement. Après tout ce temps à
travailler dans la routine, il semble que j'ai perdu certains
fondamentaux. Va falloir rattraper le retard.
Merci.

David.



Publicité
Poster une réponse
Anonyme