Connecting Tech Pros Worldwide Forums | Help | Site Map

context in template point of instantiation (g++)

carlos.urena.almagro@gmail.com
Guest
 
Posts: n/a
#1: Jan 16 '07
Hello,


I'm sorry if this post raises a question with obvious answer, or if it
has been already posted and solved. I was wondering about how the
compiler (in my case, gcc 4.0.2 on linux) handles templates
instantation, and what the standard says about that. I created a
"difficult" case, in which the meaning of a call to a function (which
is dependant) depends on the context where a template is instantiated.
I have three compilation units and a header with a template, and, to my
surprise, the output depends on....the order in which compilation units
are compiled and linked ! --- I have two overloaded versions of 'f' in
two compilation units, but the compiler issues calls to either one
depending on the order of compilation or linking. It is correct w.r.t
the standard ? it is a bug in gcc ? -- I think this is related with the
mecanism for creating single template instantiation, which can be donde
at link time, but I'm not sure about the details.

'make' creates two binaries 'a1' and 'a2', with different output, but
'a1' and 'a2' are equal except for the order compilation units are
given to 'g++'. I've included the files in the text (including
makefile)

Thanks a lot in advance for any answers,
Carlos.

file t.H
---------
template<typename TT Applyf( T a )
{ return f(a) ; }


file t1.C
-----------
#include <iostream>
#include "t.H"

using namespace std ;

double f( double x )
{
return x*x ;
}

void g1()
{
cout << Applyf(1.0) << endl ;
}

file t2.C
-----------
#include <iostream>
#include "t.H"

using namespace std ;

double f( float x )
{
return x+x ;
}

void g2()
{
cout << Applyf(1.0) << endl ;
}

file tmain.C
-----------------

void g1() ;
void g2() ;

int main( int argc, char * argv[] )
{
g1() ;
g2();
}


file makefile:
------------------
x: a1 a2
./a1
./a2
a1:
g++ -o a1 t1.C t2.C tmain.C

a2:
g++ -o a2 t2.C t1.C tmain.C


Ondra Holub
Guest
 
Posts: n/a
#2: Jan 16 '07

re: context in template point of instantiation (g++)



carlos.urena.almagro@gmail.com napsal:
Quote:
Hello,
>
>
I'm sorry if this post raises a question with obvious answer, or if it
has been already posted and solved. I was wondering about how the
compiler (in my case, gcc 4.0.2 on linux) handles templates
instantation, and what the standard says about that. I created a
"difficult" case, in which the meaning of a call to a function (which
is dependant) depends on the context where a template is instantiated.
I have three compilation units and a header with a template, and, to my
surprise, the output depends on....the order in which compilation units
are compiled and linked ! --- I have two overloaded versions of 'f' in
two compilation units, but the compiler issues calls to either one
depending on the order of compilation or linking. It is correct w.r.t
the standard ? it is a bug in gcc ? -- I think this is related with the
mecanism for creating single template instantiation, which can be donde
at link time, but I'm not sure about the details.
>
'make' creates two binaries 'a1' and 'a2', with different output, but
'a1' and 'a2' are equal except for the order compilation units are
given to 'g++'. I've included the files in the text (including
makefile)
>
Thanks a lot in advance for any answers,
Carlos.
>
file t.H
---------
template<typename TT Applyf( T a )
{ return f(a) ; }
>
>
file t1.C
-----------
#include <iostream>
#include "t.H"
>
using namespace std ;
>
double f( double x )
{
return x*x ;
}
>
void g1()
{
cout << Applyf(1.0) << endl ;
}
>
file t2.C
-----------
#include <iostream>
#include "t.H"
>
using namespace std ;
>
double f( float x )
{
return x+x ;
}
>
void g2()
{
cout << Applyf(1.0) << endl ;
}
>
file tmain.C
-----------------
>
void g1() ;
void g2() ;
>
int main( int argc, char * argv[] )
{
g1() ;
g2();
}
>
>
file makefile:
------------------
x: a1 a2
./a1
./a2
a1:
g++ -o a1 t1.C t2.C tmain.C
>
a2:
g++ -o a2 t2.C t1.C tmain.C
In
void g2()
{
cout << Applyf(1.0) << endl ;
}

Is called Applyf<doubleand it calls f(double). You have to change
Applyf(1.0) to Applyf(1.0f) to call Applyf<float>.

Anyway I do not know, why f(double) is found in g2.C compilation unit.

carlos.urena.almagro@gmail.com
Guest
 
Posts: n/a
#3: Jan 16 '07

re: context in template point of instantiation (g++)



Ondra Holub wrote:
Quote:
carlos.urena.almagro@gmail.com napsal:
Quote:
Hello,
[............]
>
Is called Applyf<doubleand it calls f(double). You have to change
Applyf(1.0) to Applyf(1.0f) to call Applyf<float>.
>
Anyway I do not know, why f(double) is found in g2.C compilation unit.
I know you can easily direct the compiler to any version of 'f' you
wish, but.... the problem is that the criteria it uses (when you do not
that and it has to choose) seems to be rather unexpected.

Greg
Guest
 
Posts: n/a
#4: Jan 16 '07

re: context in template point of instantiation (g++)


carlos.urena.almagro@gmail.com wrote:
Quote:
Ondra Holub wrote:
Quote:
carlos.urena.almagro@gmail.com napsal:
Quote:
Hello,
[............]
Is called Applyf<doubleand it calls f(double). You have to change
Applyf(1.0) to Applyf(1.0f) to call Applyf<float>.

Anyway I do not know, why f(double) is found in g2.C compilation unit.
>
I know you can easily direct the compiler to any version of 'f' you
wish, but.... the problem is that the criteria it uses (when you do not
that and it has to choose) seems to be rather unexpected.
Not really. The program can have only one, consistent Applyf<double>()
specialization - yet this program has created two Applyf<double>()
specialiations that differ from each other: one calls f(double) and the
other one calls f(float). Technically these two specializations for
Applyf<doubleviolate C++'s "One Definition Rule" (ODR). So the
behavior of the program is unexpected because it is undefined. As a
practical matter, which version of Applyf<doublewinds up in the
eventual build is dependent on factors that no build should depend on.

The fix should be straightfoward. A template function should call only
those functions that are "visible", that is, that have been declared
(they need not be defined) before the function template itself.


Greg

carlos.urena.almagro@gmail.com
Guest
 
Posts: n/a
#5: Jan 16 '07

re: context in template point of instantiation (g++)



Greg wrote:
Quote:
carlos.urena.almagro@gmail.com wrote:
Quote:
Ondra Holub wrote:
Quote:
carlos.urena.almagro@gmail.com napsal:
Hello,
[............]
>
Is called Applyf<doubleand it calls f(double). You have to change
Applyf(1.0) to Applyf(1.0f) to call Applyf<float>.
>
Anyway I do not know, why f(double) is found in g2.C compilation unit.
I know you can easily direct the compiler to any version of 'f' you
wish, but.... the problem is that the criteria it uses (when you do not
that and it has to choose) seems to be rather unexpected.
>
Not really. The program can have only one, consistent Applyf<double>()
specialization - yet this program has created two Applyf<double>()
specialiations that differ from each other: one calls f(double) and the
other one calls f(float). Technically these two specializations for
Applyf<doubleviolate C++'s "One Definition Rule" (ODR). So the
behavior of the program is unexpected because it is undefined. As a
practical matter, which version of Applyf<doublewinds up in the
eventual build is dependent on factors that no build should depend on.
>
The fix should be straightfoward. A template function should call only
those functions that are "visible", that is, that have been declared
(they need not be defined) before the function template itself.
>
>
Greg
Thanks a lot, Greg, now the issue is clear to me. I've been reading
about <a
href="http://www.slac.stanford.edu/BFROOT/www/Computing/Environment/Standards/C++/cd2/basic.html#basic.def.odr">ODR</a>
and, if I'm not wrong, it states that violations to it need not be
allways diagnosed by the compiler/linker, which is the case here with
gcc/ld (although it usually diagnoses duplicate function definitions).
I was trying to find an example in which C++ templates flexibility (in
the sense that C++ allows calls from templates to still non declared
functions) allows to introduce unexpected behavoir. It seems that I
have found one --- IMHO, Ada approach to genericity is far more safe.

Closed Thread