OVH Cloud OVH Cloud

Point d'instanciation des templates à tiroirs

6 réponses
Avatar
Vivien Gallinaro
Je me pose la question, depuis quelques jours, de savoir où se trouve le
point d'instanciation d'un template employé dans un template ?
Exemple :

// Unité 1
// définition du template dans le tiroir

export template <class T> void dans_le_tiroir (const T&) { /*...*/ }


// Unité 2
// définition d'un template à tiroir

export template <class T> void dans_le_tiroir (const T&);

// A
export template <class T> void avec_un_tiroir (const T& t)
{
dans_le_tiroir (t);
// ...
}


// Unité 3
// utilisation du template à tiroir

export template <class T> void avec_un_tiroir (const T&);

// B
void f (int i)
{
avec_un_tiroir (i);
}


Cette question vient d'un argument, que je ne comprends pas bien,
qu'Herb Sutter utilise dans plusieurs de ses articles, et qui dit en
substance : "à l'instanciation, il arrive qu'on ait à gérer un nombre
arbitraire d'unités de compilation pour déterminer le contexte".

( Dans "Why we can't afford export" :
* "export requires [...] simultaneously handling an arbitrary number of
symbol tables from different translation units" ;

* "there are names from multiple translation units available for name
lookup" ;

* et certainement d'autres passages sur lesquels je n'arrive pas à
remettre la main, dans les articles en annexe. )

Sutter cite également ce qui serait un bout de norme, dans "Export's
restrictions, part 1" (en annexe du doc précédent, donc) :
"[Dependent] names are unbound and are looked up at the point of the
template instanciation in both the context of the template and the
context of the point of instantiation."

Ce que je comprend de ce dernier passage :

* point d'instanciation de dans_le_tiroir() --> A
contexte : unités 1 et 2

* point d'instanciation de avec_le_tiroir() --> B
contexte : unités 2 et 3

Mais les premières remarques m'ammènent à penser que ce n'est pas aussi
simple, et que soit le contexte de définition de dans_le_tiroir() est
considéré comme faisant partie du contexte de définition de
avec_le_tiroir(), soit le contexte d'instanciation de avec_le_tiroir()
est considéré comme faisant partie du contexte d'instanciation de
dans_le_tiroir().

Est-ce que j'ai faux de penser qu'il n'y a que deux unités de
compilation impliquées dans l'instanciation d'un template exporté ?
D'après ces quelques citations : "oui, carrément". Mais alors laquelle
des alternatives est "la bonne" ? Quelle est la raison profonde qui
ferait que dans_le_tiroir() dépende de f() ou l'inverse ? Et qui a fait
ce choix : la norme, EDG, ou "c'est tout simplement évident" ?

Une autre possibilité serait que je n'aie pas tort sur ce coup, ou
encore que cette phrase de Sutter n'ait rien à voir avec les templates
en cascade. Si c'est dans ce paragraphe que je chauffe, alors j'ai
vraiment besoin d'aide, parce que j'ai du mal à saisir le sens profond.

Gourgouilloult du Clapotis

6 réponses

Avatar
Gabriel Dos Reis
Vivien Gallinaro writes:

| Je me pose la question, depuis quelques jours, de savoir où se trouve
| le point d'instanciation d'un template employé dans un template ?

14.6.4.1/1

For a function template specialization, a member function template
specialization, or a specialization for a member function or static
data member of a class template, if the specialization is implicitly
instantiated because it is referenced from within another template
specialization and the context from which it is refer-enced depends
on a template parameter, the point of instantiation of the
specialization is the point of instan-tiation of the enclosing
specialization. Otherwise, the point of instantiation for such a
specialization imme-diately follows the namespace scope declaration
or definition that refers to the specialization.

[...]

| Cette question vient d'un argument, que je ne comprends pas bien,
| qu'Herb Sutter utilise dans plusieurs de ses articles, et qui dit en
| substance : "à l'instanciation, il arrive qu'on ait à gérer un nombre
| arbitraire d'unités de compilation pour déterminer le contexte".
|
| ( Dans "Why we can't afford export" :
| * "export requires [...] simultaneously handling an arbitrary number
| of symbol tables from different translation units" ;

ce n'est pas nouveau. Le linker le faisait déjà.

| * "there are names from multiple translation units available for name
| lookup" ;

tant mieux, on fera un choix cohérent.

| * et certainement d'autres passages sur lesquels je n'arrive pas à
| remettre la main, dans les articles en annexe. )
|
| Sutter cite également ce qui serait un bout de norme, dans "Export's
| restrictions, part 1" (en annexe du doc précédent, donc) :
| "[Dependent] names are unbound and are looked up at the point of the
| template instanciation in both the context of the template and the
| context of the point of instantiation."

Ceci existe aussi dans le modèle d'inclusion. Ce n'est pas quelque
chose inventé par export.

| Ce que je comprend de ce dernier passage :
|
| * point d'instanciation de dans_le_tiroir() --> A
| contexte : unités 1 et 2
|
| * point d'instanciation de avec_le_tiroir() --> B
| contexte : unités 2 et 3
|
| Mais les premières remarques m'ammènent à penser que ce n'est pas
| aussi simple, et que soit le contexte de définition de
| dans_le_tiroir() est considéré comme faisant partie du contexte de
| définition de avec_le_tiroir(),

je ne peux trouver cela nulle part dans la norme.

| soit le contexte d'instanciation de
| avec_le_tiroir() est considéré comme faisant partie du contexte
| d'instanciation de dans_le_tiroir().

Voir la citation en début du message.

-- Gaby
Avatar
Jean-Marc Bourguet
Gabriel Dos Reis writes:

| Ce que je comprend de ce dernier passage :
|
| * point d'instanciation de dans_le_tiroir() --> A
| contexte : unités 1 et 2
|
| * point d'instanciation de avec_le_tiroir() --> B
| contexte : unités 2 et 3
|
| Mais les premières remarques m'ammènent à penser que ce n'est pas
| aussi simple, et que soit le contexte de définition de
| dans_le_tiroir() est considéré comme faisant partie du contexte de
| définition de avec_le_tiroir(),

je ne peux trouver cela nulle part dans la norme.


Non, mais une recherche pour des noms dependants au point
d'instantiation dans l'unite 2 peut trouver des choses dans l'unite 1.
C'est evident dans le cas d'un type utilisateur.

// unite 3

export template<typename T1, typename T2> void ft2(T1 p1, T2 p2) {
...
}

// unite 2

export template<typename T1> void ft1(T1 p1) {
fct2(p1, 5);
}

// unite 1

namespace {
class foo {
...
};
foo makefoo();
}

...
ft1(makefoo());
...

--
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
Gabriel Dos Reis
Jean-Marc Bourguet writes:

| Gabriel Dos Reis writes:
|
| > | Ce que je comprend de ce dernier passage :
| > |
| > | * point d'instanciation de dans_le_tiroir() --> A
| > | contexte : unités 1 et 2
| > |
| > | * point d'instanciation de avec_le_tiroir() --> B
| > | contexte : unités 2 et 3
| > |
| > | Mais les premières remarques m'ammènent à penser que ce n'est pas
| > | aussi simple, et que soit le contexte de définition de
| > | dans_le_tiroir() est considéré comme faisant partie du contexte de
| > | définition de avec_le_tiroir(),
| >
| > je ne peux trouver cela nulle part dans la norme.
|
| Non, mais une recherche pour des noms dependants au point
| d'instantiation dans l'unite 2 peut trouver des choses dans l'unite 1.

cela ne veut pas dire que le contexte de définition de
dans_le_tiroir() est considérée comme faisant partie du contexte de
définition de avec_le_tiroir().

| C'est evident dans le cas d'un type utilisateur.
|
| // unite 3
|
| export template<typename T1, typename T2> void ft2(T1 p1, T2 p2) {
| ...
| }
|
| // unite 2
|
| export template<typename T1> void ft1(T1 p1) {
| fct2(p1, 5);
| }
|
| // unite 1
|
| namespace {
| class foo {
| ...
| };
| foo makefoo();
| }
|
| ...
| ft1(makefoo());

L'instantiation de ft1() va demander celle de ft2() mais le contexte
de définition de ft2() n'est pas incluse dans le contexte de
définition de ft1(). La seule chose que ft1() a besoin de savoir
c'est que ft2() est exportée.

-- Gaby
Avatar
Jean-Marc Bourguet
Gabriel Dos Reis writes:

Jean-Marc Bourguet writes:

| Gabriel Dos Reis writes:
|
| > | Ce que je comprend de ce dernier passage :
| > |
| > | * point d'instanciation de dans_le_tiroir() --> A
| > | contexte : unités 1 et 2
| > |
| > | * point d'instanciation de avec_le_tiroir() --> B
| > | contexte : unités 2 et 3
| > |
| > | Mais les premières remarques m'ammènent à penser que ce n'est pas
| > | aussi simple, et que soit le contexte de définition de
| > | dans_le_tiroir() est considéré comme faisant partie du contexte de
| > | définition de avec_le_tiroir(),
| >
| > je ne peux trouver cela nulle part dans la norme.
|
| Non, mais une recherche pour des noms dependants au point
| d'instantiation dans l'unite 2 peut trouver des choses dans l'unite 1.

cela ne veut pas dire que le contexte de définition de
dans_le_tiroir() est considérée comme faisant partie du contexte de
définition de avec_le_tiroir().

| C'est evident dans le cas d'un type utilisateur.
|
| // unite 3
|
| export template<typename T1, typename T2> void ft2(T1 p1, T2 p2) {
| ...
| }
|
| // unite 2
|
| export template<typename T1> void ft1(T1 p1) {
| fct2(p1, 5);
| }
|
| // unite 1
|
| namespace {
| class foo {
| ...
| };
| foo makefoo();
| }
|
| ...
| ft1(makefoo());

L'instantiation de ft1() va demander celle de ft2() mais le contexte
de définition de ft2() n'est pas incluse dans le contexte de
définition de ft1(). La seule chose que ft1() a besoin de savoir
c'est que ft2() est exportée.


Nous sommes bien d'accord. Tout d'abord je crois qu'il a utilise
definition pour instantiation (en tout cas la correction automatique
chez moi a fait cette substitution). Ensuite j'ai oublie d'insister
sur le fait que si l'instantiation de ft2 utilise des choses de
l'unite 1, c'est uniquement parce qu'elles sont "transportees" par un
parametre template de ft1 et que le meme type est un parametre
template de ft2.

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
Vivien Gallinaro
Gabriel Dos Reis wrote:
Vivien Gallinaro writes:

| Je me pose la question, depuis quelques jours, de savoir où se trouve
| le point d'instanciation d'un template employé dans un template ?

14.6.4.1/1 [...]


Tu cherches à me faire comprendre que je pourrais avoir intérêt à, ou
envie de travailler avec un exemplaire de la norme ?-)

Et le contexte de définition du template du milieu (avec_le_tiroir()
dans mon exemple précédent), est-il "visible" à l'instanciation du
template du fond (dans_le_tiroir()) ? A priori, je dirais que ça fait
sens, mais ce contexte du milieu s'insère à quel titre ? Contexte de
définition, ou contexte d'instanciation du template du fond ? Je
pencherais pour la deux, mais je suis pas contre une confirmation de
tout ça ;)

| * "export requires [...] simultaneously handling an arbitrary number
| of symbol tables from different translation units" ;

ce n'est pas nouveau. Le linker le faisait déjà.


Dans ce cas, pourquoi est-ce qu'il insiste autant ?
Si j'essaye de répondre, je dirais que hors template, pour la résolution
de surcharge, une unité choisi parmi les déclarations qui lui sont
faites ; une surcharge externe plus appropriée mais non déclarée serait
ignorée. Donc le compilateur sait quelle version il faudra préférer, et
le linker n'a que celle là à trouver.
Tandis que pour un appel dépendant depuis un template, on a plusieurs
unités disponibles, dans lesquelles il faut collecter toutes les
surcharges pour le nom donné, et choisir la plus appropriée. J'aurais
tendance à penser que c'est le même boulot, mis à part qu'il faut que le
linker aussi sache le faire...
De nouveau, ce n'est qu'une hypothèse. J'essaye de comprendre.

| * "there are names from multiple translation units available for name
| lookup" ;

tant mieux, on fera un choix cohérent.


Comment se fait-il que, quand tu lances un petit truc comme ça, presque
anodin, j'ai le sentiment de passer à côté de ce que tu veux dire ?-P
Ou alors c'est en rapport à ce que je disais au dessus ? (Sinon, ben je
suis effectivement passé à côté...)

| "[Dependent] names are unbound and are looked up at the point of the
| template instanciation in both the context of the template and the
| context of the point of instantiation."

Ceci existe aussi dans le modèle d'inclusion. Ce n'est pas quelque
chose inventé par export.


Oui, ok. Mais le fait est que je m'intéresse à export (comment ça "vous
aviez compris" ?-) et que dans le modèle par inclusion, c'est tout
"mélangé", donc on sent moins ce genre de subtilité.

Gourgou

Avatar
Vivien Gallinaro
Jean-Marc Bourguet wrote:

Tout d'abord je crois qu'il a utilise
definition pour instantiation (en tout cas la correction automatique
chez moi a fait cette substitution).


Où ça ? J'ai dû relire mon post une dizaine de fois sur plusieurs jours,
mais je n'ai pas le sentiment d'avoir commis une erreur aussi grosse. Tu
parles peut-être de :

"soit le contexte de définition de dans_le_tiroir() est considéré comme
faisant partie du contexte de définition de avec_le_tiroir()"

mais c'est bien ce que je voulais dire, quand bien même ça ne voudrait
rien dire. Le fait est que jusque là, j'étais persuadé que pour une
instanciation, il fallait au plus deux unités. En posant ma question,
j'ai commencé à envisager que le template du fond pourrait avoir besoin
de choses qui viendraient du "haut" de la chaîne des instanciations. Et
comme je n'étais pas sûr, j'ai essayé d'envisager d'autres raisons au
besoin de plus de deux unités.

A côté de ça, je viens effectivement de poser la question d'autres
déclinaisons de cette proposition.

Ensuite j'ai oublie d'insister
sur le fait que si l'instantiation de ft2 utilise des choses de
l'unite 1, c'est uniquement parce qu'elles sont "transportees" par un
parametre template de ft1 et que le meme type est un parametre
template de ft2.


J'ai pas dû comprendre ce que tu voulais dire, parce que je ne vois pas
l'intérêt d'insister là-dessus une fois qu'on a lu l'article de la norme
cité plus haut par Gaby.

Gourgou