Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

inline de fonctions hors classe

13 réponses
Avatar
meow
Hello,

jusqu'=E0 aujourd'hui je pensais naivement qu'une fonction inlin=E9e
avait l'amabilit=E9 d'etre inlin=E9e lorsque le compilo le pouvait, et
non inlin=E9e autrement... mais il semblerait qu'il n'en soit rien, mon
compilo semble ne pas g=E9n=E9rer le code des fonctions inlin=E9es : leur
nom n'existe pas dans le .o

je m'esplique :
Je pensais :

fichier 1.C
inline int a(){code de a}
b(){ code de b dans lequel on appelle a() }

le fichier 1.h contiendrait les signatures de a() et de b(), le .o
contiendrait le code de a()
et celui de b() dans lequel l'appel =E0 a() serait remplac=E9 par le code
de a()


fichier 2.C
#include "fichier1.h"
c(){ code de b dans lequel on appelle a() }

le code de c() fait un appel =E0 a()


je crois compendre :
b() contient le code de a(), mais le .o ne contient pas de fonction
a(), du coup c() n'a que ses yeux pour pleurer...
C'est y bien comme =E7a que =E7a se passe ?

Je viens de coder une flopp=E9e de petites fonctions de calculs de
volumes et d'aires d'objets g=E9om=E9triques, un v=E9ritable plat de
spaghettis (enfin, c'est un arbre, y'a pas de cycle dans les appels)
d'appels de ces fonctions entre elle... Du coup, pour gagner en temps
je pensais les inliner... Mais du coup =E7a marche plus trop :D
J'ai pens=E9 =E0 un "workaround" simpliste :
a chaque fonction inlin=E9e ai() j'associerai une non inlin=E9e
bi() { return ai(); }
dont je fournirai la signature dans le .h
C'est valable ?

10 réponses

1 2
Avatar
Julien Lamy
Hello,

jusqu'à aujourd'hui je pensais naivement qu'une fonction inlinée
avait l'amabilité d'etre inlinée lorsque le compilo le pouvait, et
non inlinée autrement... mais il semblerait qu'il n'en soit rien, mon
compilo semble ne pas générer le code des fonctions inlinées : leur
nom n'existe pas dans le .o

je m'esplique :
Je pensais :

fichier 1.C
inline int a(){code de a}
b(){ code de b dans lequel on appelle a() }

le fichier 1.h contiendrait les signatures de a() et de b(), le .o
contiendrait le code de a()
et celui de b() dans lequel l'appel à a() serait remplacé par le code
de a()


fichier 2.C
#include "fichier1.h"
c(){ code de b dans lequel on appelle a() }

le code de c() fait un appel à a()


je crois compendre :
b() contient le code de a(), mais le .o ne contient pas de fonction
a(), du coup c() n'a que ses yeux pour pleurer...
C'est y bien comme ça que ça se passe ?


Le standard dit (3.2-3) : "an inlined function shall be defined in every
translation unit in which it is used". Donc oui, c() n'a que ses yeux
pour pleurer vu qu'il n'a pas la définition de a.


Je viens de coder une floppée de petites fonctions de calculs de
volumes et d'aires d'objets géométriques, un véritable plat de
spaghettis (enfin, c'est un arbre, y'a pas de cycle dans les appels)
d'appels de ces fonctions entre elle... Du coup, pour gagner en temps
je pensais les inliner... Mais du coup ça marche plus trop :D


Si tu n'as pas encore fait de profiling montrant que ces fonctions
prennent trop de temps, c'est à mon avis de l'optimisation prématurée.

J'ai pensé à un "workaround" simpliste :
a chaque fonction inlinée ai() j'associerai une non inlinée
bi() { return ai(); }
dont je fournirai la signature dans le .h
C'est valable ?



Si tu gardes l'inlining, il y a plus simple : tu mets le code de tes
petites fonctions dans le .h, et c'est fini.
--
Julien

Avatar
Sylvain Togni
Hello,

jusqu'à aujourd'hui je pensais naivement qu'une fonction inlinée
avait l'amabilité d'etre inlinée lorsque le compilo le pouvait, et
non inlinée autrement... mais il semblerait qu'il n'en soit rien, mon
compilo semble ne pas générer le code des fonctions inlinées : leur
nom n'existe pas dans le .o


Pas tout à fait. Une fonction déclarée inline est réellement inlinée
si le compilateur le peut et s'il le veut bien, mais rien ne l'y
oblige. En particulier, si il trouve que la fonction est trop grosse
et appelée trop souvent, il risque de ne pas l'inliner.

mais il semblerait qu'il n'en soit rien, mon
compilo semble ne pas générer le code des fonctions inlinées : leur
nom n'existe pas dans le .o

je m'esplique :
Je pensais :

fichier 1.C
inline int a(){code de a}
b(){ code de b dans lequel on appelle a() }

le fichier 1.h contiendrait les signatures de a() et de b(), le .o
contiendrait le code de a()
et celui de b() dans lequel l'appel à a() serait remplacé par le code
de a()

fichier 2.C
#include "fichier1.h"
c(){ code de b dans lequel on appelle a() }

le code de c() fait un appel à a()


Le mot clé inline doit être mis au niveau de la déclaration
(le .h), pas dans le .C, et la définition de la fonction doit
être présente dans toutes les unités de compilations (autrement
dit il faut la mettre aussi dans le .h).

--
Sylvain Togni

Avatar
kanze
Sylvain Togni wrote:

jusqu'à aujourd'hui je pensais naivement qu'une fonction
inlinée avait l'amabilité d'etre inlinée lorsque le
compilo le pouvait, et non inlinée autrement... mais il
semblerait qu'il n'en soit rien, mon compilo semble ne pas
générer le code des fonctions inlinées : leur nom n'existe
pas dans le .o


Pas tout à fait. Une fonction déclarée inline est réellement
inlinée si le compilateur le peut et s'il le veut bien, mais
rien ne l'y oblige. En particulier, si il trouve que la
fonction est trop grosse et appelée trop souvent, il risque de
ne pas l'inliner.


Et à l'inverse, le compilateur peut générer le code inline pour
n'importe quelle fonction, qu'elle soit déclarée inline ou non.
(Ce que fait en fait g++, et sans doute pas mal d'autres
compilateurs aussi.)

mais il semblerait qu'il n'en soit rien, mon compilo semble
ne pas générer le code des fonctions inlinées : leur nom
n'existe pas dans le .o

je m'esplique :
Je pensais :

fichier 1.C
inline int a(){code de a}
b(){ code de b dans lequel on appelle a() }

le fichier 1.h contiendrait les signatures de a() et de b(), le .o
contiendrait le code de a()
et celui de b() dans lequel l'appel à a() serait remplacé par le co de
de a()

fichier 2.C
#include "fichier1.h"
c(){ code de b dans lequel on appelle a() }

le code de c() fait un appel à a()


Le mot clé inline doit être mis au niveau de la déclaration
(le .h), pas dans le .C, et la définition de la fonction doit
être présente dans toutes les unités de compilations
(autrement dit il faut la mettre aussi dans le .h).


La clé de l'histoire, c'est 1) la définition de la fonction doit
être présente partout ou elle sert, et 2) si la fonction est
déclarée inline dans une module, elle doit l'être dans toutes.
S'il y a plusieurs déclarations de la fonction (dont la
définition) visibles, il suffit d'un inline sur n'importe
laquelle pour que ça marche. Dans le cas des fonctions membre,
par exemple, je fais prèsqu'exclusivement :

class MaClasse
{
public:
void f() ;
// ...
} ;


inline void
MaClasse::f()
{
//
}

Ça m'arrive de faire pareil avec des fonctions libres :

extern void g() ;

inline void
g()
{
// ...
}

Dans les deux cas, les définitions sont d'habitude dans un
fichier à part, include par le .hh. Simplement parce que je le
trouve plus propre comme ça. (Je fais pareil avec les templates,
dont les définitions doivent être présentes dans toutes les
unités de compilation aussi. Dans la pratique, en tout cas --
selon la norme, il suffit de les déclarer export, et ce n'est
plus nécessaire.)

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

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


Avatar
meow
Merci à tous pour vos réponses. Je retiens donc que :
- La norme dit qu'une fonction inlinée doit etre *définie* partout
où elle est utilisée, donc on aura tendance à les placer dans des .h
- le mot clé inline porte sur la *déclaration* de la fonction, et
d'après Kanze, sur nimporte laquelle de ces déclarations.

Juste pour etre certain de bien comprendre le terme : "translation
unit" ~ unité de traduction ~ nimporte quel machin qui va générer un
.o, par exemple un .c avec tous ses includes mis bout à bout.

@Kanze

extern void g() ;
euh... il sert vraiment à quelque chose le "extern" ? Je veux dire :

extern int x;
ok, ça sert à proteger la valeur de x : on précise que ce x là est
défini ailleur et qu'on l'aura à l'édition de lien, sans quoi le
compilo aurait la mauvaise idée de prendre cette déclaration pour une
définition d'un x local et de valeur 0... Mais dans le cas d'une
fonction ??? O_o

Dans la pratique, en tout cas -- selon la norme, il suffit de les décla rer export, et ce n'est
plus nécessaire.)
export... Je ne suis pas certain de comprendre la sémantique de ce mot

clé... Visiblement ça sert à signifier que le modèle (template) sur
lequel porte export est rendu accessible à toute autre unité de
traduction...
Si je comprends bien, ça me permet entre autre, de "masquer" la
définition de fonctions (ou de classes ?) templates en les faisant
dans un .C au lieu d'un .h ...?

Avatar
Julien Lamy
Merci à tous pour vos réponses. Je retiens donc que :
- La norme dit qu'une fonction inlinée doit etre *définie* partout
où elle est utilisée, donc on aura tendance à les placer dans des .h
- le mot clé inline porte sur la *déclaration* de la fonction, et
d'après Kanze, sur nimporte laquelle de ces déclarations.

Juste pour etre certain de bien comprendre le terme : "translation
unit" ~ unité de traduction ~ nimporte quel machin qui va générer un
.o, par exemple un .c avec tous ses includes mis bout à bout.


Plus precisément, c'est le resultat du préprocesseur. Standard, 2.1 :
A source file together with all the headers and source files included
via the preprocessing directive include less any source lines skipped by
any of the conditional inclusion preprocessing directives, is called a
/translation unit/.

Dans la pratique, en tout cas -- selon la norme, il suffit de les déclarer export, et ce n'est
plus nécessaire.)
export... Je ne suis pas certain de comprendre la sémantique de ce mot

clé... Visiblement ça sert à signifier que le modèle (template) sur
lequel porte export est rendu accessible à toute autre unité de
traduction...
Si je comprends bien, ça me permet entre autre, de "masquer" la
définition de fonctions (ou de classes ?) templates en les faisant
dans un .C au lieu d'un .h ...?



Oui, si j'ai aussi bien compris. Peu de compilateurs l'implantent (à ma
connaissance, Comeau est le seul), et je crois bien que le comité
parlait de le supprimer dans la prochaine version du langage.


Avatar
Arnaud Meurgues
Julien Lamy wrote:

Oui, si j'ai aussi bien compris. Peu de compilateurs l'implantent (à ma
connaissance, Comeau est le seul), et je crois bien que le comité
parlait de le supprimer dans la prochaine version du langage.


En fait, c'est surtout Microsoft qui voudrait que cela ne fasse plus
partie de la norme afin de ne pas avoir à l'implémenter.
Il est évident que les gens de Comeau ne sont pas d'accord, eux qui se
sont donné la peine de l'implémenter.

Par ailleurs, il y a beaucoup de demande de la part des utilisateurs
pour une fonctionnalité de ce type, permettant de ne pas livrer le code
source des templates. Il serait donc fort dommage que cela disparaisse
de la norme, surtout pour les mauvaises raisons invoquées par Microsoft
(raisons de faisabilité, je crois, niées par Comeau qui est un peu mieux
placé pour en parler).

--
Arnaud

Avatar
Julien Lamy
Julien Lamy wrote:

Oui, si j'ai aussi bien compris. Peu de compilateurs l'implantent (à ma
connaissance, Comeau est le seul), et je crois bien que le comité
parlait de le supprimer dans la prochaine version du langage.


En fait, c'est surtout Microsoft qui voudrait que cela ne fasse plus
partie de la norme afin de ne pas avoir à l'implémenter.
Il est évident que les gens de Comeau ne sont pas d'accord, eux qui se
sont donné la peine de l'implémenter.

Par ailleurs, il y a beaucoup de demande de la part des utilisateurs
pour une fonctionnalité de ce type, permettant de ne pas livrer le code
source des templates. Il serait donc fort dommage que cela disparaisse
de la norme, surtout pour les mauvaises raisons invoquées par Microsoft
(raisons de faisabilité, je crois, niées par Comeau qui est un peu mieux
placé pour en parler).



Je me référais à cette note
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1426.pdf

Certes, elle provient de Sutter bossant actuellement chez Microsoft,
mais les remarques en page 3 des gens de EDG (qui ont implanté export)
semblent aller vers la suppression. Y'a plus qu'à attendre la prochaine
version du standard, ou l'avis éclairé de quelqu'un fréquentant de près
le comité.


Avatar
Fabien LE LEZ
On Fri, 28 Jul 2006 10:34:25 -0400, Julien Lamy :

Certes, elle provient de Sutter bossant actuellement chez Microsoft,


Il me semble avoir entendu dire que Sutter a raconté pas mal de
conneries à propos d'export.
Si j'ai bien compris, quand Comeau a commencé à essayer d'implémenter
export, il a eu un peu de mal, et a fait part de ses doutes à Sutter.
Entre-temps, Comeau s'est aperçu que ses craintes étaient infondées,
et que export est bien implémentable, et fonctionne bien une fois
implémenté. Mais Sutter ne s'est pas mis à la page, et continue à
clamer que export est un très mauvaise idée, qui ne marchera jamais.

Note que je ne fais que répéter ce que j'ai entendu ici ou là
(principalement ici, d'ailleurs).

Je ne sais pas bien quel est le niveau d'indépendance entre Sutter et
Microsoft[*] ; quelqu'un pourrait-il me renseigner ?


[*] Dans les deux sens, d'ailleurs : la politique commerciale de MS
peut influer sur les propos de Sutter, mais l'avis (éventuellement
erroné) de Sutter peut influer sur les décisions de MS.

Avatar
Jean-Marc Bourguet
Julien Lamy writes:

Julien Lamy wrote:

Oui, si j'ai aussi bien compris. Peu de compilateurs l'implantent (à ma
connaissance, Comeau est le seul), et je crois bien que le comité
parlait de le supprimer dans la prochaine version du langage.
En fait, c'est surtout Microsoft qui voudrait que cela ne fasse plus

partie de la norme afin de ne pas avoir à l'implémenter.
Il est évident que les gens de Comeau ne sont pas d'accord, eux qui se
sont donné la peine de l'implémenter.
Par ailleurs, il y a beaucoup de demande de la part des utilisateurs
pour une fonctionnalité de ce type, permettant de ne pas livrer le code
source des templates. Il serait donc fort dommage que cela disparaisse
de la norme, surtout pour les mauvaises raisons invoquées par Microsoft
(raisons de faisabilité, je crois, niées par Comeau qui est un peu mieux
placé pour en parler).



Je me référais à cette note
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1426.pdf

Certes, elle provient de Sutter bossant actuellement chez Microsoft, mais
les remarques en page 3 des gens de EDG (qui ont implanté export) semblent
aller vers la suppression. Y'a plus qu'à attendre la prochaine version du
standard, ou l'avis éclairé de quelqu'un fréquentant de près le comité.


Regarde les archives de ce groupe ou de c.l.c++.m. On en a
discutté pas mal (et Sutter a même participé à la discussion
sur fclc++).

Un petit rapport sur les essais que j'ai fait dans le temps
avec export en utilant como: http://www.bourguet.org/cpp/export.pdf

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org



Avatar
kanze
meow wrote:
Merci à tous pour vos réponses. Je retiens donc que :
- La norme dit qu'une fonction inlinée doit etre *définie* partout
où elle est utilisée, donc on aura tendance à les placer dans des .h
- le mot clé inline porte sur la *déclaration* de la fonction, et
d'après Kanze, sur nimporte laquelle de ces déclarations.

Juste pour etre certain de bien comprendre le terme :
"translation unit" ~ unité de traduction ~ nimporte quel
machin qui va générer un .o, par exemple un .c avec tous ses
includes mis bout à bout.


Pas tout à fait. C'est grosso modo une module. C'est le fichier
source plus tous les en-têtes qu'il inclut. C'est ce qui sert à
générer un seul fichier objet.

@Kanze

extern void g() ;


euh... il sert vraiment à quelque chose le "extern" ? Je veux dire :


Pour le compilateur, c'est implicit. Pour celui qui le lit, en
revanche, ça dit quelque chose.

extern int x;
ok, ça sert à proteger la valeur de x : on précise que ce x là est
défini ailleur et qu'on l'aura à l'édition de lien, sans quoi le
compilo aurait la mauvaise idée de prendre cette déclaration pour une
définition d'un x local et de valeur 0... Mais dans le cas d'une
fonction ??? O_o


C'est plus compliqué que ça. (Sinon, ça ne serait pas C++:-).)
Il y a en fait deux choses qui entre en jeu : le linkage, et si
la déclaration est aussi une définition. Les deux choses sont
dépend en partie de s'il y a extern (ou d'autres « storage
class specifiers », du genre static), mais qu'en partie.

Un linkage externe, ça veut dire que le nom réfère à la même
chose (fonction, variable) dans tout le programme, y compris
dans d'autres unités de traduction (à condition qu'il ait un
linkage externe là aussi). Un linkage interne signifie que le
nom réfère à la même chose à l'intérieur de l'unité de
traduction, mais n'est pas « lié » aux utilisations dans les
autres unités de traductions.

Une déclaration dit que la chose existe, une définition le crée
réelement, si on veut. À quelques exceptions près (fonctions
inline, templates, classes), il ne doit y avoir qu'une seule
définition de chaque chose dans tout le programme.

En ce qui concerne les fonctions : si c'est suivi de
« {...} », c'est une définition ; sinon, ce n'est qu'une
déclaration. si c'est déclarée « static », le nom de la
fonction a un linkage interne, sinon, c'est extern. (Je parle
ici que pour des fonctions libres. Pour les fonctions membre, la
signification de « static » est tout à fait autre, et le
linkage est toujours externe.) Qu'on met l'« extern » ou non,
donc, ne change absolument rien pour le compilateur. (Mais pour
le lecteur humain, ça dit bien que la définition se trouve
ailleurs. Chose qui est déjà dit en quelque sort par la présence
soit d'un ;, soit d'un {...}, mais je trouve le mot extern plus
lisible.)

En ce qui concerne les variables, c'est un peu plus compliqué.
D'autant plus que si la variable soit const ou non y joue aussi
un rôle. Mais grosso modo, une variable a un linkage externe si
1) elle est déclarée à une portée référentielle, et 2) elle
n'est pas const et elle n'est pas déclarée static, ou elle est
const, et elle n'est pas déclarée extern. Sinon, si elle est
déclarée à une portée référentielle, elle a un linkage interne.
Elle est une définition si elle a un initialisateur ou si elle a
un linkage externe et n'est pas déclarée extern.

Donc :
int i1 = 1 ; // définition, linkage externe
int const i2 = 2 ; // définition, linkage interne
int i3 ; // définition, linkage externe
int const i4 ; // illégale (serait une définition, et
// la définition d'un const sans
// initialisateur n'est pas permis)
extern int i5 = 5 ; // définition, linkage externe
extern int const i6 = 6 ; // définition, linkage externe
extern int i7 ; // declaration, linkage extern
extern int const i8 ; // déclaration, linkage externe

En plus, des déclarations qui ont précédées peuvent modifier ces
règles. Donc :

extern int const i9 ;
int const i9 = 9 ; // définition, linkage externe (à cause
// de la déclaration précédante)

Dans la pratique, en tout cas -- selon la norme, il suffit
de les déclarer export, et ce n'est plus nécessaire.)


export... Je ne suis pas certain de comprendre la sémantique
de ce mot clé... Visiblement ça sert à signifier que le modèle
(template) sur lequel porte export est rendu accessible à
toute autre unité de traduction...


En gros, il doit permettre la compilation séparée des templates.
Dans la pratique, il y a encore trop de compilateurs qui ne
l'implémentent pas pour qu'il soit utile.

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


1 2