Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

ODL and inlining

20 views
Skip to first unread message

Scott Meyers

unread,
Jul 24, 2009, 8:55:48 PM7/24/09
to
Suppose I define a function at global scope and don't declare it inline:

void f() {}

If I put this in a header, compile it into two or more compilation units, then
link them together, my link will fail due to a multiply-defined symbol. This
is, as I understand it, a holdover from C.

The rules for inline functions are different. If I declare f inline,

inline void f() {}

I never have to worry about my link failing due to multiple definitions of f,
even if f is actually not inlined.

The rules for templates are different again. If I define a function template at
global scope and don't declare it inline,

template<typename T>
void f(T) {}

I don't have to worry about getting an error during linking that, say, f<int>,
is multiply defined, even if f<int> is instantiated and called from multiple
translation units.

Aside from the seemingly gratuitous complexity of the whole business, one
problem with this state of affairs is that compilers' ability to inline
non-inline functions (i.e., to inline them even though they were not declared
inline) is impaired. That's because the standard approach is to never put the
definitions of non-inline functions in header files, i.e., the first version of
f above would be declared in a header,

void f();

but defined elsewhere. That means that if compilers see a call to f, they are
unlikely to consider inlining it, because they don't have access to its
definition during the compilation.

Given the current asymmetry between the rules for non-inline template function
definitions (fine -- encouraged, actually -- in headers) and non-inline
non-template function definitions (allowed in headers, but not really
practical), the language could be simplified slightly and compilers' ability to
spontaneously inline functions could be enhanced by allowing multiple
definitions of non-inline functions, provided all definitions are "the same,"
where "the same" would be defined using wording similar to what is used now to
permit types (e.g., classes) to have multiple definitions, provided all are "the
same." As with types, ODL violations would be prohibited, but no diagnostic
would be required.

This change would require some tweaking of rules for internal and external
function linkage as well as for ODL violations, but given that C++0x is
apparently a lot further from finished than I'd expected, I'm wondering whether
(1) there are any technical implications of this proposed change I'm overlooking
and (2) there's any chance such a change could be made in C++0x.

Comments?

Scott

--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]

Francis Glassborow

unread,
Jul 25, 2009, 12:21:24 PM7/25/09
to
Scott Meyers wrote:

>
> Given the current asymmetry between the rules for non-inline template
> function
> definitions (fine -- encouraged, actually -- in headers) and non-inline
> non-template function definitions (allowed in headers, but not really
> practical), the language could be simplified slightly and compilers'
> ability to
> spontaneously inline functions could be enhanced by allowing multiple
> definitions of non-inline functions, provided all definitions are "the
> same,"
> where "the same" would be defined using wording similar to what is used
> now to
> permit types (e.g., classes) to have multiple definitions, provided all
> are "the
> same." As with types, ODL violations would be prohibited, but no diagnostic
> would be required.
>
> This change would require some tweaking of rules for internal and external
> function linkage as well as for ODL violations, but given that C++0x is
> apparently a lot further from finished than I'd expected, I'm wondering
> whether
> (1) there are any technical implications of this proposed change I'm
> overlooking
> and (2) there's any chance such a change could be made in C++0x.
>
> Comments?

Let me first comment the C++0x is substantially finished but the early
beta release(aka CD1) resulted in the detection of an extensive list of
issues (defects aka bugs) that needed addressing. It also became clear
to the majority that concepts were a bridge too far if we intended to
ship a final product within the next 5 years. Given the substantial
amount of other developments that magnitude of delay seemed
unacceptable. Note that one of the major elements of C++0x (and please
could all retain that designation for the next release of C++ standard,
otherwise we will get into an unholy mess wrt to nomenclature when we
also want to refer to the release after the next one -- as we do) is the
memory model for concurrency. Note that both C and C++ currently are
specified on an essentially sequential model machine. That model is now
far from the hardware that we are using. Even mobile (cell) phones
provide genuine concurrency as they are built on dual core processors.

While concepts have been a headline grabber, I think the work on
concurrency is of greater importance and that is ready to ship on the
current timeline.

Now back to the point you raise; a decade ago support for compilers
trying to inline small functions would have been important as we were
heavily reliant on relatively dumb linkers. These days we expect rather
better and are certainly getting it from more than one implementation.

Now as to the suggestion that we should allow individual TUs to include
definitions of the same function (because that is what it amounts to) I
would be very reluctant to go down that path UNLESS we also required
diagnosis of redefinitions that were not identical. Please note that it
is not enough to require lexical identity between definitions. Here is a
crude example of why that is the case:

in file 1:
#include <iostream>
#include <ostream>

static int i(0);
void f(){
std::cout << i;
}

in file 2:
#include <iostream>
#include <ostream>

static int i(0);
void f(){
std::cout << i;
}

Two identical files and yet the definitions of f() are distinct. We
struggle badly enough with such breaches of the ODR when templates are
involved, I do not see that opening up the rest of my code to such
vulnerabilities would be progress.

Balog Pal

unread,
Jul 25, 2009, 12:20:34 PM7/25/09
to
"Scott Meyers" <use...@aristeia.com>

> The rules for templates are different again. If I define a function
> template at
> global scope and don't declare it inline,
>
> template<typename T>
> void f(T) {}

... it still gets implicit inline, like functions that appear defined inside
a class.

> Aside from the seemingly gratuitous complexity of the whole business, one
> problem with this state of affairs is that compilers' ability to inline
> non-inline functions (i.e., to inline them even though they were not
> declared
> inline) is impaired. That's because the standard approach is to never put
> the
> definitions of non-inline functions in header files, i.e., the first
> version of
> f above would be declared in a header,
>
> void f();
>
> but defined elsewhere. That means that if compilers see a call to f, they
> are
> unlikely to consider inlining it, because they don't have access to its
> definition during the compilation.

My observartion in practice is that compilers agressively inline if you ask
them through switches (I used gcc and visual, from archaic to more current),
to include 'anything suitable', the effect is looking at the whole
translation unit, and at which point function is defined is completely
irrelevant, only that it is defined. (I admit most of my experiemnents were
with static functions, as I used real code, but I doubt it has influence.)
With such opt settings I had a really hard time to prevent inlining of
stuff, and defining function well after the use is definitely not a
solution.

I guess machine code generation happens in a separate run, where every
function in the uit already got (pre-)compiled, and its body available.

> Given the current asymmetry between the rules for non-inline template
> function
> definitions (fine -- encouraged, actually -- in headers) and non-inline
> non-template function definitions (allowed in headers, but not really
> practical), the language could be simplified slightly and compilers'
> ability to
> spontaneously inline functions could be enhanced by allowing multiple
> definitions of non-inline functions, provided all definitions are "the
> same,"
> where "the same" would be defined using wording similar to what is used
> now to
> permit types (e.g., classes) to have multiple definitions, provided all
> are "the
> same." As with types, ODL violations would be prohibited, but no
> diagnostic
> would be required.

As I understand the standard, ODR applies to the functions same way as to
anything else. Compiler use COMDEF, __selectany() and similar attributes for
bodies emitted to object, and the linker really selects a random one of the
candidates.

IOW you propose to make every function implicitly inline, and behave like
that?

I don't think it is a good idea in practice. Being hit by ODR violation is
one of the most costy bugs -- it is not flagged at compile, and you get
weird behavior (that may be very subtle), then pour lot of time inspecting
good code in wain before dicrovering you have some similarly named class in
two unrelated modules, that was meant local just forgot to gain 'static' or
namespace.

We would have the same with functions -- with massively elevated chance to
problem. Forgetting, or not caring to make a function 'static' is common,
as the effect is not visible, unless one browses the object code for names.
I mean until you stumble on an actual clash, that you trivially correct.
No strong guides are needed to contain that practice, as you can rely on
linker error, if the build goes, you are okay.

With your idea (IUC), this is turned to be an ODR nightmare, everyone must
watch really carefully to avoid such a distant accidental clash. (Imagine
that in projects that are large, old, and have massive parts started as .c
just compiled as .cpp; having massive amount of free functions, sitting in
the global namespace...)

> This change would require some tweaking of rules for internal and external
> function linkage as well as for ODL violations, but given that C++0x is
> apparently a lot further from finished than I'd expected, I'm wondering
> whether
> (1) there are any technical implications of this proposed change I'm
> overlooking
> and (2) there's any chance such a change could be made in C++0x.
>
> Comments?

The idea looks good on paper, but IMO on one hand solves no real problem,
and causes a plenty in a common kind of real projects. Even if we can call
those suboptimal. Removing existing sure diagnostic is not a step forward.

To make it good for everyone, you need to find a schema to work like you
say, but also provide a good diagnostic. I gave the problem thoughts not
one time, but just always concluded it is far from easy -- the code
generated for functions can be fifferent in separate units without breaking
ODR. So checksums are not enough. Some normalized immediate code is
needed to tell instances of the same function from a different one.

If you can nail that problem, it would bring a huge benefit to community --
and also certainly make your proposal a pure go-ahead.

Steve Clamage

unread,
Jul 26, 2009, 6:44:17 PM7/26/09
to
On Sat, 25 Jul 2009 10:20:34 CST, "Balog Pal" <pa...@lib.hu> wrote:

>"Scott Meyers" <use...@aristeia.com>
>
>> The rules for templates are different again. If I define a function
>> template at
>> global scope and don't declare it inline,
>>
>> template<typename T>
>> void f(T) {}
>
>... it still gets implicit inline, like functions that appear defined inside
>a class.

No, a function defined outside a class definition is not implicitly
inline, template or not. I think you may be confusing the meaning of
inline (generating the body of the function at the point of a call)
with the rule allowing multiple (but identical) definitions of an
inline function.

Because it would be too difficult for the programmer to keep track of
all needed instantiations of a function template, the compiler is
required (somehow) to keep track. In this way template instances are
different from non-template non-inline functions, where the programmer
must ensure that there is only one definition in the entire program.

As an implementation detail, the compiler might use the same mechanism
to keep track of template instances as it does for inline functions
that need to be generated as callable functions (as when the address
is taken. But that detail does not mean that template instances are
implicitly inline.

> ...

>IOW you propose to make every function implicitly inline, and behave like
>that?

No, Scott proposes to make the ODR rule for ordinary functions
equivalent to the rule for inline functions, so the C++ rules would
be more consistent.

>
>I don't think it is a good idea in practice. Being hit by ODR violation is
>one of the most costy bugs -- it is not flagged at compile, and you get
>weird behavior (that may be very subtle), then pour lot of time inspecting

>good code ....

And yet most violations of the ODR do not require a diagnostic
message. For example, you could have differing definitions of inline
functions, or a template, or a class, or a global data object, but no
diagnosis is required.

I suppose a compromise would be to leave multiple definitions an
error, but say that no diagnosis is required.

I don't see any implementation issues, since the compiler could use
the same mechanism for eliminating duplicates in the program that it
uses for inline and template functions.

I think the question comes down to whether the removal of this
asymmetry in the rules would create needless opportunities for error.

---
Steve Clamage

tni

unread,
Jul 26, 2009, 6:44:58 PM7/26/09
to
Balog Pal wrote:

> To make it good for everyone, you need to find a schema to work like you
> say, but also provide a good diagnostic. I gave the problem thoughts not
> one time, but just always concluded it is far from easy -- the code
> generated for functions can be fifferent in separate units without breaking
> ODR. So checksums are not enough. Some normalized immediate code is
> needed to tell instances of the same function from a different one.
>
> If you can nail that problem, it would bring a huge benefit to community --
> and also certainly make your proposal a pure go-ahead.

If you have a compiler/linker that smart, why wouldn't you simply do
whole program optimization/link time optimization?

E.g., Intel C++ and Visual Studio can that right now; GCC is in the
process of getting there.

Scott Meyers

unread,
Jul 26, 2009, 7:15:07 PM7/26/09
to
Balog Pal wrote:
> My observartion in practice is that compilers agressively inline if you ask
> them through switches (I used gcc and visual, from archaic to more current),
> to include 'anything suitable', the effect is looking at the whole
> translation unit, and at which point function is defined is completely
> irrelevant, only that it is defined.

But the rules for non-template non-inline functions essentially preclude
defining such functions in all but one TU. The point of my post was that
compilers would have more opportunities for inlining non-template functions if
such functions could be multiply-but-"identically" defined, just as functions
from templates are currently allowed to be.

> IOW you propose to make every function implicitly inline, and behave like
> that?

No, I propose to allow multiple identical definitions of non-inline non-template
functions, just as is currently allowed for non-inline template functions.

Scott

James Kanze

unread,
Jul 26, 2009, 7:14:27 PM7/26/09
to
On Jul 25, 6:20 pm, "Balog Pal" <p...@lib.hu> wrote:
> "Scott Meyers" <use...@aristeia.com>

> > The rules for templates are different again. If I define a
> > function template at global scope and don't declare it
> > inline,

> > template<typename T>
> > void f(T) {}

> ... it still gets implicit inline, like functions that appear
> defined inside a class.

Where does the standard say that? (The formal effects are
pretty much the same, but the "intent" is different, and a
quality compiler will respect the intent, unless it is sure that
it can do better---today, at least, most can't.)

> > Aside from the seemingly gratuitous complexity of the whole
> > business, one problem with this state of affairs is that
> > compilers' ability to inline non-inline functions (i.e., to
> > inline them even though they were not declared inline) is
> > impaired. That's because the standard approach is to never
> > put the definitions of non-inline functions in header files,
> > i.e., the first version of f above would be declared in a
> > header,

> > void f();

> > but defined elsewhere. That means that if compilers see a
> > call to f, they are unlikely to consider inlining it,
> > because they don't have access to its definition during the
> > compilation.

> My observartion in practice is that compilers agressively
> inline if you ask them through switches (I used gcc and
> visual, from archaic to more current), to include 'anything
> suitable', the effect is looking at the whole translation
> unit, and at which point function is defined is completely
> irrelevant, only that it is defined. (I admit most of my
> experiemnents were with static functions, as I used real code,
> but I doubt it has influence.) With such opt settings I had a
> really hard time to prevent inlining of stuff, and defining
> function well after the use is definitely not a solution.

With most compilers, if you use maximum optimization, the
compiler will do cross module optimization, dependent on
profiling information. At that point, where you defined the
function ceases to be relevant.

--
James Kanze (GABI Software) email:james...@gmail.com
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

unread,
Jul 26, 2009, 7:13:07 PM7/26/09
to

> void f() {}

Yes and no. Most compilers today are capable of cross module
optimizations, piloted by profiler output. In such cases, the
compiler should be able to inline a function even if its
definition is in another module.

> That's because the standard approach is to never put the
> definitions of non-inline functions in header files, i.e., the
> first version of f above would be declared in a header,

> void f();

> but defined elsewhere. That means that if compilers see a
> call to f, they are unlikely to consider inlining it, because
> they don't have access to its definition during the
> compilation.

Only if you haven't requested optimization.

> Given the current asymmetry between the rules for non-inline
> template function definitions (fine -- encouraged, actually --
> in headers) and non-inline non-template function definitions
> (allowed in headers, but not really practical), the language
> could be simplified slightly and compilers' ability to
> spontaneously inline functions could be enhanced by allowing
> multiple definitions of non-inline functions, provided all
> definitions are "the same," where "the same" would be defined
> using wording similar to what is used now to permit types
> (e.g., classes) to have multiple definitions, provided all are
> "the same." As with types, ODL violations would be prohibited,
> but no diagnostic would be required.

> This change would require some tweaking of rules for internal
> and external function linkage as well as for ODL violations,
> but given that C++0x is apparently a lot further from finished
> than I'd expected, I'm wondering whether

> (1) there are any technical implications of this proposed
> change I'm overlooking
> and (2) there's any chance such a change could be made in C++0x.

> Comments?

The evolution is in the wrong direction. We don't need more
possibilities of undefined behavior; we need less. (That's one
of the problems export was designed to solve, at least for
function templates.)

--
James Kanze (GABI Software) email:james...@gmail.com
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

Scott Meyers

unread,
Jul 26, 2009, 7:13:51 PM7/26/09
to
Francis Glassborow wrote:
> Let me first comment the C++0x is substantially finished but the early
> beta release(aka CD1) resulted in the detection of an extensive list of
> issues (defects aka bugs) that needed addressing.

My understanding is that N2928 was adopted, thus adding support for new
attributes for explicit virtual overrides. It's also my understanding that the
existence of the variadic thread constructor remains uncertain, which I think of
as a pretty crucial part of the thread API. Especially given the adoption of
new attributes, I think calling CD1 "feature-complete" is not entirely accurate.
(Not that you used the phrase "feature-complete." But others have.)

> Now back to the point you raise; a decade ago support for compilers
> trying to inline small functions would have been important as we were
> heavily reliant on relatively dumb linkers. These days we expect rather
> better and are certainly getting it from more than one implementation.

Can you elaborate on what you mean here? My interest in inlining is not the
elimination of the code for the call/return itself, but rather the ability of
the code generator to generate better code for the called function in the
context of the call, e.g., to eliminate redundant tests for null pointers
between caller and callee, along with the corresponding elimination of dead code
that results. I believe that Microsoft's compiler/linker does this when
whole-program optimization is enabled, but otherwise not, and I'm not aware of
other build systems that do this.

The benefits of inlining I'm looking for depend on full code optimization and
generation capabilities, which is traditionally the role of compilers, not
linkers, and compilers have traditionally been limited in what they can do by
the absence of information about other TUs. Has the state of commercial
practice advanced to the point where programmers can reasonably hope that their
build systems will yield optimized inline code for calls to functions defined in
other TUs?

Thanks,

Scott

Alf P. Steinbach

unread,
Jul 27, 2009, 5:22:53 PM7/27/09
to
* Steve Clamage:

> On Sat, 25 Jul 2009 10:20:34 CST, "Balog Pal" <pa...@lib.hu> wrote:
>
>> IOW you propose to make every function implicitly inline, and behave like
>> that?
>
> No, Scott proposes to make the ODR rule for ordinary functions
> equivalent to the rule for inline functions, so the C++ rules would
> be more consistent.

I can understand that the 'inline' keyword would still retain its meaning as an
optimization hint, and that would then be its only function, consistent with its
name.

But to me it sounds like wordplay.

Effectively every function would then be inline.

But for data this would not be so and isn't so: for data the only inline
behavior is for template class' static data members.

Oh gosh, inconsistency.


- Alf

Scott Meyers

unread,
Jul 27, 2009, 5:23:58 PM7/27/09
to
James Kanze wrote:
> Only if you haven't requested optimization.

More specifically, profile-guided optimization, which is a bit of a different
animal, because it requires more than one compilation of a TU as well as a
"representative" input set in order to yield an optimized executable.

Scott

Francis Glassborow

unread,
Jul 27, 2009, 5:22:08 PM7/27/09
to
Steve Clamage wrote:
>
> And yet most violations of the ODR do not require a diagnostic
> message. For example, you could have differing definitions of inline
> functions, or a template, or a class, or a global data object, but no
> diagnosis is required.

And the reason for that is that imposing a requirement for a diagnostic
(e.g. for conflicting definitions of an inline function) would impose a
considerable burden on the implementation. It means (AFAICS) that the
compiler would have to include metadata for use by the linker. Yes I
know that the Standard does not refer to such tools but in practice that
is what we would have. It gets even worse when dealing with dynamic
libraries.

>
> I suppose a compromise would be to leave multiple definitions an
> error, but say that no diagnosis is required.
>
> I don't see any implementation issues, since the compiler could use
> the same mechanism for eliminating duplicates in the program that it
> uses for inline and template functions.
>
> I think the question comes down to whether the removal of this
> asymmetry in the rules would create needless opportunities for error.

And I think both Paul and I think the answer to that is 'yes'. I would
like to see implementors work on other cross TU issues long before we
start adding new potentials for violating the ODR.

georg...@gmail.com

unread,
Jul 27, 2009, 5:23:31 PM7/27/09
to
On Jul 25, 1:21 pm, Francis Glassborow
<francis.glassbo...@btinternet.com> wrote:

Okay, maybe I have a bad case of the stupid here, but if I
theoretically put the inline keyword in front of all of my global
scope functions and defined them in-place, isn't that the behaviour
that Scott is looking for? If it works for explicitly inlined
functions, why wouldn't it work for implicit ones?

Francis Glassborow

unread,
Jul 27, 2009, 7:53:33 PM7/27/09
to
Scott Meyers wrote:
> Francis Glassborow wrote:

>> Now back to the point you raise; a decade ago support for compilers
>> trying to inline small functions would have been important as we were
>> heavily reliant on relatively dumb linkers. These days we expect rather
>> better and are certainly getting it from more than one implementation.
>
> Can you elaborate on what you mean here? My interest in inlining is not
> the
> elimination of the code for the call/return itself, but rather the
> ability of
> the code generator to generate better code for the called function in the
> context of the call, e.g., to eliminate redundant tests for null pointers
> between caller and callee, along with the corresponding elimination of
> dead code
> that results. I believe that Microsoft's compiler/linker does this when
> whole-program optimization is enabled, but otherwise not, and I'm not
> aware of
> other build systems that do this.

Actually one of the earlier IBM C++ compilers did this as well but used
a flawed mechanism (effectively concatenating all the TUs and thereby
altering the context of some definitions)

>
> The benefits of inlining I'm looking for depend on full code
> optimization and
> generation capabilities, which is traditionally the role of compilers, not
> linkers, and compilers have traditionally been limited in what they can
> do by
> the absence of information about other TUs. Has the state of commercial
> practice advanced to the point where programmers can reasonably hope
> that their
> build systems will yield optimized inline code for calls to functions
> defined in
> other TUs?

Microsoft have been providing such whole program optimisation for some
time (as you note above). I believe the Intel C++ compiler does so as
well. GCC is reported to be working to provide it.

I guess you already know that the secret lies in delaying code
generation as late as possible. Our traditional separation into
compilation/linking/loading is rapidly breaking down. We have
'compilers' generating some kind of intermediate annotated with metadata
but without code generation whilst 'linkers' use that metadata to assist
with efficient code generation.

I am not claiming that that is perfect but the pressure to provide
efficient 'object code' is pushing implementers in that direction.

>
> Thanks,
>
> Scott
>

--
Note that robinton.demon.co.uk addresses are no longer valid.

Francis Glassborow

unread,
Jul 27, 2009, 7:55:40 PM7/27/09
to
James Kanze wrote:
>
> With most compilers, if you use maximum optimization, the
> compiler will do cross module optimization, dependent on
> profiling information. At that point, where you defined the
> function ceases to be relevant.

As IBM discovered many years ago, that is not true. The point at which
you define a function is highly relevant because of the names that are
visible at that point. And that is one of the problems with multiple
definitions of functions. It can result in subtle errors with inline
functions and is a considerable headache for instantiations of function
templates, even the apparently inocuous inclusion of a header file can
result in changing the semantic of a function.

Francis Glassborow

unread,
Jul 27, 2009, 7:54:12 PM7/27/09
to
Scott Meyers wrote:
> Balog Pal wrote:
>> My observartion in practice is that compilers agressively inline if
>> you ask
>> them through switches (I used gcc and visual, from archaic to more
>> current),
>> to include 'anything suitable', the effect is looking at the whole
>> translation unit, and at which point function is defined is completely
>> irrelevant, only that it is defined.
>
> But the rules for non-template non-inline functions essentially preclude
> defining such functions in all but one TU. The point of my post was that
> compilers would have more opportunities for inlining non-template
> functions if
> such functions could be multiply-but-"identically" defined, just as
> functions
> from templates are currently allowed to be.
>
>> IOW you propose to make every function implicitly inline, and behave like
>> that?
>
> No, I propose to allow multiple identical definitions of non-inline
> non-template
> functions, just as is currently allowed for non-inline template functions.


Yes, I for one understood that. However having been involved in the 90's
with an attempt to specify what is an 'identical definition' I am rather
more aware of how difficult that is. Two lexically different definitions
can actually be 'identical' in substance and two lexically identical
ones can be different because the context is different.

We know that allowing multiple, lexically identical, definitions can
result in subtle breaches of the ODR. I for one am reluctant to increase
the potential for such problems.

Were it allowed I guess there would be a new chapter in one of your
books warning programmers of the dangers of redefinitions :)

Francis Glassborow

unread,
Jul 28, 2009, 2:22:07 PM7/28/09
to
georg...@gmail.com wrote:
> On Jul 25, 1:21 pm, Francis Glassborow
> <francis.glassbo...@btinternet.com> wrote:
>
> Okay, maybe I have a bad case of the stupid here, but if I
> theoretically put the inline keyword in front of all of my global
> scope functions and defined them in-place, isn't that the behaviour
> that Scott is looking for? If it works for explicitly inlined
> functions, why wouldn't it work for implicit ones?

False premise. It does not work. When you explicitly declare a function
inline you absolve the compiler from the responsibility for any
resulting undefined behaviour.

Consider:

in file x.h
#include <iostream>
#include <ostream>
inline int foo(){return i;}
void bary();
void barz();

in file y.cpp


static int i(0);
#include "x.h"
void bary(){
std::cout << foo();
}

infile z.cpp

static int i(0);
#include "x.h"
void barz(){
std::cout << foo();
}

in file main.cpp

#include "x.h"
int main(){
bary();
barz();
}

Should this compile? If not, why not? Must there be any diagnostics, if
so why? What should the output be?

Greg Herlihy

unread,
Jul 28, 2009, 2:32:24 PM7/28/09
to
On Jul 26, 4:15 pm, Scott Meyers <use...@aristeia.com> wrote:
> Balog Pal wrote:
> > My observartion in practice is that compilers agressively inline if you ask
> > them through switches (I used gcc and visual, from archaic to more current),
> > to include 'anything suitable', the effect is looking at the whole
> > translation unit, and at which point function is defined is completely
> > irrelevant, only that it is defined.
>
> But the rules for non-template non-inline functions essentially preclude
> defining such functions in all but one TU.

You are forgetting about anonymous namespaces. For example:

namespace {
void f() {}
}

This definition of f() may be placed in a header file and be included
by any number of source files. And generally it is safer to have
multiple functions that share a single definition, then it is to have
a single function with (potentially) multiple definitions.

> > IOW you propose to make every function implicitly inline, and behave like
> > that?
>
> No, I propose to allow multiple identical definitions of non-inline non-template
> functions, just as is currently allowed for non-inline template functions.

But how would the programmer go about creating multiple definitions of
the same function. By copying and pasting the function body into
multiple source files? Yet how easy would it be to maintain a function
if it had copies strewn across any number of source files? And
wouldn't this approach lead to an exponential increase in the size of
a program's source code?

Of course, if the multiple definitions are to come about simply by
having source files include a header file with the function definition
- then the programmer can do the same today, simply by declaring the
included function "inline." So as far as I can tell, the only effect
of this proposal would be to make the "inline" keyword implicit for
functions defined in header files.

Greg

Gabriel Dos Reis

unread,
Jul 28, 2009, 2:44:08 PM7/28/09
to
James Kanze <james...@gmail.com> writes:

[...]

| The evolution is in the wrong direction. We don't need more
| possibilities of undefined behavior; we need less. (That's one
| of the problems export was designed to solve, at least for
| function templates.)

Agreed.

Also, I think we need less of implementation details enshrining in the
language specification. I rather see the language move in the direction
modules and de-emphasize "header files" (and macro hackeries that comes
with it), than building the language around '70 technologies
of "header files" and all sorts of things one can or cannot do with
header files.

The underlying philosophy of ODR is that there is "only one single
definition of every entity in the source program". That is hard to get
with '70 technology of preprocesing. So the ODR ideal was approximated
by "same definition" in sense of "token-wise comparison", but we must
not forget what the real design ideal is. If we must change the ODR
rule, it should be in direction of reaching that ideal, not moving away
from it.

James Kanze

unread,
Jul 28, 2009, 3:07:02 PM7/28/09
to
On Jul 28, 1:55 am, Francis Glassborow

<francis.glassbo...@btinternet.com> wrote:
> James Kanze wrote:

> > With most compilers, if you use maximum optimization, the
> > compiler will do cross module optimization, dependent on
> > profiling information. At that point, where you defined the
> > function ceases to be relevant.

> As IBM discovered many years ago, that is not true. The point
> at which you define a function is highly relevant because of
> the names that are visible at that point. And that is one of
> the problems with multiple definitions of functions. It can
> result in subtle errors with inline functions and is a
> considerable headache for instantiations of function
> templates, even the apparently inocuous inclusion of a header
> file can result in changing the semantic of a function.

Yes. I was speaking only in terms of inlining. The real
problem here isn't that you can't define non-inline
non-template functions more than once; it's that you have to
define inline functions, and in the absense of export, function
templates, more than once. The "correct" solution would be to
change this (dropping support for non-exported function
templates). But of course, that would break more than a little
existing code.

--
James Kanze (GABI Software)

email:james...@gmail.com<email%3Ajame...@gmail.com>
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34


--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use

mailto:std...@netlab.cs.rpi.edu<std-c%2B%2...@netlab.cs.rpi.edu>

Bart van Ingen Schenau

unread,
Jul 28, 2009, 6:54:45 PM7/28/09
to
Steve Clamage wrote:

> On Sat, 25 Jul 2009 10:20:34 CST, "Balog Pal" <pa...@lib.hu> wrote:
>
>>
>>I don't think it is a good idea in practice. Being hit by ODR
>>violation is one of the most costy bugs -- it is not flagged at
>>compile, and you get weird behavior (that may be very subtle), then
>>pour lot of time inspecting good code ....
>
> And yet most violations of the ODR do not require a diagnostic
> message. For example, you could have differing definitions of inline
> functions, or a template, or a class, or a global data object, but no
> diagnosis is required.
>
> I suppose a compromise would be to leave multiple definitions an
> error, but say that no diagnosis is required.

I don't see how that changes anything, as the C++03 standard already
states: "Every program shall contain exactly one definition of every
*non-inline* function or object that is used in that program; *no
diagnostic required*" ([basic.def.odr]/3; emphasis mine)

So, the multiple definitions is already a non-diagnosable error.

<snip>
> ---
> Steve Clamage
>

Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/

Scott Meyers

unread,
Jul 28, 2009, 6:53:56 PM7/28/09
to
Greg Herlihy wrote:
> Of course, if the multiple definitions are to come about simply by
> having source files include a header file with the function definition

Which is what I'd expect to be pretty much the only sane use case...

> - then the programmer can do the same today, simply by declaring the
> included function "inline." So as far as I can tell, the only effect
> of this proposal would be to make the "inline" keyword implicit for
> functions defined in header files.

Except the inline keyword has semantics: it's a suggestion by the programmer
that the function be inlined. I want to make it possible for a programmer to
make a function easily inlineable during compilation without requesting that
inlining be performed. That is, I want

void f(int i) { ... }

to have the same semantics as the functions generated from

template<typename T>
void f(T i) { ... }

This simplifies the language rules, something you'd perhaps have more
appreciation for if you had to try to explain to people on a recurring basis why
the first code above isn't (practically speaking) valid in a header, yet the
second code isn't just allowed, it's (practically speaking) required by many
compilers.

It would also facilitate more options for discretionary inlining (i.e., inlining
of functions not declared inline) on the part of compilers, something they
already do when function definitions are visible. Given that they already do it
when they have the needed information at their disposal, I'm simply suggesting
that we make it possible to more frequently make the information available.

Scott

Gabriel Dos Reis

unread,
Jul 29, 2009, 2:35:47 AM7/29/09
to
Scott Meyers <use...@aristeia.com> writes:

| Greg Herlihy wrote:
| > Of course, if the multiple definitions are to come about simply by
| > having source files include a header file with the function definition
|
| Which is what I'd expect to be pretty much the only sane use case...
|
| > - then the programmer can do the same today, simply by declaring the
| > included function "inline." So as far as I can tell, the only effect
| > of this proposal would be to make the "inline" keyword implicit for
| > functions defined in header files.
|
| Except the inline keyword has semantics: it's a suggestion by the programmer
| that the function be inlined. I want to make it possible for a programmer to
| make a function easily inlineable during compilation without requesting that
| inlining be performed. That is, I want
|
| void f(int i) { ... }
|
| to have the same semantics as the functions generated from
|
| template<typename T>
| void f(T i) { ... }

I would not expect that. If normal functions were to be viewed as
generated from templates, I would expect them to be generated from
exported templates.

| This simplifies the language rules, something you'd perhaps have more
| appreciation for if you had to try to explain to people on a recurring basis why
| the first code above isn't (practically speaking) valid in a header, yet the
| second code isn't just allowed, it's (practically speaking) required by many
| compilers.

Oh, that is simple to explain:

You should avoid the first -- that is an engineering rule (and of
course, you are free to ignore it at your own risk.)
The second is an abnormal curiosity.

Now, as we all know, anomalies are not usually simple to explain so your
problem-solving programmer wouldn't expect you to start helping him/her
solve problems by giving detailed explanations of anomalies.

Balog Pal

unread,
Jul 29, 2009, 2:37:24 AM7/29/09
to
"Scott Meyers" <use...@aristeia.com>

> Greg Herlihy wrote:
>> Of course, if the multiple definitions are to come about simply by
>> having source files include a header file with the function definition
>
> Which is what I'd expect to be pretty much the only sane use case...
>
>> - then the programmer can do the same today, simply by declaring the
>> included function "inline." So as far as I can tell, the only effect
>> of this proposal would be to make the "inline" keyword implicit for
>> functions defined in header files.
>
> Except the inline keyword has semantics: it's a suggestion by the
> programmer
> that the function be inlined. I want to make it possible for a programmer
> to
> make a function easily inlineable during compilation without requesting
> that
> inlining be performed. That is, I want

Ah, that is your concern. I didn't get it the last time.
Did you see compilers actually giving a damn about that "hint"?
My experience shows they do not -- on one hand I saw not inlining pretty
trivial stuff (possibly due to compiler bug or arbitrary limitation when
functions are nested), and on the other, inlining anything goes if the
option allows.

I recall reading articles that put the use of keyword 'inline' on the level
of 'register' -- don't do it. I mean for the hint effect, restrict usage for
the COMDEF.

> void f(int i) { ... }
>
> to have the same semantics as the functions generated from
>
> template<typename T>
> void f(T i) { ... }

I was surprised people pointing out the latter is not equivalent with

template<typename T>
inline void f(T i) { ... }

what I assumed the case by behavior.

I would not be surprised to find out that internally the compilers not
supporting 'export' just flag them the same in the internal table.

> This simplifies the language rules, something you'd perhaps have more
> appreciation for if you had to try to explain to people on a recurring
> basis why
> the first code above isn't (practically speaking) valid in a header, yet
> the
> second code isn't just allowed, it's (practically speaking) required by
> many
> compilers.

Templates were using the inclusion model from the start, export was
introduced way late on track, and ignored by the industry. Isn't that
enough for explanation on the difference?

Maybe my mind is too constrained by knowledge of compilers and linkers it's
even hard to imagine as not an abvious fundamental difference. :-o

OTOH, I see free template functions written 'by hand' a very rare beast.
Most templates are used from libraries, written by others, and the
hand-crafted ones are mostly classes. And the few people who actually start
writing templated code *for good*, are on advanced level to not be surprised
by the difference. (?)

> It would also facilitate more options for discretionary inlining (i.e.,
> inlining
> of functions not declared inline) on the part of compilers, something they
> already do when function definitions are visible. Given that they already
> do it
> when they have the needed information at their disposal, I'm simply
> suggesting
> that we make it possible to more frequently make the information
> available.

If one wants to make the bodies visible, he must reorganize the project,
pasting bodies to included files. Compared to all that work, inserting a
few 'inline' words is hardly a hit.

While there are many practical reasons to avoid such inclusion -- and the
miss of 'export' is really painfu too: the implementation tends to drag in
too much stuff. Adding to both clutter, compile-time dependencies and build
times reaching for the sky. Very soon inclusion order problems surface
too, with need to split some headers, messing up the version control.

I'd be so happy with a VisualAge-like 'repository' of code where the devenv
just knows where the stuff is, and make me free of all the including
business, yet can build everything. Unfortunately, as Francis pointed out
too, C++ is not really fit for such a model, it does depend on files, order
of items, etc.

As others said on this topic, I too would rather see solutionts better
support modules, and the common pain cases.

While this asymmetry seem more like a teaching problem -- I guess it is in a
good and fat company...

Gabriel Dos Reis

unread,
Jul 29, 2009, 2:55:48 PM7/29/09
to
"Balog Pal" <pa...@lib.hu> writes:

[...]

| Templates were using the inclusion model from the start, export was
| introduced way late on track, and ignored by the industry.

That does not square with accounts I've received about the
design goals of templates. The earliest online document from 1992

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/1992/N0209.pdf

has this on page 4

# There were several goals in the original template specification, and
# it is intended wherever possible to preserve these goals. The goals
# briefly are to provide a parameterised type language facility that would
# permit automatic generation of required template types and functions;
# that would be suitable for the separate compilation of template
# declaration and definition; that would facilitate early rather than
# late detection of programmer errors; that was not subject to ambiguous
# interpretation by programmer or translator.

Balog Pal

unread,
Jul 29, 2009, 6:18:30 PM7/29/09
to
"Gabriel Dos Reis" <g...@cs.tamu.edu>

> "Balog Pal" <pa...@lib.hu> writes:
>
> | Templates were using the inclusion model from the start, export was
> | introduced way late on track, and ignored by the industry.
>
> That does not square with accounts I've received about the
> design goals of templates. The earliest online document from 1992
>
> http://www.open-std.org/JTC1/SC22/WG21/docs/papers/1992/N0209.pdf
>
> has this on page 4
>
> # There were several goals in the original template specification, and
> # it is intended wherever possible to preserve these goals. The goals
> # briefly are to provide a parameterised type language facility that
> would
> # permit automatic generation of required template types and functions;
> # that would be suitable for the separate compilation of template
> # declaration and definition; that would facilitate early rather than
> # late detection of programmer errors; that was not subject to ambiguous
> # interpretation by programmer or translator.

Possibly, what I meant was the practice. IIRC the first c++ compiler I used
was Borland 2.0, then 3.0 and 3.1, implementing features of CFront with
similar number. There were templates, without export, inclusion only and no
deep parsing of the template text until instantiation point. Later used MS
C++, Visual, Sun, forgot-what on OS/2, same story.
Eventually almost a decade passed, we got standard in '98, I used zillion
versions of Visual, Sun, g++, VisualAge, Intel, Hitachi -- new template
parsing and interpretation rules eventually sinked in (causing a deal of
pain too), but one thing is a fixed point: I did not get export, and stick
with inclusion. Did not even see some promise for a shipping date from
compilers I used most.

Guess I am not the only one without luck...

Unfortunately documents about wishful thinking fail to create executable
from my code...

Scott Meyers

unread,
Jul 29, 2009, 6:16:03 PM7/29/09
to
Gabriel Dos Reis wrote:

> "Balog Pal" <pa...@lib.hu> writes:
> | Templates were using the inclusion model from the start, export was
> | introduced way late on track, and ignored by the industry.
>
> That does not square with accounts I've received about the
> design goals of templates. The earliest online document from 1992

He didn't mention design goals, he mentioned reality.

Scott

Gabriel Dos Reis

unread,
Jul 29, 2009, 8:07:04 PM7/29/09
to
"Balog Pal" <pa...@lib.hu> writes:

| "Gabriel Dos Reis" <g...@cs.tamu.edu>
| > "Balog Pal" <pa...@lib.hu> writes:
| >
| > | Templates were using the inclusion model from the start, export was
| > | introduced way late on track, and ignored by the industry.
| >
| > That does not square with accounts I've received about the
| > design goals of templates. The earliest online document from 1992
| >
| > http://www.open-std.org/JTC1/SC22/WG21/docs/papers/1992/N0209.pdf
| >
| > has this on page 4
| >
| > # There were several goals in the original template specification, and
| > # it is intended wherever possible to preserve these goals. The goals
| > # briefly are to provide a parameterised type language facility that
| > would
| > # permit automatic generation of required template types and functions;
| > # that would be suitable for the separate compilation of template
| > # declaration and definition; that would facilitate early rather than
| > # late detection of programmer errors; that was not subject to ambiguous
| > # interpretation by programmer or translator.
|
| Possibly, what I meant was the practice. IIRC the first c++ compiler I used
| was Borland 2.0, then 3.0 and 3.1, implementing features of CFront with
| similar number.

While the implementation in CFront-3.0.x was not the "export" as we know
today, I believe it is a stretch to characterize it as "inclusion
model". And the keyword 'export' was not needed, because the semantics
was intended to be that of what we call 'export' today. As for
compilers, the Sun CC I used in 1996 let me act as if I had separate
compilation of template so that I don't have to include non-inline
function templates everywhere in my programs.

[...]

| Unfortunately documents about wishful thinking fail to create executable
| from my code...

that may definitely be so. They also help keep us honest.

Gabriel Dos Reis

unread,
Jul 29, 2009, 8:05:58 PM7/29/09
to
Scott Meyers <use...@aristeia.com> writes:

| Gabriel Dos Reis wrote:
| > "Balog Pal" <pa...@lib.hu> writes:
| > | Templates were using the inclusion model from the start, export was
| > | introduced way late on track, and ignored by the industry.
| >
| > That does not square with accounts I've received about the
| > design goals of templates. The earliest online document from 1992
|
| He didn't mention design goals, he mentioned reality.

The formulation -- still available above -- appears a bit misleading if
we were to be close to history. Templates did not start with the
inclusion model; that model appeared *later* in the development of
templates while trying to approximate the goals in implementations.
"export" was introduced as a compromise to *reinstate* the original goal.

James Kanze

unread,
Jul 30, 2009, 1:25:53 PM7/30/09
to
On Jul 30, 2:07 am, Gabriel Dos Reis <g...@cs.tamu.edu> wrote:

> "Balog Pal" <p...@lib.hu> writes:
> > Possibly, what I meant was the practice. IIRC the first c++
> > compiler I used was Borland 2.0, then 3.0 and 3.1,
> > implementing features of CFront with similar number.

> While the implementation in CFront-3.0.x was not the "export"
> as we know today, I believe it is a stretch to characterize it
> as "inclusion model".

I think that John Spicer characterized it as "implicit
inclusion". The template expansion is compiled more or less as
if it were included in the source file, at the point of
instantiation (which was a lot less clear in CFront than in the
standard). On the other hand, this took place a link time (not
compile time), and the client code never actually included a
file with the template implementation.

> And the keyword 'export' was not needed, because the semantics
> was intended to be that of what we call 'export' today.

I think that might have been part of the intent. It certainly
wasn't what actually happened. (But the exact rules weren't
very clear.)

> As for compilers, the Sun CC I used in 1996 let me act as if I
> had separate compilation of template so that I don't have to
> include non-inline function templates everywhere in my
> programs.

No, but you still got a significant part of the instantiation
context, and not just for dependent names, when you actually
compiled the instantiation.

--
James Kanze (GABI Software) email:james...@gmail.com

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

Gabriel Dos Reis

unread,
Jul 30, 2009, 4:57:25 PM7/30/09
to
James Kanze <james...@gmail.com> writes:

| On Jul 30, 2:07 am, Gabriel Dos Reis <g...@cs.tamu.edu> wrote:
| > "Balog Pal" <p...@lib.hu> writes:
| > > Possibly, what I meant was the practice. IIRC the first c++
| > > compiler I used was Borland 2.0, then 3.0 and 3.1,
| > > implementing features of CFront with similar number.
|
| > While the implementation in CFront-3.0.x was not the "export"
| > as we know today, I believe it is a stretch to characterize it
| > as "inclusion model".
|
| I think that John Spicer characterized it as "implicit
| inclusion". The template expansion is compiled more or less as
| if it were included in the source file, at the point of
| instantiation (which was a lot less clear in CFront than in the
| standard).

I believe that characterization is that of a particular
implementation strategy does as opposed to what the semantics was
supposed to be. The difference is that if you say "hey, this is just
inclusion in the source code", then people want to know
whether their macros (active at the point of instantiation) will be
properly expanded. However, if you say "hey, this is separate
compilation", then people would less expect random macros to be expanded
at random places. (Those aspects were clarified in the C++98 standard.)

So, no, the "implicit inclusion" while appropriate documentation of a
particular implementation strategy is an inadequate characterization.

| On the other hand, this took place a link time (not
| compile time), and the client code never actually included a
| file with the template implementation.

The reference I gave quite clearly articulated the issues that were
being considered at the time (1992) to get the earlier experimental
implementations closer to the goals. A key aspect was that the user
never got to explicitly include "header files" containing non-inline
function templates in every translation units that used them. Rather,
the issue was to formulate a set of rules that gave an abstract
behavior of separate compilation, and let implementations use whatever
strategy was adequate to meet that behavior.

| > And the keyword 'export' was not needed, because the semantics
| > was intended to be that of what we call 'export' today.
|
| I think that might have been part of the intent. It certainly
| wasn't what actually happened. (But the exact rules weren't
| very clear.)

We certainly don't have to guess what the goals were: they were
stated plainly. Given that the intent was stated quite explicitly, it is
clear that the implementations available were *approximations* of the
goals, that got to be refined -- as is normal process in software
development. Now it is puzzling that one would take the approximation
for the real thing (even when few implementations at the time went
beyond that approximation.)

| > As for compilers, the Sun CC I used in 1996 let me act as if I
| > had separate compilation of template so that I don't have to
| > include non-inline function templates everywhere in my
| > programs.
|
| No, but you still got a significant part of the instantiation
| context, and not just for dependent names, when you actually
| compiled the instantiation.

Instantiations of exported templates, as we know them today, rely on
the instantiation contexts. Granted the rules are less fuzzy.

Hendrik Schober

unread,
Aug 4, 2009, 3:16:25 PM8/4/09
to
Scott Meyers wrote:
> [...]

> Given the current asymmetry between the rules for non-inline template function
> definitions (fine -- encouraged, actually -- in headers) and non-inline
> non-template function definitions (allowed in headers, but not really
> practical), the language could be simplified slightly and compilers' ability to
> spontaneously inline functions could be enhanced by allowing multiple
> definitions of non-inline functions, provided all definitions are "the same,"
> where "the same" would be defined using wording similar to what is used now to
> permit types (e.g., classes) to have multiple definitions, provided all are "the
> same." As with types, ODL violations would be prohibited, but no diagnostic
> would be required.
>
> This change would require some tweaking of rules for internal and external
> function linkage as well as for ODL violations, but given that C++0x is
> apparently a lot further from finished than I'd expected, I'm wondering whether
> (1) there are any technical implications of this proposed change I'm overlooking
> and (2) there's any chance such a change could be made in C++0x.
>
> Comments?

One drawback of templates (in the absence of 'export') is that they
significantly increase compilation time. What you propose would add
to C++' already bad compilation times.
I'd rather have a module concept avoiding the textual replacement
altogether.

> Scott

Schobi

0 new messages