Cette histoire de template instanciée de manière identique à
l'ensemble des codes sources compilés, ça fait un peu effet de bord
je trouve...
On pourrait desirer que le non respect de l'ODR soit detecte, mais a
part ca je vois mal ce que tu veux. Tu peux mettre la definition du
template dans un namespace anonyme, mais alors tu ne peux plus
passer tes objets d'une unite de compilation a l'autre.
Je veux juste comprendre. J'ai fait une erreur, je cherche pas à ce qu'elle
soit acceptée. Par contre j'aurai bien aimé qu'elle soit détectée.
Pour te donner une idée de code qui crash sous VC++ 8 :
// test.cpp
#define _SECURE_SCL 0
#include <vector>
#include <algorithm>
void test()
{
std::vector<int> v( 150, 10 );
std::vector<int>::iterator max_i > std::max_element( v.begin(), v.end() );
int max = *max_i;
}
// main.cpp
#include <vector>
template class std::vector<int, std::allocator< int > >;
void test();
int main()
{
test();
return 0;
}
Pas besoin de passer des instances de std::vector entre les unités de
compilation, la spécialisation explicite dans main.cpp suffit.
Et si c'est main.cpp qui définit _SECURE_SCL à 0, ça marche...
Je me demande s'il serait possible de provoquer des erreurs à
l'édition des liens si l'ODR n'était pas respectée.
Cette histoire de template instanciée de manière identique à
l'ensemble des codes sources compilés, ça fait un peu effet de bord
je trouve...
On pourrait desirer que le non respect de l'ODR soit detecte, mais a
part ca je vois mal ce que tu veux. Tu peux mettre la definition du
template dans un namespace anonyme, mais alors tu ne peux plus
passer tes objets d'une unite de compilation a l'autre.
Je veux juste comprendre. J'ai fait une erreur, je cherche pas à ce qu'elle
soit acceptée. Par contre j'aurai bien aimé qu'elle soit détectée.
Pour te donner une idée de code qui crash sous VC++ 8 :
// test.cpp
#define _SECURE_SCL 0
#include <vector>
#include <algorithm>
void test()
{
std::vector<int> v( 150, 10 );
std::vector<int>::iterator max_i > std::max_element( v.begin(), v.end() );
int max = *max_i;
}
// main.cpp
#include <vector>
template class std::vector<int, std::allocator< int > >;
void test();
int main()
{
test();
return 0;
}
Pas besoin de passer des instances de std::vector entre les unités de
compilation, la spécialisation explicite dans main.cpp suffit.
Et si c'est main.cpp qui définit _SECURE_SCL à 0, ça marche...
Je me demande s'il serait possible de provoquer des erreurs à
l'édition des liens si l'ODR n'était pas respectée.
Cette histoire de template instanciée de manière identique à
l'ensemble des codes sources compilés, ça fait un peu effet de bord
je trouve...
On pourrait desirer que le non respect de l'ODR soit detecte, mais a
part ca je vois mal ce que tu veux. Tu peux mettre la definition du
template dans un namespace anonyme, mais alors tu ne peux plus
passer tes objets d'une unite de compilation a l'autre.
Je veux juste comprendre. J'ai fait une erreur, je cherche pas à ce qu'elle
soit acceptée. Par contre j'aurai bien aimé qu'elle soit détectée.
Pour te donner une idée de code qui crash sous VC++ 8 :
// test.cpp
#define _SECURE_SCL 0
#include <vector>
#include <algorithm>
void test()
{
std::vector<int> v( 150, 10 );
std::vector<int>::iterator max_i > std::max_element( v.begin(), v.end() );
int max = *max_i;
}
// main.cpp
#include <vector>
template class std::vector<int, std::allocator< int > >;
void test();
int main()
{
test();
return 0;
}
Pas besoin de passer des instances de std::vector entre les unités de
compilation, la spécialisation explicite dans main.cpp suffit.
Et si c'est main.cpp qui définit _SECURE_SCL à 0, ça marche...
Je me demande s'il serait possible de provoquer des erreurs à
l'édition des liens si l'ODR n'était pas respectée.
Pas besoin de passer des instances de std::vector entre les unités de
compilation, la spécialisation explicite dans main.cpp suffit.
Ce qui se passe vraissemblablement ici c'est que tu as des membres
inlines dans test.cpp qui utilisent une structure pour
std::vector<int> qui suppose _SECURE_SCL a 0. Dans test.cpp il y a
aussi des appels a des membres non inlines qui dans le cas de
l'optimisation globale sont prises dans main.cpp et qui donc supposent
_SECURE_SCL non definis. Rien n'empecherait l'editeur de liens de
faire le meme choix sans l'optimisation globale, ou meme en debug...Et si c'est main.cpp qui définit _SECURE_SCL à 0, ça marche...
Il suffit que la version choisie paraisse marcher. Soit que l'editeur
fasse un autre choix et choisisse la version dans test.cpp, soit que
par hasard la fonction avec _SECURE_SCL fonctionne dans les deux cas,
soit qu'il y a un probleme de corruption ou autre que tu n'as pas
detecte sur un exemple aussi simple.Je me demande s'il serait possible de provoquer des erreurs à
l'édition des liens si l'ODR n'était pas respectée.
Je ne vois rien a faire que de subir ce que fait l'implementation pour
qui ne la controle pas. Si tu controles l'implementation, c'est
possible avec un editeur de liens suffisemment sophistique (ce qui ne
le serait pas beaucoup). Il "suffirait" d'emettre dans chaque unite
de compilation qui voit (ou utilise simplement si on veut etre moins
strict) la definition d'un type une signature de la partie
significative du type et que l'editeur de liens verifie que tous les
types ont bien la meme signature dans toutes les unites de
compilation. C'est pas complique. La partie Ada de GCC fait plus ou
moins l'equivalent pour l'Ada (ils utilisaient d'autres fichiers que
les .o la derniere fois que j'ai regarde, mais rien n'empeche de
mettre cette info dans les .o et il me semble que la doc faisait
allusion a cette possibilite).
Pas besoin de passer des instances de std::vector entre les unités de
compilation, la spécialisation explicite dans main.cpp suffit.
Ce qui se passe vraissemblablement ici c'est que tu as des membres
inlines dans test.cpp qui utilisent une structure pour
std::vector<int> qui suppose _SECURE_SCL a 0. Dans test.cpp il y a
aussi des appels a des membres non inlines qui dans le cas de
l'optimisation globale sont prises dans main.cpp et qui donc supposent
_SECURE_SCL non definis. Rien n'empecherait l'editeur de liens de
faire le meme choix sans l'optimisation globale, ou meme en debug...
Et si c'est main.cpp qui définit _SECURE_SCL à 0, ça marche...
Il suffit que la version choisie paraisse marcher. Soit que l'editeur
fasse un autre choix et choisisse la version dans test.cpp, soit que
par hasard la fonction avec _SECURE_SCL fonctionne dans les deux cas,
soit qu'il y a un probleme de corruption ou autre que tu n'as pas
detecte sur un exemple aussi simple.
Je me demande s'il serait possible de provoquer des erreurs à
l'édition des liens si l'ODR n'était pas respectée.
Je ne vois rien a faire que de subir ce que fait l'implementation pour
qui ne la controle pas. Si tu controles l'implementation, c'est
possible avec un editeur de liens suffisemment sophistique (ce qui ne
le serait pas beaucoup). Il "suffirait" d'emettre dans chaque unite
de compilation qui voit (ou utilise simplement si on veut etre moins
strict) la definition d'un type une signature de la partie
significative du type et que l'editeur de liens verifie que tous les
types ont bien la meme signature dans toutes les unites de
compilation. C'est pas complique. La partie Ada de GCC fait plus ou
moins l'equivalent pour l'Ada (ils utilisaient d'autres fichiers que
les .o la derniere fois que j'ai regarde, mais rien n'empeche de
mettre cette info dans les .o et il me semble que la doc faisait
allusion a cette possibilite).
Pas besoin de passer des instances de std::vector entre les unités de
compilation, la spécialisation explicite dans main.cpp suffit.
Ce qui se passe vraissemblablement ici c'est que tu as des membres
inlines dans test.cpp qui utilisent une structure pour
std::vector<int> qui suppose _SECURE_SCL a 0. Dans test.cpp il y a
aussi des appels a des membres non inlines qui dans le cas de
l'optimisation globale sont prises dans main.cpp et qui donc supposent
_SECURE_SCL non definis. Rien n'empecherait l'editeur de liens de
faire le meme choix sans l'optimisation globale, ou meme en debug...Et si c'est main.cpp qui définit _SECURE_SCL à 0, ça marche...
Il suffit que la version choisie paraisse marcher. Soit que l'editeur
fasse un autre choix et choisisse la version dans test.cpp, soit que
par hasard la fonction avec _SECURE_SCL fonctionne dans les deux cas,
soit qu'il y a un probleme de corruption ou autre que tu n'as pas
detecte sur un exemple aussi simple.Je me demande s'il serait possible de provoquer des erreurs à
l'édition des liens si l'ODR n'était pas respectée.
Je ne vois rien a faire que de subir ce que fait l'implementation pour
qui ne la controle pas. Si tu controles l'implementation, c'est
possible avec un editeur de liens suffisemment sophistique (ce qui ne
le serait pas beaucoup). Il "suffirait" d'emettre dans chaque unite
de compilation qui voit (ou utilise simplement si on veut etre moins
strict) la definition d'un type une signature de la partie
significative du type et que l'editeur de liens verifie que tous les
types ont bien la meme signature dans toutes les unites de
compilation. C'est pas complique. La partie Ada de GCC fait plus ou
moins l'equivalent pour l'Ada (ils utilisaient d'autres fichiers que
les .o la derniere fois que j'ai regarde, mais rien n'empeche de
mettre cette info dans les .o et il me semble que la doc faisait
allusion a cette possibilite).
Pour info, j'ai eu ce problème "à l'insu de mon plein grès"
avec std::vector sous VC++ 8 en définissant _SECURE_SCL à 0
dans 1 fichier de mon projet pour éviter d'avoir des
warnings (ailleurs que dans std::vector). Sauf que ça
modifie pas mal de choses dans std::vector aussi, et voilà.
_SECURE_SCL, ça ne serait pas un peu comme _GLIBCXX_DEBUG
sous G++ ? C'était exactement mon problème, sauf que ça
linkait bien. (Dans mon cas, c'était une erreur dans le
fichier de make. J'étais convaincu que je me servais des
bibliothèques de debug, alors que ce n'était pas le cas.
J'avais un vecteur qui était créer dans la bibliothèque,
mais dont je me servais des itérateurs dans une fonction
templatée instantiée dans main.)
Dans mon cas c'est un peu plus pervers : c'est la fonctions
inline qui manipulait un vector (std::max_element) renvoyé par
une fonction non inline qui provoquait le problème.
En régle générale : il faut que toutes les unités de
compilation soient compilées avec exactement les mêmes
options, y compris les mêmes -D. Et que tu ne définisses
jamais de macro dans ton code avant d'inclure les en-têtes,
et que les en-têtes aussi ne définissent que les macros qui
les concernent.
J'ai enfreint ce principe dans un seul fichier de tout le
projet, et voilà :-)
Pour info, j'ai eu ce problème "à l'insu de mon plein grès"
avec std::vector sous VC++ 8 en définissant _SECURE_SCL à 0
dans 1 fichier de mon projet pour éviter d'avoir des
warnings (ailleurs que dans std::vector). Sauf que ça
modifie pas mal de choses dans std::vector aussi, et voilà.
_SECURE_SCL, ça ne serait pas un peu comme _GLIBCXX_DEBUG
sous G++ ? C'était exactement mon problème, sauf que ça
linkait bien. (Dans mon cas, c'était une erreur dans le
fichier de make. J'étais convaincu que je me servais des
bibliothèques de debug, alors que ce n'était pas le cas.
J'avais un vecteur qui était créer dans la bibliothèque,
mais dont je me servais des itérateurs dans une fonction
templatée instantiée dans main.)
Dans mon cas c'est un peu plus pervers : c'est la fonctions
inline qui manipulait un vector (std::max_element) renvoyé par
une fonction non inline qui provoquait le problème.
En régle générale : il faut que toutes les unités de
compilation soient compilées avec exactement les mêmes
options, y compris les mêmes -D. Et que tu ne définisses
jamais de macro dans ton code avant d'inclure les en-têtes,
et que les en-têtes aussi ne définissent que les macros qui
les concernent.
J'ai enfreint ce principe dans un seul fichier de tout le
projet, et voilà :-)
Pour info, j'ai eu ce problème "à l'insu de mon plein grès"
avec std::vector sous VC++ 8 en définissant _SECURE_SCL à 0
dans 1 fichier de mon projet pour éviter d'avoir des
warnings (ailleurs que dans std::vector). Sauf que ça
modifie pas mal de choses dans std::vector aussi, et voilà.
_SECURE_SCL, ça ne serait pas un peu comme _GLIBCXX_DEBUG
sous G++ ? C'était exactement mon problème, sauf que ça
linkait bien. (Dans mon cas, c'était une erreur dans le
fichier de make. J'étais convaincu que je me servais des
bibliothèques de debug, alors que ce n'était pas le cas.
J'avais un vecteur qui était créer dans la bibliothèque,
mais dont je me servais des itérateurs dans une fonction
templatée instantiée dans main.)
Dans mon cas c'est un peu plus pervers : c'est la fonctions
inline qui manipulait un vector (std::max_element) renvoyé par
une fonction non inline qui provoquait le problème.
En régle générale : il faut que toutes les unités de
compilation soient compilées avec exactement les mêmes
options, y compris les mêmes -D. Et que tu ne définisses
jamais de macro dans ton code avant d'inclure les en-têtes,
et que les en-têtes aussi ne définissent que les macros qui
les concernent.
J'ai enfreint ce principe dans un seul fichier de tout le
projet, et voilà :-)
Dans mon cas c'est un peu plus pervers : c'est la fonctions
inline qui manipulait un vector (std::max_element) renvoyé par
une fonction non inline qui provoquait le problème.
Ce qui revient au même. L'important, c'est que l'instance de
vector s'est fait (allocation et appel du constructeur) dans une
contexte sans le debug, et l'utilisation dans une contexte avec.
En gros, le but de _GLIBCXX_DEBUG (et, je suppose, de
_SECURE_SCL), c'est de détecter des erreurs du genre utilisation
d'un itérateur invalid. Quand tu effectues une opération qui
peut invalider un itérateur (comme par exemple push_back sur un
vector), tous les itérateurs sont marqués invalides, et si on
essaie d'utiliser un itérateur marqué invalide, tu as une erreur
claire et nette (et immédiate). Pour pouvoir marquer les
itérateurs, en revanche, il faut bien qu'on puisse les trouver à
partir du vector -- dans l'implémentation g++, les itérateurs se
trouvent tous dans une liste chaîné dont la racine se trouve
dans le vecteur. Si le vecteur est construit dans une contexte
sans _GLIBCXX_DEBUG, il ne contient pas cette racine, et si on
construit ou copie un itérateur dans une contexte avec
_GLIBCXX_DEBUG, on déréférence des pointeurs dans cette racine
qui n'y est pas (et qui donc n'a pas été initialisée).
Dans mon cas c'est un peu plus pervers : c'est la fonctions
inline qui manipulait un vector (std::max_element) renvoyé par
une fonction non inline qui provoquait le problème.
Ce qui revient au même. L'important, c'est que l'instance de
vector s'est fait (allocation et appel du constructeur) dans une
contexte sans le debug, et l'utilisation dans une contexte avec.
En gros, le but de _GLIBCXX_DEBUG (et, je suppose, de
_SECURE_SCL), c'est de détecter des erreurs du genre utilisation
d'un itérateur invalid. Quand tu effectues une opération qui
peut invalider un itérateur (comme par exemple push_back sur un
vector), tous les itérateurs sont marqués invalides, et si on
essaie d'utiliser un itérateur marqué invalide, tu as une erreur
claire et nette (et immédiate). Pour pouvoir marquer les
itérateurs, en revanche, il faut bien qu'on puisse les trouver à
partir du vector -- dans l'implémentation g++, les itérateurs se
trouvent tous dans une liste chaîné dont la racine se trouve
dans le vecteur. Si le vecteur est construit dans une contexte
sans _GLIBCXX_DEBUG, il ne contient pas cette racine, et si on
construit ou copie un itérateur dans une contexte avec
_GLIBCXX_DEBUG, on déréférence des pointeurs dans cette racine
qui n'y est pas (et qui donc n'a pas été initialisée).
Dans mon cas c'est un peu plus pervers : c'est la fonctions
inline qui manipulait un vector (std::max_element) renvoyé par
une fonction non inline qui provoquait le problème.
Ce qui revient au même. L'important, c'est que l'instance de
vector s'est fait (allocation et appel du constructeur) dans une
contexte sans le debug, et l'utilisation dans une contexte avec.
En gros, le but de _GLIBCXX_DEBUG (et, je suppose, de
_SECURE_SCL), c'est de détecter des erreurs du genre utilisation
d'un itérateur invalid. Quand tu effectues une opération qui
peut invalider un itérateur (comme par exemple push_back sur un
vector), tous les itérateurs sont marqués invalides, et si on
essaie d'utiliser un itérateur marqué invalide, tu as une erreur
claire et nette (et immédiate). Pour pouvoir marquer les
itérateurs, en revanche, il faut bien qu'on puisse les trouver à
partir du vector -- dans l'implémentation g++, les itérateurs se
trouvent tous dans une liste chaîné dont la racine se trouve
dans le vecteur. Si le vecteur est construit dans une contexte
sans _GLIBCXX_DEBUG, il ne contient pas cette racine, et si on
construit ou copie un itérateur dans une contexte avec
_GLIBCXX_DEBUG, on déréférence des pointeurs dans cette racine
qui n'y est pas (et qui donc n'a pas été initialisée).