context in template point of instantiation (g++)

  • Thread starter carlos.urena.almagro
  • Start date
C

carlos.urena.almagro

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 T> T 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
 
O

Ondra Holub

(e-mail address removed) napsal:
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 T> T 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<double> and 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.
 
C

carlos.urena.almagro

Ondra said:
(e-mail address removed) napsal:
Hello,
[............]

Is called Applyf<double> and 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.
 
G

Greg

Ondra said:
(e-mail address removed) napsal:
Hello,
[............]

Is called Applyf<double> and 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<double> violate 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<double> winds 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
 
C

carlos.urena.almagro

Greg said:
Ondra said:
(e-mail address removed) napsal:
Hello,
[............]

Is called Applyf<double> and 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<double> violate 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<double> winds 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...nt/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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,058
Latest member
QQXCharlot

Latest Threads

Top