J'ai pour tâche de porter quelques milliers de lignes de code C++ de
Windows (Visual Studio 2005) vers Linux (gcc 4.1.2). Je précise que
je ne suis pas vraiment un programmeur C++ : je connais surtout le C.
Apparemment, le compilateur de Windows est beaucoup plus laxiste que
gcc, même en ajoutant les flags -fpermissive -ffriend-injection à
ce dernier. Je tombe sur un problème que je pense avoir à peu près
identifié, mais j'aurais besoin d'aide pour le corriger ou le
contourner.
Voici un bout de code simplifié montrant le problème. J'espère que je
n'ai pas trop simplifié.
La fonction ContexteVide() retourne un objet de classe Contexte. Le
compilateur Windows est parfaitement satisfait avec ce code (du moins
j'espère ne pas l'avoir trop charcuté), mais gcc me dit « no matching
function for call to Foo::myString(Contexte) ».
L'analyse que je fais, c'est qu'il refuse de passer le Contexte par
référence du fait que c'est une valeur pour laquelle l'appelant n'a
justement aucune référence (elle est générée par la fonction
ContexteVide() ). D'ailleurs, j'ai un cas similaire où j'ai trouvé
deux contournements possibles :
Truc RetourneUnTruc() { ... }
Machin une_fonction(Truc & truc) { ... }
{
...
result = une_fonction(RetourneUnTruc());
...
}
Le premier contournement consiste à changer la définition de
une_fonction() en ajoutant 'const', ce qui est possible si le
paramètre truc n'est pas modifié par la fonction :
Machin une_fonction(const Truc & truc) { ... }
L'autre contournement consiste à changer l'appel en passant par
une variable intermédiaire :
Très bien ici. Mais dans le problème du début, je ne peux pas facilement
rajouter un const à la définition de Foo::myString() parce qu'elle-même
appelle une autre fonction qui en appelle une autre, etc., sans aucun
const -- même si je suis persuadé que le paramètre n'est en fait jamais
modifié. Et je ne vois pas comment passer par une variable intermédiaire
dans la liste d'initialisation du constructeur de Bar.
Que me conseillez-vous ?
Note : la réponse RTFM est parfaitement possible, si vous avez un
pointeur vers un bon « FM » à me recommander...
Le plus simple est d'utiliser un petit helper, du genre : class Bar { std::string _fooBar; static std::string the_foobar(const Foo & foo) { Contexte ctx; return foo.myString(ctx); } Bar(const Foo & foo) : _fooBar(the_foobar(foo)) {}; }; Ici the_foobar fabrique directement un std::string à partir d'un Foo. -- Alain.
Le plus simple est d'utiliser un petit helper, du genre : class Bar { std::string _fooBar; static std::string the_foobar(const Foo & foo) { Contexte ctx; return foo.myString(ctx); } Bar(const Foo & foo) : _fooBar(the_foobar(foo)) {}; }; Ici the_foobar fabrique directement un std::string à partir d'un Foo. -- Alain.
Olivier Miakinen
Bonjour Alain, Le 20/09/2016 17:50, Alain Ketterlin m'a répondu :
class Context { ... } class Foo { const std::string myString(Contexte & ctx) { ... } }
J'imagine que tu as oublié un const pour myString(), cad const std::string myString(Contexte & ctx) const { ... }
Tu as raison. J'avais donc effectivement trop simplifié.
sinon la suite (foo.myString()) ne peut pas marcher.
Oh... parce que foo est passé en const dans l'appel du constructeur de Bar ! C'est là que je vois que je vais devoir pratiquer pas mal le C++ pour avoir ce genre de réflexe, je ne l'avais pas vu.
class Bar { std::string _fooBar; Bar(const Foo & foo) : _fooBar(foo.myString(ContexteVide())) {}; } La fonction ContexteVide() retourne un objet de classe Contexte. Le compilateur Windows est parfaitement satisfait avec ce code (du moins j'espère ne pas l'avoir trop charcuté), mais gcc me dit « no matching function for call to Foo::myString(Contexte) ».
Oui, il considère que l'objet n'est pas une référence.
C'est logique... même s'il m'a fallu deux jours pour trouver quelle fonction convenait à Visual Studio, et pourquoi gcc ne la trouvait pas ! Il faut dire qu'en réalité il y a une dizaine de fonctions surchargées, avec chacune une demi-douzaine de paramètres dont des templates dans tous les sens.
[...]
Très bien ici. Mais dans le problème du début, je ne peux pas facilement rajouter un const à la définition de Foo::myString() parce qu'elle-même appelle une autre fonction qui en appelle une autre, etc., sans aucun const -- même si je suis persuadé que le paramètre n'est en fait jamais modifié.
A terme c'est surement la meilleure chose à faire, si c'est possible.
Je suis bien d'accord que ce serait la meilleure chose à faire, mais malheureusement ce n'est pas possible dans le temps dont je dispose. Je me vois déjà difficilement vérifier que mon hypothèse soit toujours vraie (à savoir qu'il n'est vraiment jamais modifié) car l'arbre d'héritage me semble assez touffu...
Et je ne vois pas comment passer par une variable intermédiaire dans la liste d'initialisation du constructeur de Bar.
Le plus simple est d'utiliser un petit helper, du genre : class Bar { std::string _fooBar; static std::string the_foobar(const Foo & foo) { Contexte ctx;
Contexte ctx = ContexteVide(); // je suppose
return foo.myString(ctx); } Bar(const Foo & foo) : _fooBar(the_foobar(foo)) {}; }; Ici the_foobar fabrique directement un std::string à partir d'un Foo.
Ça me semble une excellente méthode, qui ne modifie qu'un seul fichier et pas des dizaines. Merci beaucoup ! -- Olivier Miakinen
Bonjour Alain,
Le 20/09/2016 17:50, Alain Ketterlin m'a répondu :
Tu as raison. J'avais donc effectivement trop simplifié.
sinon la suite (foo.myString()) ne peut pas marcher.
Oh... parce que foo est passé en const dans l'appel du constructeur
de Bar ! C'est là que je vois que je vais devoir pratiquer pas mal
le C++ pour avoir ce genre de réflexe, je ne l'avais pas vu.
La fonction ContexteVide() retourne un objet de classe Contexte. Le
compilateur Windows est parfaitement satisfait avec ce code (du moins
j'espère ne pas l'avoir trop charcuté), mais gcc me dit « no matching
function for call to Foo::myString(Contexte) ».
Oui, il considère que l'objet n'est pas une référence.
C'est logique... même s'il m'a fallu deux jours pour trouver quelle
fonction convenait à Visual Studio, et pourquoi gcc ne la trouvait
pas ! Il faut dire qu'en réalité il y a une dizaine de fonctions
surchargées, avec chacune une demi-douzaine de paramètres dont des
templates dans tous les sens.
[...]
Très bien ici. Mais dans le problème du début, je ne peux pas facilement
rajouter un const à la définition de Foo::myString() parce qu'elle-même
appelle une autre fonction qui en appelle une autre, etc., sans aucun
const -- même si je suis persuadé que le paramètre n'est en fait jamais
modifié.
A terme c'est surement la meilleure chose à faire, si c'est possible.
Je suis bien d'accord que ce serait la meilleure chose à faire, mais
malheureusement ce n'est pas possible dans le temps dont je dispose.
Je me vois déjà difficilement vérifier que mon hypothèse soit toujours
vraie (à savoir qu'il n'est vraiment jamais modifié) car l'arbre
d'héritage me semble assez touffu...
Et je ne vois pas comment passer par une variable intermédiaire dans
la liste d'initialisation du constructeur de Bar.
Le plus simple est d'utiliser un petit helper, du genre :
Bonjour Alain, Le 20/09/2016 17:50, Alain Ketterlin m'a répondu :
class Context { ... } class Foo { const std::string myString(Contexte & ctx) { ... } }
J'imagine que tu as oublié un const pour myString(), cad const std::string myString(Contexte & ctx) const { ... }
Tu as raison. J'avais donc effectivement trop simplifié.
sinon la suite (foo.myString()) ne peut pas marcher.
Oh... parce que foo est passé en const dans l'appel du constructeur de Bar ! C'est là que je vois que je vais devoir pratiquer pas mal le C++ pour avoir ce genre de réflexe, je ne l'avais pas vu.
class Bar { std::string _fooBar; Bar(const Foo & foo) : _fooBar(foo.myString(ContexteVide())) {}; } La fonction ContexteVide() retourne un objet de classe Contexte. Le compilateur Windows est parfaitement satisfait avec ce code (du moins j'espère ne pas l'avoir trop charcuté), mais gcc me dit « no matching function for call to Foo::myString(Contexte) ».
Oui, il considère que l'objet n'est pas une référence.
C'est logique... même s'il m'a fallu deux jours pour trouver quelle fonction convenait à Visual Studio, et pourquoi gcc ne la trouvait pas ! Il faut dire qu'en réalité il y a une dizaine de fonctions surchargées, avec chacune une demi-douzaine de paramètres dont des templates dans tous les sens.
[...]
Très bien ici. Mais dans le problème du début, je ne peux pas facilement rajouter un const à la définition de Foo::myString() parce qu'elle-même appelle une autre fonction qui en appelle une autre, etc., sans aucun const -- même si je suis persuadé que le paramètre n'est en fait jamais modifié.
A terme c'est surement la meilleure chose à faire, si c'est possible.
Je suis bien d'accord que ce serait la meilleure chose à faire, mais malheureusement ce n'est pas possible dans le temps dont je dispose. Je me vois déjà difficilement vérifier que mon hypothèse soit toujours vraie (à savoir qu'il n'est vraiment jamais modifié) car l'arbre d'héritage me semble assez touffu...
Et je ne vois pas comment passer par une variable intermédiaire dans la liste d'initialisation du constructeur de Bar.
Le plus simple est d'utiliser un petit helper, du genre : class Bar { std::string _fooBar; static std::string the_foobar(const Foo & foo) { Contexte ctx;
Contexte ctx = ContexteVide(); // je suppose
return foo.myString(ctx); } Bar(const Foo & foo) : _fooBar(the_foobar(foo)) {}; }; Ici the_foobar fabrique directement un std::string à partir d'un Foo.
Ça me semble une excellente méthode, qui ne modifie qu'un seul fichier et pas des dizaines. Merci beaucoup ! -- Olivier Miakinen
allenrobin
Le mardi 20 Septembre 2016 à 17:50 par Alain Ketterlin :
Olivier Miakinen
class Context { ... } class Foo { const std::string myString(Contexte & ctx) { ... } }
Le plus simple est d'utiliser un petit helper, du genre : class Bar { std::string _fooBar; static std::string the_foobar(const Foo & foo) { Contexte ctx; return foo.myString(ctx); } Bar(const Foo & foo) : _fooBar(the_foobar(foo)) {}; }; Ici the_foobar fabrique directement un std::string à partir d'un Foo. -- Alain.
I was also facing such kind of problem in C++ coding platform. Actually, there was mainly a matching issue in such case it can't find or trace the actual passage argument. You may get the solution from https://chathelp.org/epson-printer-support/ to solve this problem.
Le plus simple est d'utiliser un petit helper, du genre : class Bar { std::string _fooBar; static std::string the_foobar(const Foo & foo) { Contexte ctx; return foo.myString(ctx); } Bar(const Foo & foo) : _fooBar(the_foobar(foo)) {}; }; Ici the_foobar fabrique directement un std::string à partir d'un Foo. -- Alain.
I was also facing such kind of problem in C++ coding platform. Actually, there was mainly a matching issue in such case it can't find or trace the actual passage argument. You may get the solution from https://chathelp.org/epson-printer-support/ to solve this problem.