TMatrix::TMatrix(int Shape[], double Valeurs[])
{
dim = sizeof(Shape)/sizeof(Shape[0]);
nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
TMatrix::TMatrix(int Shape[], double Valeurs[])
{
dim = sizeof(Shape)/sizeof(Shape[0]);
nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
TMatrix::TMatrix(int Shape[], double Valeurs[])
{
dim = sizeof(Shape)/sizeof(Shape[0]);
nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
Je souhaites realiser la gestion de matrices
multidimensionnelles dans mon programme. J'ai créé un classe
TMatrix, qui comporte le constructeur :
TMatrix::TMatrix(int Shape[], double Valeurs[])
{
dim = sizeof(Shape)/sizeof(Shape[0]);
nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
[......]
}
Alors se posent plusieurs questions :
1- Je ne peux pas créer de matrices en faisant directement :
TMatrix* mat =
new TMatrix({2,2},{1.1 0.5 0.7 0.6});
, je suis obligé de créer des variables int[] et double[] et
les passer
en parametre avec ces valeurs.... Comment puis-je y remedier ?
2- En passant un Shape de : int[] Shape = {2,2}; lorsque
j'execute le programme, il me met dans dim la valeur 1 (et
quel que soit le vecteur Shape d'ailleurs...). Comment cela se
fait-il ? Comment y remedier ?!
Je souhaites realiser la gestion de matrices
multidimensionnelles dans mon programme. J'ai créé un classe
TMatrix, qui comporte le constructeur :
TMatrix::TMatrix(int Shape[], double Valeurs[])
{
dim = sizeof(Shape)/sizeof(Shape[0]);
nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
[......]
}
Alors se posent plusieurs questions :
1- Je ne peux pas créer de matrices en faisant directement :
TMatrix* mat =
new TMatrix({2,2},{1.1 0.5 0.7 0.6});
, je suis obligé de créer des variables int[] et double[] et
les passer
en parametre avec ces valeurs.... Comment puis-je y remedier ?
2- En passant un Shape de : int[] Shape = {2,2}; lorsque
j'execute le programme, il me met dans dim la valeur 1 (et
quel que soit le vecteur Shape d'ailleurs...). Comment cela se
fait-il ? Comment y remedier ?!
Je souhaites realiser la gestion de matrices
multidimensionnelles dans mon programme. J'ai créé un classe
TMatrix, qui comporte le constructeur :
TMatrix::TMatrix(int Shape[], double Valeurs[])
{
dim = sizeof(Shape)/sizeof(Shape[0]);
nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
[......]
}
Alors se posent plusieurs questions :
1- Je ne peux pas créer de matrices en faisant directement :
TMatrix* mat =
new TMatrix({2,2},{1.1 0.5 0.7 0.6});
, je suis obligé de créer des variables int[] et double[] et
les passer
en parametre avec ces valeurs.... Comment puis-je y remedier ?
2- En passant un Shape de : int[] Shape = {2,2}; lorsque
j'execute le programme, il me met dans dim la valeur 1 (et
quel que soit le vecteur Shape d'ailleurs...). Comment cela se
fait-il ? Comment y remedier ?!
"Nicolas Bonneel" writes:TMatrix::TMatrix(int Shape[], double Valeurs[])
{
dim = sizeof(Shape)/sizeof(Shape[0]);
nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
Hou, ... Tu vas au devant d'ennuis. Ne serait-ce pas plus simple
d'utiliser des std::vector<> ?
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
{
dim = shapes.size() ;
nbelements = values.size() ;
Ou, si dim et nbelements sont des membres :
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
: dim( shapes.size() )
, nbelements( values.size() )
{
Tu peux aussi utiliser deux paires d'itérateurs, si tu ne veux pas
te cantoner à un conteneur particulier. Tu pourras alors même
utiliser des tableaux C en arguments. Mais il faut alors ici faire un
constructeur template.
Si le but est de te familiariser avec C++, la solution utilisant
std::vector<> est de loin la plus simple, ÀMHA.
"Nicolas Bonneel" <nicolas.bonneel.asuprrimer@wanadoo.fr> writes:
TMatrix::TMatrix(int Shape[], double Valeurs[])
{
dim = sizeof(Shape)/sizeof(Shape[0]);
nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
Hou, ... Tu vas au devant d'ennuis. Ne serait-ce pas plus simple
d'utiliser des std::vector<> ?
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
{
dim = shapes.size() ;
nbelements = values.size() ;
Ou, si dim et nbelements sont des membres :
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
: dim( shapes.size() )
, nbelements( values.size() )
{
Tu peux aussi utiliser deux paires d'itérateurs, si tu ne veux pas
te cantoner à un conteneur particulier. Tu pourras alors même
utiliser des tableaux C en arguments. Mais il faut alors ici faire un
constructeur template.
Si le but est de te familiariser avec C++, la solution utilisant
std::vector<> est de loin la plus simple, ÀMHA.
"Nicolas Bonneel" writes:TMatrix::TMatrix(int Shape[], double Valeurs[])
{
dim = sizeof(Shape)/sizeof(Shape[0]);
nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
Hou, ... Tu vas au devant d'ennuis. Ne serait-ce pas plus simple
d'utiliser des std::vector<> ?
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
{
dim = shapes.size() ;
nbelements = values.size() ;
Ou, si dim et nbelements sont des membres :
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
: dim( shapes.size() )
, nbelements( values.size() )
{
Tu peux aussi utiliser deux paires d'itérateurs, si tu ne veux pas
te cantoner à un conteneur particulier. Tu pourras alors même
utiliser des tableaux C en arguments. Mais il faut alors ici faire un
constructeur template.
Si le but est de te familiariser avec C++, la solution utilisant
std::vector<> est de loin la plus simple, ÀMHA.
Je souhaites realiser la gestion de matrices
multidimensionnelles dans mon programme. J'ai créé un classe
TMatrix, qui comporte le constructeur :TMatrix::TMatrix(int Shape[], double Valeurs[])
C-à-d :
TMatrix::TMatrix( int* shape, double* valeurs ) ;{
dim = sizeof(Shape)/sizeof(Shape[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 1, et en
mode 64 bits, 2.nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 0, et en
mode 64 bits, 1.
[......]
}
Tu as déjà un problème : si tu te sers des tableaux à la C, il
faut explicitement passer au même temps les longueurs, parce
qu'il n'y a aucun moyen de les récupérer autrement.
Changer la signature du constructeur en :
TMatrix::TMatrix( std::vector< int > const& shape,
std::vector< double > const& valeurs )
résoud cette problème de façon élégante, mais rend l'appel du
constructeur plus pénible (parce que j'imagine qu'assez souvent,
au moins shape est une constante).
La meilleur solution, c'est sans doute de déclarer :
TMatrix::TMatrix( Shape const& shape, Values const& values )
puis de définir Shape et Values avec une variété de
constructeurs pour permettre tous les cas intéressants. Pour
Shape, je ne vois pas trop de problème :
class Shape
{
public:
template< typename FwdIter >
Shape( FwdIter begin, FwdIter end )
: myData( begin, end )
{
}
template< size_t N >
Shape( int const (&init)[ N ] )
: myData( init, init + N )
{
}
// ...
private:
std::vector< int > myData ;
} ;
(Il y a une solution encore plus élégant, avec un Shape<N> : la
class Shape<N> contient un Shape<N-1> et un int, et il y a
spécialisation pour Shape<1>. Mais j'avoue que l'écrire dépasse
de loin mes compétences en templates.)
En principe, on pouvait se servir de la même technique pour
Values. Seulement, dans le cas de Shape, j'imagine que c'est
plutôt rare que le tableau dépasse une dizaine d'éléments ; en
faire une copie n'est pas la fin du monde. Pour Values, en
revanche, je n'ai pas de mal à imaginer qu'on pourrait avoir des
millions de valeurs, sinon plus. Et même s'il ne faut pas se
précipiter pour s'occuper des problèmes d'optimisation, des
temporaires de cette taille ne sont jamais une bonne chose. Du
coup, il faudrait probablement s'arranger que Values ne copie
pas, mais sait lire les différentes sources, ce qui est plus
difficile. En fin de compte, beaucoup dépend de ce que tu veux
faire avec les valeurs. S'il suffit de pouvoir les copier dans
une collection d'un type connu d'avance, on doit pouvoir se
servir de l'idiome lettre/envélope, avec une fonction virtuelle
copyInto( DestinationType& dest ).
Alors se posent plusieurs questions :
1- Je ne peux pas créer de matrices en faisant directement :
TMatrix* mat >new TMatrix({2,2},{1.1 0.5 0.7 0.6});
, je suis obligé de créer des variables int[] et double[] et
les passeren parametre avec ces valeurs.... Comment puis-je y remedier ?
Pratiquement : tu ne peux pas. Il n'y a simplement pas de syntax
dans le langage pour spécifier une liste de valeurs d'une
longueur arbitraire dans une expression.
Pour Shape, je penserais à fournir des constructeurs spéciaux
pour les cas fréquents : Shape( int ), Shape( int, int ), etc.
Jusqu'où tu veux voir dépend de toi, mais j'imagine avec un et
deux int serait un minimum. Et je serais le premier à avouer que
ce n'est pas très élégant de permettre :
TMatrix m( Shape( 5, 10, 3 ), ... )
mais non
TMatrix m( Shape( 5, 10, 3, 2 ), ... )
Une possibilité aussi serait de fornir un constructeur :
Shape::Shape( int first, ... )
: myData( 1, first )
{
va_list a ;
va_start( a, first ) ;
for ( int i = va_arg( a, int ) ;
i > 0 ;
i = va_arg( a, int ) ) {
myData.push_back( i ) ;
}
}
C'est la plus facile à utliser, mais aussi la plus facile pour
faire des connéries. Il exige que la liste des dimensions soit
terminée par une valeur <= 0 ; que l'utilisateur l'oublie, et tu
pars très vite dans les choux.
Et évidemment, tout ça ne vaut que pour Shape. Pour Values, je
ne vois pas où s'arrêter avec les constructeurs surchargés pour
un, deux ... etc. paramètres, et évidemment, il n'y a pas de
valeur sentinelle disponible pour la solution va_args.2- En passant un Shape de : int[] Shape = {2,2}; lorsque
j'execute le programme, il me met dans dim la valeur 1 (et
quel que soit le vecteur Shape d'ailleurs...). Comment cela se
fait-il ? Comment y remedier ?!
Voir ci-dessus. C'est un problème classique des tableaux de type
C. On ne passe pas des tableaux de type C, on en passe l'adresse
du premier élément. Et même quand on écrit que le programme
prend un tableau comme paramètre, le compilateur le convertit en
pointeur au premier élément. Ce qui veut dire que pour shape,
ton expression vaut « sizeof( int* ) / sizeof( int ) ».
C'est une des raisons (peut-être la raison la plus importante)
pourquoi on dit d'éviter les tableaux de type C, et d'utiliser
vector à sa place. Seulement, passer des valeurs qui sont pour
toi des constantes, comme tu sembles vouloir faire, c'est encore
plus pénible.
N'empeche que c'est avec le constructeur aux deux vecteurs que
je commencerais. Et aussi, les fonctions begin et end classique :
template< typename T, size_t N >
T const*
begin( T const (&array)[ N ] )
{
return array ;
}
template< typename T, size_t N >
T const*
end( T const (&array)[ N ] )
{
return array + N ;
}
et des typedef :
typedef std::vector< int > Shape ;
typedef std::vector< double > Values ;
Ça permettrait quand même à écrire des choses comme :
int const shape[] = { 3, 3 } ;
double const values[] = { 1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0 } ;
TMatrix m( Shape( begin( shape ), end( shape ) ),
Values( begin( values ), end( values ) ) ) ;
Mais je crains que le coût de copier les valeurs soit prohibitif
dans une vraie application.
Je souhaites realiser la gestion de matrices
multidimensionnelles dans mon programme. J'ai créé un classe
TMatrix, qui comporte le constructeur :
TMatrix::TMatrix(int Shape[], double Valeurs[])
C-à-d :
TMatrix::TMatrix( int* shape, double* valeurs ) ;
{
dim = sizeof(Shape)/sizeof(Shape[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 1, et en
mode 64 bits, 2.
nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 0, et en
mode 64 bits, 1.
[......]
}
Tu as déjà un problème : si tu te sers des tableaux à la C, il
faut explicitement passer au même temps les longueurs, parce
qu'il n'y a aucun moyen de les récupérer autrement.
Changer la signature du constructeur en :
TMatrix::TMatrix( std::vector< int > const& shape,
std::vector< double > const& valeurs )
résoud cette problème de façon élégante, mais rend l'appel du
constructeur plus pénible (parce que j'imagine qu'assez souvent,
au moins shape est une constante).
La meilleur solution, c'est sans doute de déclarer :
TMatrix::TMatrix( Shape const& shape, Values const& values )
puis de définir Shape et Values avec une variété de
constructeurs pour permettre tous les cas intéressants. Pour
Shape, je ne vois pas trop de problème :
class Shape
{
public:
template< typename FwdIter >
Shape( FwdIter begin, FwdIter end )
: myData( begin, end )
{
}
template< size_t N >
Shape( int const (&init)[ N ] )
: myData( init, init + N )
{
}
// ...
private:
std::vector< int > myData ;
} ;
(Il y a une solution encore plus élégant, avec un Shape<N> : la
class Shape<N> contient un Shape<N-1> et un int, et il y a
spécialisation pour Shape<1>. Mais j'avoue que l'écrire dépasse
de loin mes compétences en templates.)
En principe, on pouvait se servir de la même technique pour
Values. Seulement, dans le cas de Shape, j'imagine que c'est
plutôt rare que le tableau dépasse une dizaine d'éléments ; en
faire une copie n'est pas la fin du monde. Pour Values, en
revanche, je n'ai pas de mal à imaginer qu'on pourrait avoir des
millions de valeurs, sinon plus. Et même s'il ne faut pas se
précipiter pour s'occuper des problèmes d'optimisation, des
temporaires de cette taille ne sont jamais une bonne chose. Du
coup, il faudrait probablement s'arranger que Values ne copie
pas, mais sait lire les différentes sources, ce qui est plus
difficile. En fin de compte, beaucoup dépend de ce que tu veux
faire avec les valeurs. S'il suffit de pouvoir les copier dans
une collection d'un type connu d'avance, on doit pouvoir se
servir de l'idiome lettre/envélope, avec une fonction virtuelle
copyInto( DestinationType& dest ).
Alors se posent plusieurs questions :
1- Je ne peux pas créer de matrices en faisant directement :
TMatrix* mat >
new TMatrix({2,2},{1.1 0.5 0.7 0.6});
, je suis obligé de créer des variables int[] et double[] et
les passer
en parametre avec ces valeurs.... Comment puis-je y remedier ?
Pratiquement : tu ne peux pas. Il n'y a simplement pas de syntax
dans le langage pour spécifier une liste de valeurs d'une
longueur arbitraire dans une expression.
Pour Shape, je penserais à fournir des constructeurs spéciaux
pour les cas fréquents : Shape( int ), Shape( int, int ), etc.
Jusqu'où tu veux voir dépend de toi, mais j'imagine avec un et
deux int serait un minimum. Et je serais le premier à avouer que
ce n'est pas très élégant de permettre :
TMatrix m( Shape( 5, 10, 3 ), ... )
mais non
TMatrix m( Shape( 5, 10, 3, 2 ), ... )
Une possibilité aussi serait de fornir un constructeur :
Shape::Shape( int first, ... )
: myData( 1, first )
{
va_list a ;
va_start( a, first ) ;
for ( int i = va_arg( a, int ) ;
i > 0 ;
i = va_arg( a, int ) ) {
myData.push_back( i ) ;
}
}
C'est la plus facile à utliser, mais aussi la plus facile pour
faire des connéries. Il exige que la liste des dimensions soit
terminée par une valeur <= 0 ; que l'utilisateur l'oublie, et tu
pars très vite dans les choux.
Et évidemment, tout ça ne vaut que pour Shape. Pour Values, je
ne vois pas où s'arrêter avec les constructeurs surchargés pour
un, deux ... etc. paramètres, et évidemment, il n'y a pas de
valeur sentinelle disponible pour la solution va_args.
2- En passant un Shape de : int[] Shape = {2,2}; lorsque
j'execute le programme, il me met dans dim la valeur 1 (et
quel que soit le vecteur Shape d'ailleurs...). Comment cela se
fait-il ? Comment y remedier ?!
Voir ci-dessus. C'est un problème classique des tableaux de type
C. On ne passe pas des tableaux de type C, on en passe l'adresse
du premier élément. Et même quand on écrit que le programme
prend un tableau comme paramètre, le compilateur le convertit en
pointeur au premier élément. Ce qui veut dire que pour shape,
ton expression vaut « sizeof( int* ) / sizeof( int ) ».
C'est une des raisons (peut-être la raison la plus importante)
pourquoi on dit d'éviter les tableaux de type C, et d'utiliser
vector à sa place. Seulement, passer des valeurs qui sont pour
toi des constantes, comme tu sembles vouloir faire, c'est encore
plus pénible.
N'empeche que c'est avec le constructeur aux deux vecteurs que
je commencerais. Et aussi, les fonctions begin et end classique :
template< typename T, size_t N >
T const*
begin( T const (&array)[ N ] )
{
return array ;
}
template< typename T, size_t N >
T const*
end( T const (&array)[ N ] )
{
return array + N ;
}
et des typedef :
typedef std::vector< int > Shape ;
typedef std::vector< double > Values ;
Ça permettrait quand même à écrire des choses comme :
int const shape[] = { 3, 3 } ;
double const values[] = { 1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0 } ;
TMatrix m( Shape( begin( shape ), end( shape ) ),
Values( begin( values ), end( values ) ) ) ;
Mais je crains que le coût de copier les valeurs soit prohibitif
dans une vraie application.
Je souhaites realiser la gestion de matrices
multidimensionnelles dans mon programme. J'ai créé un classe
TMatrix, qui comporte le constructeur :TMatrix::TMatrix(int Shape[], double Valeurs[])
C-à-d :
TMatrix::TMatrix( int* shape, double* valeurs ) ;{
dim = sizeof(Shape)/sizeof(Shape[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 1, et en
mode 64 bits, 2.nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 0, et en
mode 64 bits, 1.
[......]
}
Tu as déjà un problème : si tu te sers des tableaux à la C, il
faut explicitement passer au même temps les longueurs, parce
qu'il n'y a aucun moyen de les récupérer autrement.
Changer la signature du constructeur en :
TMatrix::TMatrix( std::vector< int > const& shape,
std::vector< double > const& valeurs )
résoud cette problème de façon élégante, mais rend l'appel du
constructeur plus pénible (parce que j'imagine qu'assez souvent,
au moins shape est une constante).
La meilleur solution, c'est sans doute de déclarer :
TMatrix::TMatrix( Shape const& shape, Values const& values )
puis de définir Shape et Values avec une variété de
constructeurs pour permettre tous les cas intéressants. Pour
Shape, je ne vois pas trop de problème :
class Shape
{
public:
template< typename FwdIter >
Shape( FwdIter begin, FwdIter end )
: myData( begin, end )
{
}
template< size_t N >
Shape( int const (&init)[ N ] )
: myData( init, init + N )
{
}
// ...
private:
std::vector< int > myData ;
} ;
(Il y a une solution encore plus élégant, avec un Shape<N> : la
class Shape<N> contient un Shape<N-1> et un int, et il y a
spécialisation pour Shape<1>. Mais j'avoue que l'écrire dépasse
de loin mes compétences en templates.)
En principe, on pouvait se servir de la même technique pour
Values. Seulement, dans le cas de Shape, j'imagine que c'est
plutôt rare que le tableau dépasse une dizaine d'éléments ; en
faire une copie n'est pas la fin du monde. Pour Values, en
revanche, je n'ai pas de mal à imaginer qu'on pourrait avoir des
millions de valeurs, sinon plus. Et même s'il ne faut pas se
précipiter pour s'occuper des problèmes d'optimisation, des
temporaires de cette taille ne sont jamais une bonne chose. Du
coup, il faudrait probablement s'arranger que Values ne copie
pas, mais sait lire les différentes sources, ce qui est plus
difficile. En fin de compte, beaucoup dépend de ce que tu veux
faire avec les valeurs. S'il suffit de pouvoir les copier dans
une collection d'un type connu d'avance, on doit pouvoir se
servir de l'idiome lettre/envélope, avec une fonction virtuelle
copyInto( DestinationType& dest ).
Alors se posent plusieurs questions :
1- Je ne peux pas créer de matrices en faisant directement :
TMatrix* mat >new TMatrix({2,2},{1.1 0.5 0.7 0.6});
, je suis obligé de créer des variables int[] et double[] et
les passeren parametre avec ces valeurs.... Comment puis-je y remedier ?
Pratiquement : tu ne peux pas. Il n'y a simplement pas de syntax
dans le langage pour spécifier une liste de valeurs d'une
longueur arbitraire dans une expression.
Pour Shape, je penserais à fournir des constructeurs spéciaux
pour les cas fréquents : Shape( int ), Shape( int, int ), etc.
Jusqu'où tu veux voir dépend de toi, mais j'imagine avec un et
deux int serait un minimum. Et je serais le premier à avouer que
ce n'est pas très élégant de permettre :
TMatrix m( Shape( 5, 10, 3 ), ... )
mais non
TMatrix m( Shape( 5, 10, 3, 2 ), ... )
Une possibilité aussi serait de fornir un constructeur :
Shape::Shape( int first, ... )
: myData( 1, first )
{
va_list a ;
va_start( a, first ) ;
for ( int i = va_arg( a, int ) ;
i > 0 ;
i = va_arg( a, int ) ) {
myData.push_back( i ) ;
}
}
C'est la plus facile à utliser, mais aussi la plus facile pour
faire des connéries. Il exige que la liste des dimensions soit
terminée par une valeur <= 0 ; que l'utilisateur l'oublie, et tu
pars très vite dans les choux.
Et évidemment, tout ça ne vaut que pour Shape. Pour Values, je
ne vois pas où s'arrêter avec les constructeurs surchargés pour
un, deux ... etc. paramètres, et évidemment, il n'y a pas de
valeur sentinelle disponible pour la solution va_args.2- En passant un Shape de : int[] Shape = {2,2}; lorsque
j'execute le programme, il me met dans dim la valeur 1 (et
quel que soit le vecteur Shape d'ailleurs...). Comment cela se
fait-il ? Comment y remedier ?!
Voir ci-dessus. C'est un problème classique des tableaux de type
C. On ne passe pas des tableaux de type C, on en passe l'adresse
du premier élément. Et même quand on écrit que le programme
prend un tableau comme paramètre, le compilateur le convertit en
pointeur au premier élément. Ce qui veut dire que pour shape,
ton expression vaut « sizeof( int* ) / sizeof( int ) ».
C'est une des raisons (peut-être la raison la plus importante)
pourquoi on dit d'éviter les tableaux de type C, et d'utiliser
vector à sa place. Seulement, passer des valeurs qui sont pour
toi des constantes, comme tu sembles vouloir faire, c'est encore
plus pénible.
N'empeche que c'est avec le constructeur aux deux vecteurs que
je commencerais. Et aussi, les fonctions begin et end classique :
template< typename T, size_t N >
T const*
begin( T const (&array)[ N ] )
{
return array ;
}
template< typename T, size_t N >
T const*
end( T const (&array)[ N ] )
{
return array + N ;
}
et des typedef :
typedef std::vector< int > Shape ;
typedef std::vector< double > Values ;
Ça permettrait quand même à écrire des choses comme :
int const shape[] = { 3, 3 } ;
double const values[] = { 1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0 } ;
TMatrix m( Shape( begin( shape ), end( shape ) ),
Values( begin( values ), end( values ) ) ) ;
Mais je crains que le coût de copier les valeurs soit prohibitif
dans une vraie application.
Hou, ... Tu vas au devant d'ennuis. Ne serait-ce pas plus simple
d'utiliser des std::vector<> ?
Dans ce cas precis, std::valarray<> serait preferable.
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
{
dim = shapes.size() ;
nbelements = values.size() ;
Ou, si dim et nbelements sont des membres :
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
: dim( shapes.size() )
, nbelements( values.size() )
{
Tu peux aussi utiliser deux paires d'itérateurs, si tu ne veux pas
te cantoner à un conteneur particulier. Tu pourras alors même
utiliser des tableaux C en arguments. Mais il faut alors ici faire un
constructeur template.
Si le but est de te familiariser avec C++, la solution utilisant
std::vector<> est de loin la plus simple, ÀMHA.
Si c'est pas le cas, il vaut mieux utiliser une bib existante, parce que
le probleme a l'air simple a premier abord, mais il se complique tres
rapidement.
a+, ld.
Hou, ... Tu vas au devant d'ennuis. Ne serait-ce pas plus simple
d'utiliser des std::vector<> ?
Dans ce cas precis, std::valarray<> serait preferable.
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
{
dim = shapes.size() ;
nbelements = values.size() ;
Ou, si dim et nbelements sont des membres :
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
: dim( shapes.size() )
, nbelements( values.size() )
{
Tu peux aussi utiliser deux paires d'itérateurs, si tu ne veux pas
te cantoner à un conteneur particulier. Tu pourras alors même
utiliser des tableaux C en arguments. Mais il faut alors ici faire un
constructeur template.
Si le but est de te familiariser avec C++, la solution utilisant
std::vector<> est de loin la plus simple, ÀMHA.
Si c'est pas le cas, il vaut mieux utiliser une bib existante, parce que
le probleme a l'air simple a premier abord, mais il se complique tres
rapidement.
a+, ld.
Hou, ... Tu vas au devant d'ennuis. Ne serait-ce pas plus simple
d'utiliser des std::vector<> ?
Dans ce cas precis, std::valarray<> serait preferable.
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
{
dim = shapes.size() ;
nbelements = values.size() ;
Ou, si dim et nbelements sont des membres :
TMatrix::TMatrix( vector< int > shapes , vector< double > values )
: dim( shapes.size() )
, nbelements( values.size() )
{
Tu peux aussi utiliser deux paires d'itérateurs, si tu ne veux pas
te cantoner à un conteneur particulier. Tu pourras alors même
utiliser des tableaux C en arguments. Mais il faut alors ici faire un
constructeur template.
Si le but est de te familiariser avec C++, la solution utilisant
std::vector<> est de loin la plus simple, ÀMHA.
Si c'est pas le cas, il vaut mieux utiliser une bib existante, parce que
le probleme a l'air simple a premier abord, mais il se complique tres
rapidement.
a+, ld.
Je souhaites realiser la gestion de matrices multidimensionnelles dans mon
programme.
Je souhaites realiser la gestion de matrices multidimensionnelles dans mon
programme.
Je souhaites realiser la gestion de matrices multidimensionnelles dans mon
programme.
On Thu, 6 Jan 2005 03:32:25 +0100, "Nicolas Bonneel"
:Je souhaites realiser la gestion de matrices multidimensionnelles dans mon
programme.
Il y a eu un thread là-dessus ici même, assez complet. Cf
<http://www.google.com/advanced_group_search>.
On Thu, 6 Jan 2005 03:32:25 +0100, "Nicolas Bonneel"
<nicolas.bonneel.asuprrimer@wanadoo.fr>:
Je souhaites realiser la gestion de matrices multidimensionnelles dans mon
programme.
Il y a eu un thread là-dessus ici même, assez complet. Cf
<http://www.google.com/advanced_group_search>.
On Thu, 6 Jan 2005 03:32:25 +0100, "Nicolas Bonneel"
:Je souhaites realiser la gestion de matrices multidimensionnelles dans mon
programme.
Il y a eu un thread là-dessus ici même, assez complet. Cf
<http://www.google.com/advanced_group_search>.
(Il y a une solution encore plus élégant, avec un Shape<N> : la
class Shape<N> contient un Shape<N-1> et un int, et il y a
spécialisation pour Shape<1>. Mais j'avoue que l'écrire dépasse
de loin mes compétences en templates.)
(Il y a une solution encore plus élégant, avec un Shape<N> : la
class Shape<N> contient un Shape<N-1> et un int, et il y a
spécialisation pour Shape<1>. Mais j'avoue que l'écrire dépasse
de loin mes compétences en templates.)
(Il y a une solution encore plus élégant, avec un Shape<N> : la
class Shape<N> contient un Shape<N-1> et un int, et il y a
spécialisation pour Shape<1>. Mais j'avoue que l'écrire dépasse
de loin mes compétences en templates.)
Si c'est pas le cas, il vaut mieux utiliser une bib existante, parce
que le probleme a l'air simple a premier abord, mais il se complique
tres rapidement.
erf...justement, y'en a ? ;)
Si c'est pas le cas, il vaut mieux utiliser une bib existante, parce
que le probleme a l'air simple a premier abord, mais il se complique
tres rapidement.
erf...justement, y'en a ? ;)
Si c'est pas le cas, il vaut mieux utiliser une bib existante, parce
que le probleme a l'air simple a premier abord, mais il se complique
tres rapidement.
erf...justement, y'en a ? ;)
Desolé pour ma réponse tardive mais mon PC est parti en
reparation, et je viens à peine de rentrer chez mes parents
pour squatter leur pc ;)Je souhaites realiser la gestion de matrices
multidimensionnelles dans mon programme. J'ai créé un classe
TMatrix, qui comporte le constructeur :
TMatrix::TMatrix(int Shape[], double Valeurs[])
C-à-d :
TMatrix::TMatrix( int* shape, double* valeurs ) ;{
dim = sizeof(Shape)/sizeof(Shape[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 1, et en
mode 64 bits, 2.nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 0, et
en mode 64 bits, 1.
J'avais pourtant vu cette astuce dans l'aide de c++ builder (à
sizeof) pour connaitre le nombre d'elements d'un tableau...
[......]
}
Tu as déjà un problème : si tu te sers des tableaux à la C,
il faut explicitement passer au même temps les longueurs,
parce qu'il n'y a aucun moyen de les récupérer autrement.
Changer la signature du constructeur en :
TMatrix::TMatrix( std::vector< int > const& shape,
std::vector< double > const& valeurs )
résoud cette problème de façon élégante, mais rend l'appel
du constructeur plus pénible (parce que j'imagine qu'assez
souvent, au moins shape est une constante).
ben en faite je comptais pas laisser shape constant... (comme
lorsqu'on utilise la fonction reshape de matlab...) enfin
disons plutot que le parametre shape est constant tout au long
du constructeur, mais le membre _shape ne l'est pas...
La meilleur solution, c'est sans doute de déclarer :
TMatrix::TMatrix( Shape const& shape, Values const& values )
puis de définir Shape et Values avec une variété de
constructeurs pour permettre tous les cas intéressants. Pour
Shape, je ne vois pas trop de problème :
class Shape
{
public:
template< typename FwdIter >
Shape( FwdIter begin, FwdIter end )
: myData( begin, end )
{
}
template< size_t N >
Shape( int const (&init)[ N ] )
: myData( init, init + N )
{
}
// ...
private:
std::vector< int > myData ;
} ;
(Il y a une solution encore plus élégant, avec un Shape<N> :
la class Shape<N> contient un Shape<N-1> et un int, et il y
a spécialisation pour Shape<1>. Mais j'avoue que l'écrire
dépasse de loin mes compétences en templates.)
Cette solution me parait jolie, permettrait de pouvoir faire des
tableaux d'int, de float et de double ...mais le hic c'est que je ne
connais pas du tout le fonctionnement des templates ;)
Va faloir que je me documente un peu moi! (enfin dans mon cas,
Shape sera toujours un tableau d'entiers, mais values pourront
etre des reels ou des entiers (ou des complexes... ou peut
être meme d'autres tableaux ?..))
Bref, tout ca pour dire que j'ai un peu de mal à voir ce que
le code ci-dessus realise exactement ;)
En principe, on pouvait se servir de la même technique pour
Values. Seulement, dans le cas de Shape, j'imagine que c'est
plutôt rare que le tableau dépasse une dizaine d'éléments ;
en faire une copie n'est pas la fin du monde. Pour Values,
en revanche, je n'ai pas de mal à imaginer qu'on pourrait
avoir des millions de valeurs, sinon plus. Et même s'il ne
faut pas se précipiter pour s'occuper des problèmes
d'optimisation, des temporaires de cette taille ne sont
jamais une bonne chose. Du coup, il faudrait probablement
s'arranger que Values ne copie pas, mais sait lire les
différentes sources, ce qui est plus difficile. En fin de
compte, beaucoup dépend de ce que tu veux faire avec les
valeurs. S'il suffit de pouvoir les copier dans une
collection d'un type connu d'avance, on doit pouvoir se
servir de l'idiome lettre/envélope, avec une fonction
virtuelle copyInto( DestinationType& dest ).
C'est vrai que values pourra contenir autant d'elements qu'on
souhaite (milliers, millions et plus..?). C'est pour faire une
classe matrix complete où je pourrais deriver une classe
vecteur et une classe matrix2D (pour y faire des
factorisations matricielles, des resolutions etc...)
Alors se posent plusieurs questions :
1- Je ne peux pas créer de matrices en faisant directement :
TMatrix* mat =new TMatrix({2,2},{1.1 0.5 0.7 0.6});
, je suis obligé de créer des variables int[] et double[] et
les passeren parametre avec ces valeurs.... Comment puis-je y remedier ?
Pratiquement : tu ne peux pas. Il n'y a simplement pas de
syntax dans le langage pour spécifier une liste de valeurs
d'une longueur arbitraire dans une expression.
ben pourtant on peu declarer : int tableau[] = {1,2,3,4}; non ??
Pour Shape, je penserais à fournir des constructeurs
spéciaux pour les cas fréquents : Shape( int ), Shape( int,
int ), etc.
Erf, je vois ce que tu veux dire... mais en fait je comptais
laisser shape sous forme de tableau 1D puisque le tableau que
shape décrit peut contenir autant de dimensions qu'on le
souhaite : autant une matrice 4x5 (declarée par un shape(4,5))
qu'une hypermatrice 4x3x2x8x6x5.. declarée shape(4,3,2,8,6,5).
C'est vrai que d'un maniere générale shape ne contiendra pas
beaucoup plus de 5-6 elements au grand maximum, mais on sait
jamais ;)
Jusqu'où tu veux voir dépend de toi, mais j'imagine avec un
et deux int serait un minimum. Et je serais le premier à
avouer que ce n'est pas très élégant de permettre :
TMatrix m( Shape( 5, 10, 3 ), ... )
mais non
TMatrix m( Shape( 5, 10, 3, 2 ), ... )
voilà.. ;)Une possibilité aussi serait de fornir un constructeur :
Shape::Shape( int first, ... )
: myData( 1, first )
{
va_list a ;
va_start( a, first ) ;
for ( int i = va_arg( a, int ) ;
i > 0 ;
i = va_arg( a, int ) ) {
myData.push_back( i ) ;
}
}
C'est la plus facile à utliser, mais aussi la plus facile
pour faire des connéries. Il exige que la liste des
dimensions soit terminée par une valeur <= 0 ; que
l'utilisateur l'oublie, et tu pars très vite dans les choux.
Là se pose un autre problème : les paramètres proviennent d'un
script interprété. Donc si je connais N paramètres lus du
script, en voulant utiliser le constructeur, je ne saurais pas
trop comment faire ! genre un Shape(param[1],
param[2],...,param[N]) avec N variable...
Et évidemment, tout ça ne vaut que pour Shape. Pour Values,
je ne vois pas où s'arrêter avec les constructeurs
surchargés pour un, deux ... etc. paramètres, et évidemment,
il n'y a pas de valeur sentinelle disponible pour la
solution va_args.2- En passant un Shape de : int[] Shape = {2,2}; lorsque
j'execute le programme, il me met dans dim la valeur 1 (et
quel que soit le vecteur Shape d'ailleurs...). Comment cela se
fait-il ? Comment y remedier ?!
Voir ci-dessus. C'est un problème classique des tableaux de
type C. On ne passe pas des tableaux de type C, on en passe
l'adresse du premier élément. Et même quand on écrit que le
programme prend un tableau comme paramètre, le compilateur
le convertit en pointeur au premier élément. Ce qui veut
dire que pour shape, ton expression vaut « sizeof( int* ) /
sizeof( int ) ».
C'est une des raisons (peut-être la raison la plus
importante) pourquoi on dit d'éviter les tableaux de type C,
et d'utiliser vector à sa place. Seulement, passer des
valeurs qui sont pour toi des constantes, comme tu sembles
vouloir faire, c'est encore plus pénible.
Ben je crois que je vais utiliser le vector moi finallement...
Le fait de passer des vector en parametre du constructeur, ca
les passe par copie ou par reference (comme pour un tableau
C) ?
Recopier un vecteur d'1 million d'elements sur un autre,
est-ce une opération longue, ou suffisament bien gérée pour
qu'elle soit rapide ?
(disons que l'interet des grosses matrices, c'est de faire des
gros calculs derriere, donc je serai pas à une copie près en
terme de cpu...)
Desolé pour ma réponse tardive mais mon PC est parti en
reparation, et je viens à peine de rentrer chez mes parents
pour squatter leur pc ;)
Je souhaites realiser la gestion de matrices
multidimensionnelles dans mon programme. J'ai créé un classe
TMatrix, qui comporte le constructeur :
TMatrix::TMatrix(int Shape[], double Valeurs[])
C-à-d :
TMatrix::TMatrix( int* shape, double* valeurs ) ;
{
dim = sizeof(Shape)/sizeof(Shape[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 1, et en
mode 64 bits, 2.
nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 0, et
en mode 64 bits, 1.
J'avais pourtant vu cette astuce dans l'aide de c++ builder (à
sizeof) pour connaitre le nombre d'elements d'un tableau...
[......]
}
Tu as déjà un problème : si tu te sers des tableaux à la C,
il faut explicitement passer au même temps les longueurs,
parce qu'il n'y a aucun moyen de les récupérer autrement.
Changer la signature du constructeur en :
TMatrix::TMatrix( std::vector< int > const& shape,
std::vector< double > const& valeurs )
résoud cette problème de façon élégante, mais rend l'appel
du constructeur plus pénible (parce que j'imagine qu'assez
souvent, au moins shape est une constante).
ben en faite je comptais pas laisser shape constant... (comme
lorsqu'on utilise la fonction reshape de matlab...) enfin
disons plutot que le parametre shape est constant tout au long
du constructeur, mais le membre _shape ne l'est pas...
La meilleur solution, c'est sans doute de déclarer :
TMatrix::TMatrix( Shape const& shape, Values const& values )
puis de définir Shape et Values avec une variété de
constructeurs pour permettre tous les cas intéressants. Pour
Shape, je ne vois pas trop de problème :
class Shape
{
public:
template< typename FwdIter >
Shape( FwdIter begin, FwdIter end )
: myData( begin, end )
{
}
template< size_t N >
Shape( int const (&init)[ N ] )
: myData( init, init + N )
{
}
// ...
private:
std::vector< int > myData ;
} ;
(Il y a une solution encore plus élégant, avec un Shape<N> :
la class Shape<N> contient un Shape<N-1> et un int, et il y
a spécialisation pour Shape<1>. Mais j'avoue que l'écrire
dépasse de loin mes compétences en templates.)
Cette solution me parait jolie, permettrait de pouvoir faire des
tableaux d'int, de float et de double ...mais le hic c'est que je ne
connais pas du tout le fonctionnement des templates ;)
Va faloir que je me documente un peu moi! (enfin dans mon cas,
Shape sera toujours un tableau d'entiers, mais values pourront
etre des reels ou des entiers (ou des complexes... ou peut
être meme d'autres tableaux ?..))
Bref, tout ca pour dire que j'ai un peu de mal à voir ce que
le code ci-dessus realise exactement ;)
En principe, on pouvait se servir de la même technique pour
Values. Seulement, dans le cas de Shape, j'imagine que c'est
plutôt rare que le tableau dépasse une dizaine d'éléments ;
en faire une copie n'est pas la fin du monde. Pour Values,
en revanche, je n'ai pas de mal à imaginer qu'on pourrait
avoir des millions de valeurs, sinon plus. Et même s'il ne
faut pas se précipiter pour s'occuper des problèmes
d'optimisation, des temporaires de cette taille ne sont
jamais une bonne chose. Du coup, il faudrait probablement
s'arranger que Values ne copie pas, mais sait lire les
différentes sources, ce qui est plus difficile. En fin de
compte, beaucoup dépend de ce que tu veux faire avec les
valeurs. S'il suffit de pouvoir les copier dans une
collection d'un type connu d'avance, on doit pouvoir se
servir de l'idiome lettre/envélope, avec une fonction
virtuelle copyInto( DestinationType& dest ).
C'est vrai que values pourra contenir autant d'elements qu'on
souhaite (milliers, millions et plus..?). C'est pour faire une
classe matrix complete où je pourrais deriver une classe
vecteur et une classe matrix2D (pour y faire des
factorisations matricielles, des resolutions etc...)
Alors se posent plusieurs questions :
1- Je ne peux pas créer de matrices en faisant directement :
TMatrix* mat =
new TMatrix({2,2},{1.1 0.5 0.7 0.6});
, je suis obligé de créer des variables int[] et double[] et
les passer
en parametre avec ces valeurs.... Comment puis-je y remedier ?
Pratiquement : tu ne peux pas. Il n'y a simplement pas de
syntax dans le langage pour spécifier une liste de valeurs
d'une longueur arbitraire dans une expression.
ben pourtant on peu declarer : int tableau[] = {1,2,3,4}; non ??
Pour Shape, je penserais à fournir des constructeurs
spéciaux pour les cas fréquents : Shape( int ), Shape( int,
int ), etc.
Erf, je vois ce que tu veux dire... mais en fait je comptais
laisser shape sous forme de tableau 1D puisque le tableau que
shape décrit peut contenir autant de dimensions qu'on le
souhaite : autant une matrice 4x5 (declarée par un shape(4,5))
qu'une hypermatrice 4x3x2x8x6x5.. declarée shape(4,3,2,8,6,5).
C'est vrai que d'un maniere générale shape ne contiendra pas
beaucoup plus de 5-6 elements au grand maximum, mais on sait
jamais ;)
Jusqu'où tu veux voir dépend de toi, mais j'imagine avec un
et deux int serait un minimum. Et je serais le premier à
avouer que ce n'est pas très élégant de permettre :
TMatrix m( Shape( 5, 10, 3 ), ... )
mais non
TMatrix m( Shape( 5, 10, 3, 2 ), ... )
voilà.. ;)
Une possibilité aussi serait de fornir un constructeur :
Shape::Shape( int first, ... )
: myData( 1, first )
{
va_list a ;
va_start( a, first ) ;
for ( int i = va_arg( a, int ) ;
i > 0 ;
i = va_arg( a, int ) ) {
myData.push_back( i ) ;
}
}
C'est la plus facile à utliser, mais aussi la plus facile
pour faire des connéries. Il exige que la liste des
dimensions soit terminée par une valeur <= 0 ; que
l'utilisateur l'oublie, et tu pars très vite dans les choux.
Là se pose un autre problème : les paramètres proviennent d'un
script interprété. Donc si je connais N paramètres lus du
script, en voulant utiliser le constructeur, je ne saurais pas
trop comment faire ! genre un Shape(param[1],
param[2],...,param[N]) avec N variable...
Et évidemment, tout ça ne vaut que pour Shape. Pour Values,
je ne vois pas où s'arrêter avec les constructeurs
surchargés pour un, deux ... etc. paramètres, et évidemment,
il n'y a pas de valeur sentinelle disponible pour la
solution va_args.
2- En passant un Shape de : int[] Shape = {2,2}; lorsque
j'execute le programme, il me met dans dim la valeur 1 (et
quel que soit le vecteur Shape d'ailleurs...). Comment cela se
fait-il ? Comment y remedier ?!
Voir ci-dessus. C'est un problème classique des tableaux de
type C. On ne passe pas des tableaux de type C, on en passe
l'adresse du premier élément. Et même quand on écrit que le
programme prend un tableau comme paramètre, le compilateur
le convertit en pointeur au premier élément. Ce qui veut
dire que pour shape, ton expression vaut « sizeof( int* ) /
sizeof( int ) ».
C'est une des raisons (peut-être la raison la plus
importante) pourquoi on dit d'éviter les tableaux de type C,
et d'utiliser vector à sa place. Seulement, passer des
valeurs qui sont pour toi des constantes, comme tu sembles
vouloir faire, c'est encore plus pénible.
Ben je crois que je vais utiliser le vector moi finallement...
Le fait de passer des vector en parametre du constructeur, ca
les passe par copie ou par reference (comme pour un tableau
C) ?
Recopier un vecteur d'1 million d'elements sur un autre,
est-ce une opération longue, ou suffisament bien gérée pour
qu'elle soit rapide ?
(disons que l'interet des grosses matrices, c'est de faire des
gros calculs derriere, donc je serai pas à une copie près en
terme de cpu...)
Desolé pour ma réponse tardive mais mon PC est parti en
reparation, et je viens à peine de rentrer chez mes parents
pour squatter leur pc ;)Je souhaites realiser la gestion de matrices
multidimensionnelles dans mon programme. J'ai créé un classe
TMatrix, qui comporte le constructeur :
TMatrix::TMatrix(int Shape[], double Valeurs[])
C-à-d :
TMatrix::TMatrix( int* shape, double* valeurs ) ;{
dim = sizeof(Shape)/sizeof(Shape[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 1, et en
mode 64 bits, 2.nbelements = sizeof(Valeurs)/sizeof(Valeurs[0]);
Sur ma machine, en mode 32 bits, ça donnerait toujours 0, et
en mode 64 bits, 1.
J'avais pourtant vu cette astuce dans l'aide de c++ builder (à
sizeof) pour connaitre le nombre d'elements d'un tableau...
[......]
}
Tu as déjà un problème : si tu te sers des tableaux à la C,
il faut explicitement passer au même temps les longueurs,
parce qu'il n'y a aucun moyen de les récupérer autrement.
Changer la signature du constructeur en :
TMatrix::TMatrix( std::vector< int > const& shape,
std::vector< double > const& valeurs )
résoud cette problème de façon élégante, mais rend l'appel
du constructeur plus pénible (parce que j'imagine qu'assez
souvent, au moins shape est une constante).
ben en faite je comptais pas laisser shape constant... (comme
lorsqu'on utilise la fonction reshape de matlab...) enfin
disons plutot que le parametre shape est constant tout au long
du constructeur, mais le membre _shape ne l'est pas...
La meilleur solution, c'est sans doute de déclarer :
TMatrix::TMatrix( Shape const& shape, Values const& values )
puis de définir Shape et Values avec une variété de
constructeurs pour permettre tous les cas intéressants. Pour
Shape, je ne vois pas trop de problème :
class Shape
{
public:
template< typename FwdIter >
Shape( FwdIter begin, FwdIter end )
: myData( begin, end )
{
}
template< size_t N >
Shape( int const (&init)[ N ] )
: myData( init, init + N )
{
}
// ...
private:
std::vector< int > myData ;
} ;
(Il y a une solution encore plus élégant, avec un Shape<N> :
la class Shape<N> contient un Shape<N-1> et un int, et il y
a spécialisation pour Shape<1>. Mais j'avoue que l'écrire
dépasse de loin mes compétences en templates.)
Cette solution me parait jolie, permettrait de pouvoir faire des
tableaux d'int, de float et de double ...mais le hic c'est que je ne
connais pas du tout le fonctionnement des templates ;)
Va faloir que je me documente un peu moi! (enfin dans mon cas,
Shape sera toujours un tableau d'entiers, mais values pourront
etre des reels ou des entiers (ou des complexes... ou peut
être meme d'autres tableaux ?..))
Bref, tout ca pour dire que j'ai un peu de mal à voir ce que
le code ci-dessus realise exactement ;)
En principe, on pouvait se servir de la même technique pour
Values. Seulement, dans le cas de Shape, j'imagine que c'est
plutôt rare que le tableau dépasse une dizaine d'éléments ;
en faire une copie n'est pas la fin du monde. Pour Values,
en revanche, je n'ai pas de mal à imaginer qu'on pourrait
avoir des millions de valeurs, sinon plus. Et même s'il ne
faut pas se précipiter pour s'occuper des problèmes
d'optimisation, des temporaires de cette taille ne sont
jamais une bonne chose. Du coup, il faudrait probablement
s'arranger que Values ne copie pas, mais sait lire les
différentes sources, ce qui est plus difficile. En fin de
compte, beaucoup dépend de ce que tu veux faire avec les
valeurs. S'il suffit de pouvoir les copier dans une
collection d'un type connu d'avance, on doit pouvoir se
servir de l'idiome lettre/envélope, avec une fonction
virtuelle copyInto( DestinationType& dest ).
C'est vrai que values pourra contenir autant d'elements qu'on
souhaite (milliers, millions et plus..?). C'est pour faire une
classe matrix complete où je pourrais deriver une classe
vecteur et une classe matrix2D (pour y faire des
factorisations matricielles, des resolutions etc...)
Alors se posent plusieurs questions :
1- Je ne peux pas créer de matrices en faisant directement :
TMatrix* mat =new TMatrix({2,2},{1.1 0.5 0.7 0.6});
, je suis obligé de créer des variables int[] et double[] et
les passeren parametre avec ces valeurs.... Comment puis-je y remedier ?
Pratiquement : tu ne peux pas. Il n'y a simplement pas de
syntax dans le langage pour spécifier une liste de valeurs
d'une longueur arbitraire dans une expression.
ben pourtant on peu declarer : int tableau[] = {1,2,3,4}; non ??
Pour Shape, je penserais à fournir des constructeurs
spéciaux pour les cas fréquents : Shape( int ), Shape( int,
int ), etc.
Erf, je vois ce que tu veux dire... mais en fait je comptais
laisser shape sous forme de tableau 1D puisque le tableau que
shape décrit peut contenir autant de dimensions qu'on le
souhaite : autant une matrice 4x5 (declarée par un shape(4,5))
qu'une hypermatrice 4x3x2x8x6x5.. declarée shape(4,3,2,8,6,5).
C'est vrai que d'un maniere générale shape ne contiendra pas
beaucoup plus de 5-6 elements au grand maximum, mais on sait
jamais ;)
Jusqu'où tu veux voir dépend de toi, mais j'imagine avec un
et deux int serait un minimum. Et je serais le premier à
avouer que ce n'est pas très élégant de permettre :
TMatrix m( Shape( 5, 10, 3 ), ... )
mais non
TMatrix m( Shape( 5, 10, 3, 2 ), ... )
voilà.. ;)Une possibilité aussi serait de fornir un constructeur :
Shape::Shape( int first, ... )
: myData( 1, first )
{
va_list a ;
va_start( a, first ) ;
for ( int i = va_arg( a, int ) ;
i > 0 ;
i = va_arg( a, int ) ) {
myData.push_back( i ) ;
}
}
C'est la plus facile à utliser, mais aussi la plus facile
pour faire des connéries. Il exige que la liste des
dimensions soit terminée par une valeur <= 0 ; que
l'utilisateur l'oublie, et tu pars très vite dans les choux.
Là se pose un autre problème : les paramètres proviennent d'un
script interprété. Donc si je connais N paramètres lus du
script, en voulant utiliser le constructeur, je ne saurais pas
trop comment faire ! genre un Shape(param[1],
param[2],...,param[N]) avec N variable...
Et évidemment, tout ça ne vaut que pour Shape. Pour Values,
je ne vois pas où s'arrêter avec les constructeurs
surchargés pour un, deux ... etc. paramètres, et évidemment,
il n'y a pas de valeur sentinelle disponible pour la
solution va_args.2- En passant un Shape de : int[] Shape = {2,2}; lorsque
j'execute le programme, il me met dans dim la valeur 1 (et
quel que soit le vecteur Shape d'ailleurs...). Comment cela se
fait-il ? Comment y remedier ?!
Voir ci-dessus. C'est un problème classique des tableaux de
type C. On ne passe pas des tableaux de type C, on en passe
l'adresse du premier élément. Et même quand on écrit que le
programme prend un tableau comme paramètre, le compilateur
le convertit en pointeur au premier élément. Ce qui veut
dire que pour shape, ton expression vaut « sizeof( int* ) /
sizeof( int ) ».
C'est une des raisons (peut-être la raison la plus
importante) pourquoi on dit d'éviter les tableaux de type C,
et d'utiliser vector à sa place. Seulement, passer des
valeurs qui sont pour toi des constantes, comme tu sembles
vouloir faire, c'est encore plus pénible.
Ben je crois que je vais utiliser le vector moi finallement...
Le fait de passer des vector en parametre du constructeur, ca
les passe par copie ou par reference (comme pour un tableau
C) ?
Recopier un vecteur d'1 million d'elements sur un autre,
est-ce une opération longue, ou suffisament bien gérée pour
qu'elle soit rapide ?
(disons que l'interet des grosses matrices, c'est de faire des
gros calculs derriere, donc je serai pas à une copie près en
terme de cpu...)