std::map<std::string, config_value<T>* > et une classe config_value
définie comme suit :
template <class T> class config_value
{
T value;
public:
config_value(T& t) : value(t) {}
}
Maintenant que j'ai bien relu le chapitre sur les templates, je me rend
compte de l'absurdité de ce système, ne tapez pas. Cependant je tiens
toujours à la simplicité d'utilisation décrite plus haut, mais je
n'arrive pas à trouver de système qui me permette de stocker dans une
std::map des types hétérogènes, ou qui au moins sont perçus comme
hétérogènes par les utilisateurs de la classe.
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre sur
une solution de repli moins pratique ?
std::map<std::string, config_value<T>* > et une classe config_value
définie comme suit :
template <class T> class config_value
{
T value;
public:
config_value(T& t) : value(t) {}
}
Maintenant que j'ai bien relu le chapitre sur les templates, je me rend
compte de l'absurdité de ce système, ne tapez pas. Cependant je tiens
toujours à la simplicité d'utilisation décrite plus haut, mais je
n'arrive pas à trouver de système qui me permette de stocker dans une
std::map des types hétérogènes, ou qui au moins sont perçus comme
hétérogènes par les utilisateurs de la classe.
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre sur
une solution de repli moins pratique ?
std::map<std::string, config_value<T>* > et une classe config_value
définie comme suit :
template <class T> class config_value
{
T value;
public:
config_value(T& t) : value(t) {}
}
Maintenant que j'ai bien relu le chapitre sur les templates, je me rend
compte de l'absurdité de ce système, ne tapez pas. Cependant je tiens
toujours à la simplicité d'utilisation décrite plus haut, mais je
n'arrive pas à trouver de système qui me permette de stocker dans une
std::map des types hétérogènes, ou qui au moins sont perçus comme
hétérogènes par les utilisateurs de la classe.
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre sur
une solution de repli moins pratique ?
Bonsoir,
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre sur
une solution de repli moins pratique ?
Merci,
Greg
Bonsoir,
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre sur
une solution de repli moins pratique ?
Merci,
Greg
Bonsoir,
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre sur
une solution de repli moins pratique ?
Merci,
Greg
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre sur
une solution de repli moins pratique ?
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre sur
une solution de repli moins pratique ?
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre sur
une solution de repli moins pratique ?
sous ce titre confus se cache un problème qui l'est tout autant pour
moi, et ma faible expérience du c++ (je ne pratique pas assez) fait
que je n'arrive pas à trouver le vocabulaire adéquat.
Mon problème est le suivant : je souhaite réaliser un ensemble de
classes qui vont me permettre de lire un fichier de configuration
(texte simple, avec une syntaxe classique variable=valeur) et de
typer
les variables rencontrées dans le fichier en fonction de certains
paramètres. Ces paramètres se trouveraient dans un second fichier
dont
les lignes seraient de la forme variable=type (avec type choisi dans
une liste fixe).
Dans le programme, le but est d'obtenir une syntaxe la plus simple
possible de style :
configuration* c = new configuration(fichier_config, fichier_params);
// exemple pour un entier quelconque, "nom_de_l_entier" est
// le nom de la variable dans le fichier de config
int entier_quelconque = c["nom_de_l_entier"];
Fort naïvement, et n'ayant pas bien compris les templates j'avais
commencé à créer une version de démo en définissant une
std::map<std::string, config_value<T>* > et une classe config_value
définie comme suit :
template <class T> class config_value
{
T value;
public:
config_value(T& t) : value(t) {}
}
Maintenant que j'ai bien relu le chapitre sur les templates, je me
rend compte de l'absurdité de ce système, ne tapez pas. Cependant
je
tiens toujours à la simplicité d'utilisation décrite plus haut,
mais
je n'arrive pas à trouver de système qui me permette de stocker
dans
une std::map des types hétérogènes, ou qui au moins sont perçus
comme
hétérogènes par les utilisateurs de la classe.
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre
sur une solution de repli moins pratique ?
sous ce titre confus se cache un problème qui l'est tout autant pour
moi, et ma faible expérience du c++ (je ne pratique pas assez) fait
que je n'arrive pas à trouver le vocabulaire adéquat.
Mon problème est le suivant : je souhaite réaliser un ensemble de
classes qui vont me permettre de lire un fichier de configuration
(texte simple, avec une syntaxe classique variable=valeur) et de
typer
les variables rencontrées dans le fichier en fonction de certains
paramètres. Ces paramètres se trouveraient dans un second fichier
dont
les lignes seraient de la forme variable=type (avec type choisi dans
une liste fixe).
Dans le programme, le but est d'obtenir une syntaxe la plus simple
possible de style :
configuration* c = new configuration(fichier_config, fichier_params);
// exemple pour un entier quelconque, "nom_de_l_entier" est
// le nom de la variable dans le fichier de config
int entier_quelconque = c["nom_de_l_entier"];
Fort naïvement, et n'ayant pas bien compris les templates j'avais
commencé à créer une version de démo en définissant une
std::map<std::string, config_value<T>* > et une classe config_value
définie comme suit :
template <class T> class config_value
{
T value;
public:
config_value(T& t) : value(t) {}
}
Maintenant que j'ai bien relu le chapitre sur les templates, je me
rend compte de l'absurdité de ce système, ne tapez pas. Cependant
je
tiens toujours à la simplicité d'utilisation décrite plus haut,
mais
je n'arrive pas à trouver de système qui me permette de stocker
dans
une std::map des types hétérogènes, ou qui au moins sont perçus
comme
hétérogènes par les utilisateurs de la classe.
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre
sur une solution de repli moins pratique ?
sous ce titre confus se cache un problème qui l'est tout autant pour
moi, et ma faible expérience du c++ (je ne pratique pas assez) fait
que je n'arrive pas à trouver le vocabulaire adéquat.
Mon problème est le suivant : je souhaite réaliser un ensemble de
classes qui vont me permettre de lire un fichier de configuration
(texte simple, avec une syntaxe classique variable=valeur) et de
typer
les variables rencontrées dans le fichier en fonction de certains
paramètres. Ces paramètres se trouveraient dans un second fichier
dont
les lignes seraient de la forme variable=type (avec type choisi dans
une liste fixe).
Dans le programme, le but est d'obtenir une syntaxe la plus simple
possible de style :
configuration* c = new configuration(fichier_config, fichier_params);
// exemple pour un entier quelconque, "nom_de_l_entier" est
// le nom de la variable dans le fichier de config
int entier_quelconque = c["nom_de_l_entier"];
Fort naïvement, et n'ayant pas bien compris les templates j'avais
commencé à créer une version de démo en définissant une
std::map<std::string, config_value<T>* > et une classe config_value
définie comme suit :
template <class T> class config_value
{
T value;
public:
config_value(T& t) : value(t) {}
}
Maintenant que j'ai bien relu le chapitre sur les templates, je me
rend compte de l'absurdité de ce système, ne tapez pas. Cependant
je
tiens toujours à la simplicité d'utilisation décrite plus haut,
mais
je n'arrive pas à trouver de système qui me permette de stocker
dans
une std::map des types hétérogènes, ou qui au moins sont perçus
comme
hétérogènes par les utilisateurs de la classe.
Bref, existe-t-il une solution à ce problème ou dois-je me rabattre
sur une solution de repli moins pratique ?
struct Value {
} ;
class Variables {
typedef std::map< std::string , Value * >
Map ;
public:
~Variables() {
for( Map::iterator it = myVariables.begin() ,
end = myVariables.end() ;
it != end ;
++ it ) {
delete it->second ;
}
}
void display( Variables const & vars ) {
int const * i = 0 ;
std::string const * s = 0 ;
vars.get( "integer" , i ) ;
vars.get( "string" , s ) ;
if ( i ) {
std::cout << "integer=" << * i << std::endl ;
}
else {
std::cout << "integer=<not set>" << std::endl ;
}
if ( s ) {
std::cout << "string=" << * s << std::endl ;
}
else {
std::cout << "string=<not set>" << std::endl ;
}
}
Ce n'est qu'un point de départ, il y a plusieurs variantes
possibles. Je dirais que c'est un exemple ou les Policy Classes
devraient être utile.
struct Value {
} ;
class Variables {
typedef std::map< std::string , Value * >
Map ;
public:
~Variables() {
for( Map::iterator it = myVariables.begin() ,
end = myVariables.end() ;
it != end ;
++ it ) {
delete it->second ;
}
}
void display( Variables const & vars ) {
int const * i = 0 ;
std::string const * s = 0 ;
vars.get( "integer" , i ) ;
vars.get( "string" , s ) ;
if ( i ) {
std::cout << "integer=" << * i << std::endl ;
}
else {
std::cout << "integer=<not set>" << std::endl ;
}
if ( s ) {
std::cout << "string=" << * s << std::endl ;
}
else {
std::cout << "string=<not set>" << std::endl ;
}
}
Ce n'est qu'un point de départ, il y a plusieurs variantes
possibles. Je dirais que c'est un exemple ou les Policy Classes
devraient être utile.
struct Value {
} ;
class Variables {
typedef std::map< std::string , Value * >
Map ;
public:
~Variables() {
for( Map::iterator it = myVariables.begin() ,
end = myVariables.end() ;
it != end ;
++ it ) {
delete it->second ;
}
}
void display( Variables const & vars ) {
int const * i = 0 ;
std::string const * s = 0 ;
vars.get( "integer" , i ) ;
vars.get( "string" , s ) ;
if ( i ) {
std::cout << "integer=" << * i << std::endl ;
}
else {
std::cout << "integer=<not set>" << std::endl ;
}
if ( s ) {
std::cout << "string=" << * s << std::endl ;
}
else {
std::cout << "string=<not set>" << std::endl ;
}
}
Ce n'est qu'un point de départ, il y a plusieurs variantes
possibles. Je dirais que c'est un exemple ou les Policy Classes
devraient être utile.
Mais je me pose la question : tu lis les
valeurs en tant que texte, pourquoi pas les stocker aussi sous ce
format, et ne convertir qu'en cas de besoin ? C-à-d grosso modo que ta
classe Configuration contiendrait un std::map<std::string,std::string>
tout bêtement, et que soit tu effectues les conversions dans des
fonctions du genre getInt, getString, etc., soit ta fonction get
renvoie un proxy avec un opérateur de conversion templatée.
J'ai fini par conclure qu'il faut
savoir non seulement le type, mais si le paramètre est obligatoire ou
non, ou sinon une valeur par défaut (qui manque dans le cas d'un
paramètre par défaut).
<nom> = <type> [ ':' <valeur> ]
Sans le ':', le paramètre est obligatoire dans le fichier de config.
Avec le ':', le paramètre a <valeur> (qui peut être vide) comme
valeur par défaut.
Mais je me pose la question : tu lis les
valeurs en tant que texte, pourquoi pas les stocker aussi sous ce
format, et ne convertir qu'en cas de besoin ? C-à-d grosso modo que ta
classe Configuration contiendrait un std::map<std::string,std::string>
tout bêtement, et que soit tu effectues les conversions dans des
fonctions du genre getInt, getString, etc., soit ta fonction get
renvoie un proxy avec un opérateur de conversion templatée.
J'ai fini par conclure qu'il faut
savoir non seulement le type, mais si le paramètre est obligatoire ou
non, ou sinon une valeur par défaut (qui manque dans le cas d'un
paramètre par défaut).
<nom> = <type> [ ':' <valeur> ]
Sans le ':', le paramètre est obligatoire dans le fichier de config.
Avec le ':', le paramètre a <valeur> (qui peut être vide) comme
valeur par défaut.
Mais je me pose la question : tu lis les
valeurs en tant que texte, pourquoi pas les stocker aussi sous ce
format, et ne convertir qu'en cas de besoin ? C-à-d grosso modo que ta
classe Configuration contiendrait un std::map<std::string,std::string>
tout bêtement, et que soit tu effectues les conversions dans des
fonctions du genre getInt, getString, etc., soit ta fonction get
renvoie un proxy avec un opérateur de conversion templatée.
J'ai fini par conclure qu'il faut
savoir non seulement le type, mais si le paramètre est obligatoire ou
non, ou sinon une valeur par défaut (qui manque dans le cas d'un
paramètre par défaut).
<nom> = <type> [ ':' <valeur> ]
Sans le ':', le paramètre est obligatoire dans le fichier de config.
Avec le ':', le paramètre a <valeur> (qui peut être vide) comme
valeur par défaut.
On 7 Dec 2004 00:27:27 -0800
wrote:Mais je me pose la question : tu lis les
valeurs en tant que texte, pourquoi pas les stocker aussi sous ce
format, et ne convertir qu'en cas de besoin ? C-à-d grosso modo
que ta
classe Configuration contiendrait un
std::map<std::string,std::string>
tout bêtement, et que soit tu effectues les conversions dans des
fonctions du genre getInt, getString, etc., soit ta fonction get
renvoie un proxy avec un opérateur de conversion templatée.
J'avais utilisé les stringstreams pour faire ce genre de choses dans
un petit bout de programme, est-ce que vous parlez bien de ça ?
Auquel
cas, je ne vois pas trop l'intérêt d'un proxy (ou alors je n'ai pas
compris ce qu'est un proxy).
J'ai fini par conclure qu'il faut savoir non seulement le type,
mais
si le paramètre est obligatoire ou non, ou sinon une valeur par
défaut (qui manque dans le cas d'un paramètre par défaut).
J'avais pensé coder les valeurs en dur dans l'appli, via une
interface
de la classe configuration, mais la syntaxe qui suit est
intéressante.
<nom> = <type> [ ':' <valeur> ]
Sans le ':', le paramètre est obligatoire dans le fichier de
config.
Avec le ':', le paramètre a <valeur> (qui peut être vide) comme
valeur par défaut.
J'avais déjà envisagé une syntaxe plus complexe à analyser, mais
plus
pratique à mon sens pour l'écriture des fichiers de config. Avec
vos
ajouts ça donnerait :
group [type] {
variable1:valeur1
variable2:valeur2
variable3:
}
où [type] est choisi dans les types intégrés de C++. Ça demande
plus
de boulot pour l'analyse du fichier, mais ça permet de jouer au
fainéant sur le long terme.
On 7 Dec 2004 00:27:27 -0800
kanze@gabi-soft.fr wrote:
Mais je me pose la question : tu lis les
valeurs en tant que texte, pourquoi pas les stocker aussi sous ce
format, et ne convertir qu'en cas de besoin ? C-à-d grosso modo
que ta
classe Configuration contiendrait un
std::map<std::string,std::string>
tout bêtement, et que soit tu effectues les conversions dans des
fonctions du genre getInt, getString, etc., soit ta fonction get
renvoie un proxy avec un opérateur de conversion templatée.
J'avais utilisé les stringstreams pour faire ce genre de choses dans
un petit bout de programme, est-ce que vous parlez bien de ça ?
Auquel
cas, je ne vois pas trop l'intérêt d'un proxy (ou alors je n'ai pas
compris ce qu'est un proxy).
J'ai fini par conclure qu'il faut savoir non seulement le type,
mais
si le paramètre est obligatoire ou non, ou sinon une valeur par
défaut (qui manque dans le cas d'un paramètre par défaut).
J'avais pensé coder les valeurs en dur dans l'appli, via une
interface
de la classe configuration, mais la syntaxe qui suit est
intéressante.
<nom> = <type> [ ':' <valeur> ]
Sans le ':', le paramètre est obligatoire dans le fichier de
config.
Avec le ':', le paramètre a <valeur> (qui peut être vide) comme
valeur par défaut.
J'avais déjà envisagé une syntaxe plus complexe à analyser, mais
plus
pratique à mon sens pour l'écriture des fichiers de config. Avec
vos
ajouts ça donnerait :
group [type] {
variable1:valeur1
variable2:valeur2
variable3:
}
où [type] est choisi dans les types intégrés de C++. Ça demande
plus
de boulot pour l'analyse du fichier, mais ça permet de jouer au
fainéant sur le long terme.
On 7 Dec 2004 00:27:27 -0800
wrote:Mais je me pose la question : tu lis les
valeurs en tant que texte, pourquoi pas les stocker aussi sous ce
format, et ne convertir qu'en cas de besoin ? C-à-d grosso modo
que ta
classe Configuration contiendrait un
std::map<std::string,std::string>
tout bêtement, et que soit tu effectues les conversions dans des
fonctions du genre getInt, getString, etc., soit ta fonction get
renvoie un proxy avec un opérateur de conversion templatée.
J'avais utilisé les stringstreams pour faire ce genre de choses dans
un petit bout de programme, est-ce que vous parlez bien de ça ?
Auquel
cas, je ne vois pas trop l'intérêt d'un proxy (ou alors je n'ai pas
compris ce qu'est un proxy).
J'ai fini par conclure qu'il faut savoir non seulement le type,
mais
si le paramètre est obligatoire ou non, ou sinon une valeur par
défaut (qui manque dans le cas d'un paramètre par défaut).
J'avais pensé coder les valeurs en dur dans l'appli, via une
interface
de la classe configuration, mais la syntaxe qui suit est
intéressante.
<nom> = <type> [ ':' <valeur> ]
Sans le ':', le paramètre est obligatoire dans le fichier de
config.
Avec le ':', le paramètre a <valeur> (qui peut être vide) comme
valeur par défaut.
J'avais déjà envisagé une syntaxe plus complexe à analyser, mais
plus
pratique à mon sens pour l'écriture des fichiers de config. Avec
vos
ajouts ça donnerait :
group [type] {
variable1:valeur1
variable2:valeur2
variable3:
}
où [type] est choisi dans les types intégrés de C++. Ça demande
plus
de boulot pour l'analyse du fichier, mais ça permet de jouer au
fainéant sur le long terme.
Alternativement, il faut que l'utilisateur spécifie le type dans le
nom
de la fonction, par exemple :
std::string opt1 = Configuration::instance().get< std::string >(
"opt1" ) ;
int opt2 = Configuration::instance().get< int >( "opt2" ) ;
// etc.
Alternativement, il faut que l'utilisateur spécifie le type dans le
nom
de la fonction, par exemple :
std::string opt1 = Configuration::instance().get< std::string >(
"opt1" ) ;
int opt2 = Configuration::instance().get< int >( "opt2" ) ;
// etc.
Alternativement, il faut que l'utilisateur spécifie le type dans le
nom
de la fonction, par exemple :
std::string opt1 = Configuration::instance().get< std::string >(
"opt1" ) ;
int opt2 = Configuration::instance().get< int >( "opt2" ) ;
// etc.
On Tue, 07 Dec 2004 01:42:47 +0100
drkm wrote:struct Value {
} ;
Il ne faut pas définir ici un destructeur virtuel vu que cette classe
est appelée à n'être qu'une classe de base ?
class Variables {
typedef std::map< std::string , Value * >
Map ;
public:
~Variables() {
for( Map::iterator it = myVariables.begin() ,
end = myVariables.end() ;
it != end ;
++ it ) {
delete it->second ;
}
}
Pourquoi appeler les destructeurs de chaque élément de la std::map ? Son
destructeur ne le fait pas ?
[snip de la fin de la classe Variables]void display( Variables const & vars ) {
int const * i = 0 ;
std::string const * s = 0 ;
vars.get( "integer" , i ) ;
vars.get( "string" , s ) ;
if ( i ) {
std::cout << "integer=" << * i << std::endl ;
}
else {
std::cout << "integer=<not set>" << std::endl ;
}
if ( s ) {
std::cout << "string=" << * s << std::endl ;
}
else {
std::cout << "string=<not set>" << std::endl ;
}
}
Le souci c'est qu'avec cette fonction il faut prévoir un cas par type
intégré ou des conversions implicites sont-elles réalisées ?
Ce n'est qu'un point de départ, il y a plusieurs variantes
possibles. Je dirais que c'est un exemple ou les Policy Classes
devraient être utile.
Je vais googler un coup sur "policy classes", merci pour le code. Voilà
ce que j'ai laborieusement pondu :
--8<-------------------------------------------------------------------
#include <iostream>
#include <map>
class untyped_value
{
public:
virtual ~untyped_value() {}
};
template<class T>
class typed_value : public untyped_value
{
public:
typed_value(const T& t) : value(t) {}
T& get_val() { return value; }
private:
T value;
};
class config_value
{
public:
config_value() : conf(0) {}
~config_value() { delete conf; }
template<class T> config_value(const T& t)
{
conf = new typed_value<T>(t);
}
template<class T> void operator=(const T& t)
{
if(conf) delete conf;
conf = new typed_value<T>(t);
}
template<class T> operator T()
{
return dynamic_cast<typed_value<T>* >(conf)->get_val();
}
private:
untyped_value* conf;
};
--8<-------------------------------------------------------------------
En lisant les autres mails qui m'ont dirigé vers boost::any, je me rends
compte que j'ai presque réussi à réinventer la roue, mais avec des
soucis pour la surcharge de "operator<<" (fonction non présente dans ce
code). Si je la déclare comme suit en tant que membre de la classe
config_value :
template<class T> std::ostream& operator(std::ostream& s);
la fonction ne semble pas prise en compte lors de la résolution de
surcharge. Y a-t-il une raison à ça ?
On Tue, 07 Dec 2004 01:42:47 +0100
drkm <usenet.fclcxx@fgeorges.org> wrote:
struct Value {
} ;
Il ne faut pas définir ici un destructeur virtuel vu que cette classe
est appelée à n'être qu'une classe de base ?
class Variables {
typedef std::map< std::string , Value * >
Map ;
public:
~Variables() {
for( Map::iterator it = myVariables.begin() ,
end = myVariables.end() ;
it != end ;
++ it ) {
delete it->second ;
}
}
Pourquoi appeler les destructeurs de chaque élément de la std::map ? Son
destructeur ne le fait pas ?
[snip de la fin de la classe Variables]
void display( Variables const & vars ) {
int const * i = 0 ;
std::string const * s = 0 ;
vars.get( "integer" , i ) ;
vars.get( "string" , s ) ;
if ( i ) {
std::cout << "integer=" << * i << std::endl ;
}
else {
std::cout << "integer=<not set>" << std::endl ;
}
if ( s ) {
std::cout << "string=" << * s << std::endl ;
}
else {
std::cout << "string=<not set>" << std::endl ;
}
}
Le souci c'est qu'avec cette fonction il faut prévoir un cas par type
intégré ou des conversions implicites sont-elles réalisées ?
Ce n'est qu'un point de départ, il y a plusieurs variantes
possibles. Je dirais que c'est un exemple ou les Policy Classes
devraient être utile.
Je vais googler un coup sur "policy classes", merci pour le code. Voilà
ce que j'ai laborieusement pondu :
--8<-------------------------------------------------------------------
#include <iostream>
#include <map>
class untyped_value
{
public:
virtual ~untyped_value() {}
};
template<class T>
class typed_value : public untyped_value
{
public:
typed_value(const T& t) : value(t) {}
T& get_val() { return value; }
private:
T value;
};
class config_value
{
public:
config_value() : conf(0) {}
~config_value() { delete conf; }
template<class T> config_value(const T& t)
{
conf = new typed_value<T>(t);
}
template<class T> void operator=(const T& t)
{
if(conf) delete conf;
conf = new typed_value<T>(t);
}
template<class T> operator T()
{
return dynamic_cast<typed_value<T>* >(conf)->get_val();
}
private:
untyped_value* conf;
};
--8<-------------------------------------------------------------------
En lisant les autres mails qui m'ont dirigé vers boost::any, je me rends
compte que j'ai presque réussi à réinventer la roue, mais avec des
soucis pour la surcharge de "operator<<" (fonction non présente dans ce
code). Si je la déclare comme suit en tant que membre de la classe
config_value :
template<class T> std::ostream& operator(std::ostream& s);
la fonction ne semble pas prise en compte lors de la résolution de
surcharge. Y a-t-il une raison à ça ?
On Tue, 07 Dec 2004 01:42:47 +0100
drkm wrote:struct Value {
} ;
Il ne faut pas définir ici un destructeur virtuel vu que cette classe
est appelée à n'être qu'une classe de base ?
class Variables {
typedef std::map< std::string , Value * >
Map ;
public:
~Variables() {
for( Map::iterator it = myVariables.begin() ,
end = myVariables.end() ;
it != end ;
++ it ) {
delete it->second ;
}
}
Pourquoi appeler les destructeurs de chaque élément de la std::map ? Son
destructeur ne le fait pas ?
[snip de la fin de la classe Variables]void display( Variables const & vars ) {
int const * i = 0 ;
std::string const * s = 0 ;
vars.get( "integer" , i ) ;
vars.get( "string" , s ) ;
if ( i ) {
std::cout << "integer=" << * i << std::endl ;
}
else {
std::cout << "integer=<not set>" << std::endl ;
}
if ( s ) {
std::cout << "string=" << * s << std::endl ;
}
else {
std::cout << "string=<not set>" << std::endl ;
}
}
Le souci c'est qu'avec cette fonction il faut prévoir un cas par type
intégré ou des conversions implicites sont-elles réalisées ?
Ce n'est qu'un point de départ, il y a plusieurs variantes
possibles. Je dirais que c'est un exemple ou les Policy Classes
devraient être utile.
Je vais googler un coup sur "policy classes", merci pour le code. Voilà
ce que j'ai laborieusement pondu :
--8<-------------------------------------------------------------------
#include <iostream>
#include <map>
class untyped_value
{
public:
virtual ~untyped_value() {}
};
template<class T>
class typed_value : public untyped_value
{
public:
typed_value(const T& t) : value(t) {}
T& get_val() { return value; }
private:
T value;
};
class config_value
{
public:
config_value() : conf(0) {}
~config_value() { delete conf; }
template<class T> config_value(const T& t)
{
conf = new typed_value<T>(t);
}
template<class T> void operator=(const T& t)
{
if(conf) delete conf;
conf = new typed_value<T>(t);
}
template<class T> operator T()
{
return dynamic_cast<typed_value<T>* >(conf)->get_val();
}
private:
untyped_value* conf;
};
--8<-------------------------------------------------------------------
En lisant les autres mails qui m'ont dirigé vers boost::any, je me rends
compte que j'ai presque réussi à réinventer la roue, mais avec des
soucis pour la surcharge de "operator<<" (fonction non présente dans ce
code). Si je la déclare comme suit en tant que membre de la classe
config_value :
template<class T> std::ostream& operator(std::ostream& s);
la fonction ne semble pas prise en compte lors de la résolution de
surcharge. Y a-t-il une raison à ça ?
On Tue, 07 Dec 2004 01:42:47 +0100
drkm wrote:
sans accès au net pendant 2 jours, j'ai eu le temps de bosser sur
une
version de la classe de base qui va servir à stocker les valeurs de
configuration (le code est à la fin de ce mail), mais j'ai pas mal
de
question (de débutant je suppose) sur votre bout de code.struct Value {
} ;
Il ne faut pas définir ici un destructeur virtuel vu que cette
classe
est appelée à n'être qu'une classe de base ?
class Variables {
typedef std::map< std::string , Value * >
Map ;
public:
~Variables() {
for( Map::iterator it = myVariables.begin() ,
end = myVariables.end() ;
it != end ;
++ it ) {
delete it->second ;
}
}
Pourquoi appeler les destructeurs de chaque élément de la std::map
?
Son destructeur ne le fait pas ?
[snip de la fin de la classe Variables]void display( Variables const & vars ) {
int const * i = 0 ;
std::string const * s = 0 ;
vars.get( "integer" , i ) ;
vars.get( "string" , s ) ;
if ( i ) {
std::cout << "integer=" << * i << std::endl ;
}
else {
std::cout << "integer=<not set>" << std::endl ;
}
if ( s ) {
std::cout << "string=" << * s << std::endl ;
}
else {
std::cout << "string=<not set>" << std::endl ;
}
}
Le souci c'est qu'avec cette fonction il faut prévoir un cas par
type
intégré ou des conversions implicites sont-elles réalisées ?
Ce n'est qu'un point de départ, il y a plusieurs variantes
possibles. Je dirais que c'est un exemple ou les Policy Classes
devraient être utile.
Je vais googler un coup sur "policy classes", merci pour le code.
Voilà ce que j'ai laborieusement pondu :
--8<-------------------------------------------------------------------
#include <iostream>
#include <map>
class untyped_value
{
public:
virtual ~untyped_value() {}
};
template<class T>
class typed_value : public untyped_value
{
public:
typed_value(const T& t) : value(t) {}
T& get_val() { return value; }
private:
T value;
};
class config_value
{
public:
config_value() : conf(0) {}
~config_value() { delete conf; }
template<class T> config_value(const T& t)
{
conf = new typed_value<T>(t);
}
template<class T> void operator=(const T& t)
{
if(conf) delete conf;
conf = new typed_value<T>(t);
}
template<class T> operator T()
{
return dynamic_cast<typed_value<T>*
(conf)->get_val();
}
private:
untyped_value* conf;
};
--8<-------------------------------------------------------------------
En lisant les autres mails qui m'ont dirigé vers boost::any, je me
rends compte que j'ai presque réussi à réinventer la roue, mais
avec
des soucis pour la surcharge de "operator<<" (fonction non présente
dans ce code). Si je la déclare comme suit en tant que membre de la
classe config_value :
template<class T> std::ostream& operator(std::ostream& s);
la fonction ne semble pas prise en compte lors de la résolution de
surcharge. Y a-t-il une raison à ça ?
On Tue, 07 Dec 2004 01:42:47 +0100
drkm <usenet.fclcxx@fgeorges.org> wrote:
sans accès au net pendant 2 jours, j'ai eu le temps de bosser sur
une
version de la classe de base qui va servir à stocker les valeurs de
configuration (le code est à la fin de ce mail), mais j'ai pas mal
de
question (de débutant je suppose) sur votre bout de code.
struct Value {
} ;
Il ne faut pas définir ici un destructeur virtuel vu que cette
classe
est appelée à n'être qu'une classe de base ?
class Variables {
typedef std::map< std::string , Value * >
Map ;
public:
~Variables() {
for( Map::iterator it = myVariables.begin() ,
end = myVariables.end() ;
it != end ;
++ it ) {
delete it->second ;
}
}
Pourquoi appeler les destructeurs de chaque élément de la std::map
?
Son destructeur ne le fait pas ?
[snip de la fin de la classe Variables]
void display( Variables const & vars ) {
int const * i = 0 ;
std::string const * s = 0 ;
vars.get( "integer" , i ) ;
vars.get( "string" , s ) ;
if ( i ) {
std::cout << "integer=" << * i << std::endl ;
}
else {
std::cout << "integer=<not set>" << std::endl ;
}
if ( s ) {
std::cout << "string=" << * s << std::endl ;
}
else {
std::cout << "string=<not set>" << std::endl ;
}
}
Le souci c'est qu'avec cette fonction il faut prévoir un cas par
type
intégré ou des conversions implicites sont-elles réalisées ?
Ce n'est qu'un point de départ, il y a plusieurs variantes
possibles. Je dirais que c'est un exemple ou les Policy Classes
devraient être utile.
Je vais googler un coup sur "policy classes", merci pour le code.
Voilà ce que j'ai laborieusement pondu :
--8<-------------------------------------------------------------------
#include <iostream>
#include <map>
class untyped_value
{
public:
virtual ~untyped_value() {}
};
template<class T>
class typed_value : public untyped_value
{
public:
typed_value(const T& t) : value(t) {}
T& get_val() { return value; }
private:
T value;
};
class config_value
{
public:
config_value() : conf(0) {}
~config_value() { delete conf; }
template<class T> config_value(const T& t)
{
conf = new typed_value<T>(t);
}
template<class T> void operator=(const T& t)
{
if(conf) delete conf;
conf = new typed_value<T>(t);
}
template<class T> operator T()
{
return dynamic_cast<typed_value<T>*
(conf)->get_val();
}
private:
untyped_value* conf;
};
--8<-------------------------------------------------------------------
En lisant les autres mails qui m'ont dirigé vers boost::any, je me
rends compte que j'ai presque réussi à réinventer la roue, mais
avec
des soucis pour la surcharge de "operator<<" (fonction non présente
dans ce code). Si je la déclare comme suit en tant que membre de la
classe config_value :
template<class T> std::ostream& operator(std::ostream& s);
la fonction ne semble pas prise en compte lors de la résolution de
surcharge. Y a-t-il une raison à ça ?
On Tue, 07 Dec 2004 01:42:47 +0100
drkm wrote:
sans accès au net pendant 2 jours, j'ai eu le temps de bosser sur
une
version de la classe de base qui va servir à stocker les valeurs de
configuration (le code est à la fin de ce mail), mais j'ai pas mal
de
question (de débutant je suppose) sur votre bout de code.struct Value {
} ;
Il ne faut pas définir ici un destructeur virtuel vu que cette
classe
est appelée à n'être qu'une classe de base ?
class Variables {
typedef std::map< std::string , Value * >
Map ;
public:
~Variables() {
for( Map::iterator it = myVariables.begin() ,
end = myVariables.end() ;
it != end ;
++ it ) {
delete it->second ;
}
}
Pourquoi appeler les destructeurs de chaque élément de la std::map
?
Son destructeur ne le fait pas ?
[snip de la fin de la classe Variables]void display( Variables const & vars ) {
int const * i = 0 ;
std::string const * s = 0 ;
vars.get( "integer" , i ) ;
vars.get( "string" , s ) ;
if ( i ) {
std::cout << "integer=" << * i << std::endl ;
}
else {
std::cout << "integer=<not set>" << std::endl ;
}
if ( s ) {
std::cout << "string=" << * s << std::endl ;
}
else {
std::cout << "string=<not set>" << std::endl ;
}
}
Le souci c'est qu'avec cette fonction il faut prévoir un cas par
type
intégré ou des conversions implicites sont-elles réalisées ?
Ce n'est qu'un point de départ, il y a plusieurs variantes
possibles. Je dirais que c'est un exemple ou les Policy Classes
devraient être utile.
Je vais googler un coup sur "policy classes", merci pour le code.
Voilà ce que j'ai laborieusement pondu :
--8<-------------------------------------------------------------------
#include <iostream>
#include <map>
class untyped_value
{
public:
virtual ~untyped_value() {}
};
template<class T>
class typed_value : public untyped_value
{
public:
typed_value(const T& t) : value(t) {}
T& get_val() { return value; }
private:
T value;
};
class config_value
{
public:
config_value() : conf(0) {}
~config_value() { delete conf; }
template<class T> config_value(const T& t)
{
conf = new typed_value<T>(t);
}
template<class T> void operator=(const T& t)
{
if(conf) delete conf;
conf = new typed_value<T>(t);
}
template<class T> operator T()
{
return dynamic_cast<typed_value<T>*
(conf)->get_val();
}
private:
untyped_value* conf;
};
--8<-------------------------------------------------------------------
En lisant les autres mails qui m'ont dirigé vers boost::any, je me
rends compte que j'ai presque réussi à réinventer la roue, mais
avec
des soucis pour la surcharge de "operator<<" (fonction non présente
dans ce code). Si je la déclare comme suit en tant que membre de la
classe config_value :
template<class T> std::ostream& operator(std::ostream& s);
la fonction ne semble pas prise en compte lors de la résolution de
surcharge. Y a-t-il une raison à ça ?