inlining and function static variables

P

publictom

I just happened to read item 47, "Control Flow", in Exceptional C++ by
Herb Sutter (which is based on Guru of the Week 12), just after reading
item 33, "Use Inlining Judiciously", in Effective C++ by Scott Meyers.
Now I'm a little confused.

Scott Meyers says that if an inline function can't be inlined by the
compiler it is treated like a normal non-inlined function. However, he
says that there is an older behaviour where compilers would treat the
function as static, so there would be multiple copies of it, one in
each translation unit that uses the function. He says the latter
behaviour is non-standard but (was at the time, 1998...?) widespread
enough that you should be aware of it.

Skipping to Herb Sutter's book, published 2 years later, there is the
following code:

// [This declaration comes from some other header file]
extern int fileIdCounter;
....
// Helpers to automate class invariant checking.
template<class T>
inline void AAssert( T& p )
{
static int localFileId = ++fileIdCounter;
if( !p.Invariant() )
{
cerr << "Invariant failed: file " << localFileId
<< ", " << typeid(p).name()
<< " at " << static_cast<void*>(&p) << endl;
assert( false );
}
}

This is at the start of a chunk of example code designed to illustrate
things that can go wrong with control flow. The only problem that the
book points out with the variable localFileId is that fileIdCounter
might not be initialised by the time the first copy of localFileId is
initialised. (Aside: Aren't function static variables initialised the
first time the function is executed, ie. after all globals are
initialised? I'm not sure I understand this problem)

It seems to me, though, that aside from the problem of the order of
initialisation, this code would only work as intended if the function
was not successfully inlined, and then only on a compiler that followed
the incorrect (according to Meyers) behaviour of treating the
non-inlined inline function as local to the translation unit.

If the function was successfully inlined, or if the compiler followed
the standard behaviour of treating the function as a normal non-inlined
function if it can't inline it, wouldn't there just be a single copy of
localFileId, initialised once, in the whole program, thus defeating the
object of using it to identify the file? Although this would be a
control flow problem, "Exception C++" doesn't mention it, and neither
do any of the posts I could find for the corresponding Guru of the
Week, so I think I must have misunderstood something.

I was wondering whether the fact the function is a template might have
some effect, but I found this:
"A namespace scope template function has internal linkage if it is
explicitly declared static. No other template function has internal
linkage. The inline function specifier does not affect the linkage of a
template function."
at
http://publib.boulder.ibm.com/infoc...om.ibm.vacpp6m.doc/compiler/ref/tutmplat3.htm.

Can anyone help?
 
V

Victor Bazarov

// [This declaration comes from some other header file]
extern int fileIdCounter;

This variable is defined elsewhere.
...
// Helpers to automate class invariant checking.
template<class T>
inline void AAssert( T& p )
{
static int localFileId = ++fileIdCounter;
if( !p.Invariant() )
{
cerr << "Invariant failed: file " << localFileId
<< ", " << typeid(p).name()
<< " at " << static_cast<void*>(&p) << endl;
assert( false );
}
}

This is at the start of a chunk of example code designed to illustrate
things that can go wrong with control flow. The only problem that the
book points out with the variable localFileId is that fileIdCounter
might not be initialised by the time the first copy of localFileId is
initialised. (Aside: Aren't function static variables initialised the
first time the function is executed, ie. after all globals are
initialised? I'm not sure I understand this problem)

It's called "static initialisation order fiasco". Look it up.
It seems to me, though, that aside from the problem of the order of
initialisation, this code would only work as intended if the function
was not successfully inlined, and then only on a compiler that followed
the incorrect (according to Meyers) behaviour of treating the
non-inlined inline function as local to the translation unit.

If the function was successfully inlined, or if the compiler followed
the standard behaviour of treating the function as a normal non-inlined
function if it can't inline it, wouldn't there just be a single copy of
localFileId, initialised once, in the whole program, thus defeating the
object of using it to identify the file?

Actually the purpose wasn't to identify the file, but rather to identify
an instantiation of 'AAssert' for all T, probably.

There will be a single copy of 'localFieldId' for every instantiation of
'AAssert'. This is required by the Standard (14.8/2).

V
 
P

publictom

It's called "static initialisation order fiasco". Look it up.

Yes, I realised after posting that if the function was called by the
constructor of a static object then there would be a problem. I didn't
know that situation had a name. Thanks.
Actually the purpose wasn't to identify the file, but rather to
identify
an instantiation of 'AAssert' for all T, probably.

It would certainly work for that. It seems a funny thing to do, though.
Working out the type for which the template was instantiated from the
ID would be tricky, wouldn't it? It would depend on the order in which
the different instantiations of AAsert are called for the first time.
This seems unnecessary since the code already spits out the name of the
type for which the template function is instantiated.
Also, the variable is called localFileId, not localTypeId or something.
 
V

Victor Bazarov

[...]
This seems unnecessary since the code already spits out the name of the
type for which the template function is instantiated.
Also, the variable is called localFileId, not localTypeId or something.

It could just be left-over from the old days when it was intended to
identify the file in which the function was "instantiated" from a header
of something...
 
P

publictom

Oh. The typeid operator would give the dynamic type rather than the
static type, so they would give different information.

Outputing typeid<T>.name() would show the type the function was
instantiated for, though, and would be a lot simpler than the counter.
 
P

publictom

Oh. The typeid operator would give the dynamic type rather than the
static type, so they would give different information.

Outputing typeid(T).name() would show the type the function was
instantiated for, though, and would be a lot simpler than the counter.
 

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

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top