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

Suboptimal optimization

2 views
Skip to first unread message

mlimber

unread,
Nov 10, 2005, 7:28:08 AM11/10/05
to
In a recent thread on comp.lang.c++
(http://groups.google.com/group/comp.lang.c++/browse_frm/thread/033d97e6592e0562),
I posed a rather intricate problem, which boils down to the following:

A const static member of a template class is being optimized away by
the compiler. Unfortunately, the initialization of that member has a
global side effect that the compiler doesn't detect. Here's a
relatively simple example:

#include <iostream>
using namespace std;

int GetInvocationCount()
{
// Code with side-effect
static int i=0;
return ++i;
}

template<class T>
struct A
{
typedef T Type;
const static int i;
};

// Cause side effect
template<class T>
const int A<T>::i = GetInvocationCount();

// Instantiate the template
template A<int>;

// Instantiate the template again
struct B : A<float> {};

int main()
{
(void)B::i; // ***
cout << GetInvocationCount() << endl;
return 0;
};

If I run the program as is, I get 3 as expected. If, however, I run the
program after deleting the first line of main, I get 2 because the
instantiation of A<float> doesn't take place. This example is obviously
trivial, but in the other thread, it has serious consequences (viz., my
classes are not registered with my factory).

Obviously, I want to avoid this suboptimal optimization. Is this
standard-conformant behavior? Can anyone suggest a portable way to
avoid this problem?

Cheers! --M


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Ron Natalie

unread,
Nov 10, 2005, 8:42:52 AM11/10/05
to
mlimber wrote:
ot registered with my factory).
>
> Obviously, I want to avoid this suboptimal optimization. Is this
> standard-conformant behavior? Can anyone suggest a portable way to
> avoid this problem?
>
I believe the behavior you're seeing is legal. The implicit
instantiation for the A<float> doesn't necessarily happen unless
you make an object of type B or somehow use the class in a way
that it would make a difference to the program. Your use of
it as a base class in an empty wrapper class is trivial enough
that one could argue that it doesn't make a difference to the
program.

You'll have to pull one of those kludges (either touch the
internals of the template or declare an object of the class).

mlimber

unread,
Nov 10, 2005, 12:03:16 PM11/10/05
to
Ron Natalie wrote:

> mlimber wrote:
> > Obviously, I want to avoid this suboptimal optimization. Is this
> > standard-conformant behavior? Can anyone suggest a portable way to
> > avoid this problem?
> >
> I believe the behavior you're seeing is legal. The implicit
> instantiation for the A<float> doesn't necessarily happen unless
> you make an object of type B or somehow use the class in a way
> that it would make a difference to the program. Your use of
> it as a base class in an empty wrapper class is trivial enough
> that one could argue that it doesn't make a difference to the
> program.

The B class is trivial, but my real code, which demonstrates the same
problem, is not. See the other thread for a less trivial example. In
any case, my point is that the instantiation has a global side effect
that, ipso facto, makes it non-trivial and thus not suitable for
deletion by the pessimizer... er, optimizer. (Note that in these posts,
I am operating with compiler optimization entirely disabled via command
line switches.)

BTW, one of my compilers that this code must work on (VC6 with sp6)
warns if I add an explicit instantiation ("template A<float>;") in
addition to the inherited version ("warning C4660: template-class
specialization 'A<float>' is already instantiated"). If I ignore that
warning, the code generates the desired result (3), but if I "correct"
the code by removing the putatively redundant instantiation, I get the
undesired result (2). That's self-contradiction at its best.

> You'll have to pull one of those kludges (either touch the
> internals of the template or declare an object of the class).

I'm trying to avoid referencing each inherited instantiation (like
A<float>), except in the inheritance specification itself, because it's
a maintenance hassle especially when the number of classes grows large.

Cheers! --M

Greg Herlihy

unread,
Nov 10, 2005, 3:21:11 PM11/10/05
to

No, this line is a declaration for the struct B. It is not an explicit
instantiation of A<float>. If it were, it would look like this:

template A<float>;

And in fact adding this explicit instantiation produces the desired
output of 3.

> int main()
> {
> (void)B::i; // ***
> cout << GetInvocationCount() << endl;
> return 0;
> };
>
> If I run the program as is, I get 3 as expected. If, however, I run the
> program after deleting the first line of main, I get 2 because the
> instantiation of A<float> doesn't take place. This example is obviously
> trivial, but in the other thread, it has serious consequences (viz., my
> classes are not registered with my factory).

> Obviously, I want to avoid this suboptimal optimization. Is this
> standard-conformant behavior? Can anyone suggest a portable way to
> avoid this problem?

There is no "optimization" being applied here. The problem is simply an
omission on the program's part. There are only two ways to instantiate
A<float>. The first is to allocate an A<float> or an object of a
derived class; the other way is to instruct the compiler to instantiate
A<float> explicitly. This program does neither.

Greg

mlimber

unread,
Nov 11, 2005, 4:04:46 AM11/11/05
to
[snip]

1. Why does my inheritance specification not qualify as an
instantiation of the template?

2. Can someone quote the relevant portion(s) of the Standard on this
point for me?

3. Is there a way to force the compiler to instantiate the base class
(A<float>) without explicitly referencing each specialization (e.g.,
"(void)A<float>::i;", "template A<float>;")?

Cheers! --M

Alberto Ganesh Barbati

unread,
Nov 11, 2005, 4:10:56 AM11/11/05
to
mlimber wrote:
> In a recent thread on comp.lang.c++
> (http://groups.google.com/group/comp.lang.c++/browse_frm/thread/033d97e6592e0562),
> I posed a rather intricate problem, which boils down to the following:
>
> A const static member of a template class is being optimized away by
> the compiler. Unfortunately, the initialization of that member has a
> global side effect that the compiler doesn't detect. Here's a
> relatively simple example:
>
> <snip>

>
> If I run the program as is, I get 3 as expected. If, however, I run the
> program after deleting the first line of main, I get 2 because the
> instantiation of A<float> doesn't take place.

The compiler is correct. Line "struct B : A<float> {};" causes an
*implicit* instantiation of A<float>. According to 14.7.1/1:

"[...] The implicit instantiation of a class template specialization
causes the implicit instantiation of the declarations, but not of [...]
static data members [...]. [...] the specialization of the member is
implicitly instantiated when the specialization is referenced in a
context that requires the member definition to exist; in particular, the
initialization (and any associated side-effects) of a static data member
does not occur unless the static data member is itself used in a way
that requires the definition of the static data member to exist."

If you comment out line (***) which is the only reference to B::i, then
B::i need not be instantiated and therefore *must not* be instantiated.
It's not a matter of optimization, it's mandatory.

A more tricky question is: is the statement "(void)B::i;" a context that
"requires the definition of B::i to exists"? Compilers tends to
disagree, in fact, a lot of optimizers will detect that the statement
has no side-effects and will strip it entirely, thus also stripping the
initialization of B::i which *had* side-effects :-(

An even bigger problem is that the same optimizers also strip the
initialization of A::i which should not be stripped in any case,
according to 3.7.1/2. (For example VC7.1 falls in this category.)
:-((

HTH,

Ganesh

Greg Herlihy

unread,
Nov 11, 2005, 8:58:30 AM11/11/05
to

Because this statement merely declares B as a struct type; it does not
declare any B type objects. The compiler will not decide to allocate B
objects on its own, it will only do so when it sees a B type object
declared somewhere in the program.

Instantiating templates works the same way. Unless the compiler
actually sees a B type variable in the flesh, it will not go ahead and
instantiate the A<float> template just because it's needed to create B
type objects.

> 2. Can someone quote the relevant portion(s) of the Standard on this
> point for me?

"Unless a class template specialization has been explicitly
instantiated (14.7.2) or explicitly specialized (14.7.3), the class
template specialization is implicitly instantiated when the
specialization is referenced in a context that requires a
completely-defined object type or when the completeness of the class
type affects the semantics of the program."

The declaration of B is not a context that requires the instantiation
of A<float>. Constructing a B struct, would be such a context however.

> 3. Is there a way to force the compiler to instantiate the base class
> (A<float>) without explicitly referencing each specialization (e.g.,
> "(void)A<float>::i;", "template A<float>;")?

template A<float> will instantiate all methods and members of A<float>,
so, yes, it is not necessary to instantiate each member and method
explicitly individually.

Greg

mlimber

unread,
Nov 12, 2005, 5:26:48 AM11/12/05
to

Thanks, Alberto. That clarifies proper compiler behavior and proves
that my code shouldn't work on a compliant compiler.

So, my two remaining questions are:

1. Why? Can anyone explain the rationale for the behavior described in
14.7.1/1 with respect to side-effects? It's not clear to me why the
specified behavior is preferable to the one I expected.

2. Can anyone suggest a standard conformant way to force instantiation
of an inherited class like A<float> without listing each instantiation
explicitly? (Sneaky backdoors accepted.)

Cheers! --M

Alberto Ganesh Barbati

unread,
Nov 13, 2005, 10:39:20 AM11/13/05
to
mlimber wrote:
>
> So, my two remaining questions are:
>
> 1. Why? Can anyone explain the rationale for the behavior described in
> 14.7.1/1 with respect to side-effects? It's not clear to me why the
> specified behavior is preferable to the one I expected.

I'm not sure about it, but I guess it's the old C++ motto "you don't pay
for what you don't use".

>
> 2. Can anyone suggest a standard conformant way to force instantiation
> of an inherited class like A<float> without listing each instantiation
> explicitly? (Sneaky backdoors accepted.)
>

The best trick I can think of is to refer to the member in the template
destructor. Unless you never destroy objects of the derived class (very
unlikely) the destructor of the base class will need to be instantiated,
so any reference in it to the static member will trigger the member
instantiation. For example:

template<class T>
struct A
{
typedef T Type;
const static int i;

~A() { (void)&i; } // force instantiation of i
};

I don't know if this is enough for those very aggressive optimizers I
described in my previous post, but it's definitely legal and should
accomplish the task on a conformant compiler.

HTH,

Ganesh

kanze

unread,
Nov 14, 2005, 9:36:24 AM11/14/05
to
mlimber wrote:
> Alberto Ganesh Barbati wrote:
> > mlimber wrote:
> > > In a recent thread on comp.lang.c++
> > > (http://groups.google.com/group/comp.lang.c++/browse_frm/thread/033d97e6592e0562),
> > > I posed a rather intricate problem, which boils down to
> > > the following:

> > > A const static member of a template class is being
> > > optimized away by the compiler. Unfortunately, the
> > > initialization of that member has a global side effect
> > > that the compiler doesn't detect. Here's a relatively
> > > simple example:

> > > <snip>

I think from a standards point of view, it counts as a use. The
compiler may (and probably will) optimize the actual statement
away, but the statement must trigger the instantiation of the
static variable.

> > An even bigger problem is that the same optimizers also
> > strip the initialization of A::i which should not be
> > stripped in any case, according to 3.7.1/2. (For example
> > VC7.1 falls in this category.)
> > :-((

> Thanks, Alberto. That clarifies proper compiler behavior and
> proves that my code shouldn't work on a compliant compiler.

> So, my two remaining questions are:

> 1. Why? Can anyone explain the rationale for the behavior
> described in 14.7.1/1 with respect to side-effects? It's not
> clear to me why the specified behavior is preferable to the
> one I expected.

I'm not sure of what the committee members had in mind, but I
can think of one possible reason: the desire to have the same
rules for data members and functions, and the fact that you
definitly don't want to instantiate a function unless it is
used. (Consider cases like std::list<>::sort.)

> 2. Can anyone suggest a standard conformant way to force
> instantiation of an inherited class like A<float> without
> listing each instantiation explicitly? (Sneaky backdoors
> accepted.)

Well, you're going to have to list the targetted types
somewhere; there are potentially an infinit number of types over
which the template can be instantiated, and the compiler cannot
instantiate them all. So some subset must be specified, and the
only way I know of for specifying such a subset is to list all
of its elements.

--
James Kanze GABI Software
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

mlimber

unread,
Nov 15, 2005, 10:51:06 AM11/15/05
to
kanze wrote:
> mlimber wrote:
> > Alberto Ganesh Barbati wrote:
> > > mlimber wrote:
> > > > A const static member of a template class is being
> > > > optimized away by the compiler. Unfortunately, the
> > > > initialization of that member has a global side effect
> > > > that the compiler doesn't detect. Here's a relatively
> > > > simple example:
>
> I think from a standards point of view, it counts as a use. The
> compiler may (and probably will) optimize the actual statement
> away, but the statement must trigger the instantiation of the
> static variable.

According to 14.7.1/1, as quoted by Ganesh above in this thread:

"[...] The implicit instantiation of a class template specialization
causes the implicit instantiation of the declarations, but not of [...]
static data members [...]. [...] the specialization of the member is
implicitly instantiated when the specialization is referenced in a
context that requires the member definition to exist; in particular,
the initialization (and any associated side-effects) of a static data
member does not occur unless the static data member is itself used in a
way that requires the definition of the static data member to exist."

In short, side-effects of implicit instantiations (like my
instantiation in the inheritance specification) may be ignored.

[snip]


> > 1. Why? Can anyone explain the rationale for the behavior
> > described in 14.7.1/1 with respect to side-effects? It's not
> > clear to me why the specified behavior is preferable to the
> > one I expected.
>
> I'm not sure of what the committee members had in mind, but I
> can think of one possible reason: the desire to have the same
> rules for data members and functions, and the fact that you
> definitly don't want to instantiate a function unless it is
> used. (Consider cases like std::list<>::sort.)

I understand that the compiler should not instantiate functions or
static variables that are not used, but you seem to agree above with my
expectation that the side-effect should still occur. Functions, of
course, cannot have side-effects if they are not used, so it seems to
me that the rules *ought* to be different (if indeed having the same
rules was the committee's motivation for the behavior in question)
because of the different nature of variables and functions.

> > 2. Can anyone suggest a standard conformant way to force
> > instantiation of an inherited class like A<float> without
> > listing each instantiation explicitly? (Sneaky backdoors
> > accepted.)
>
> Well, you're going to have to list the targetted types
> somewhere; there are potentially an infinit number of types over
> which the template can be instantiated, and the compiler cannot
> instantiate them all. So some subset must be specified, and the
> only way I know of for specifying such a subset is to list all
> of its elements.

Right, but I would like to list the targeted types only in my
inheritance specifications, as in the OP.

Cheers! --M

mlimber

unread,
Nov 15, 2005, 7:41:33 PM11/15/05
to
Alberto Ganesh Barbati wrote:
> mlimber wrote:
[snip]

> > 2. Can anyone suggest a standard conformant way to force instantiation
> > of an inherited class like A<float> without listing each instantiation
> > explicitly? (Sneaky backdoors accepted.)
>
> The best trick I can think of is to refer to the member in the template
> destructor. Unless you never destroy objects of the derived class (very
> unlikely) the destructor of the base class will need to be instantiated,
> so any reference in it to the static member will trigger the member
> instantiation. For example:
>
> template<class T>
> struct A
> {
> typedef T Type;
> const static int i;
>
> ~A() { (void)&i; } // force instantiation of i
> };
>
> I don't know if this is enough for those very aggressive optimizers I
> described in my previous post, but it's definitely legal and should
> accomplish the task on a conformant compiler.
[snip]

Thanks for the reply. Using your code, wouldn't A<>::~A() only be
instantiated under the same circumstances as A<>::i? If so, that
appears to leave me in the same position as before since listing A<> in
an inheritance specification for another class doesn't count as an
explicit instantiation. (Practically speaking, this doesn't appear to
work on my two test-beds -- VC6 with sp6 and g++ 3.4.1, though
admittedly neither is highly conformant.)

Cheers! --M

kanze

unread,
Nov 16, 2005, 7:48:17 AM11/16/05
to
mlimber wrote:
> kanze wrote:
> > mlimber wrote:
> > > Alberto Ganesh Barbati wrote:
> > > > mlimber wrote:
> > > > > A const static member of a template class is being
> > > > > optimized away by the compiler. Unfortunately, the
> > > > > initialization of that member has a global side effect
> > > > > that the compiler doesn't detect. Here's a relatively
> > > > > simple example:

Did something get lost here? Or did I just get confused
concerning the context.

> > I think from a standards point of view, it counts as a use.
> > The compiler may (and probably will) optimize the actual
> > statement away, but the statement must trigger the
> > instantiation of the static variable.

> According to 14.7.1/1, as quoted by Ganesh above in this thread:

> "[...] The implicit instantiation of a class template specialization
> causes the implicit instantiation of the declarations, but not of [...]
> static data members [...]. [...] the specialization of the member is
> implicitly instantiated when the specialization is referenced in a
> context that requires the member definition to exist; in particular,
> the initialization (and any associated side-effects) of a static data
> member does not occur unless the static data member is itself used in a
> way that requires the definition of the static data member to exist."

Attention: this has nothing to do with "optimization". Unless
the static member is used, the compiler is not allowed to
instantiate it. It's not "optimizing the actual statement
away", but compiling what the standard requires for that
statement.

In fact, that's probably what confused me with regards to the
context. The definition of a static variable of a class
template does not, in itself, generate any code that the
compiler can optimize away -- the only code is in the
instantiations. Now, suppose that somewhere you have a
statement which uses the static variable, something like
&A<int>::i. The compiler can optimize that statement away, if
it can determine that doing so doesn't have any effect on the
observable behavior of the program (and if that is the entire
statement, determining that is trivial). Even if it optimizes
the statement away, however, it must still instantiate the
template variable.

> In short, side-effects of implicit instantiations (like my
> instantiation in the inheritance specification) may be
> ignored.

No. All "side-effects" of an instantiation, implicit or
explicit, must occur. The problem here is that there is no
instantiation, so no side-effects.

> [snip]
> > > 1. Why? Can anyone explain the rationale for the behavior
> > > described in 14.7.1/1 with respect to side-effects? It's
> > > not clear to me why the specified behavior is preferable
> > > to the one I expected.

> > I'm not sure of what the committee members had in mind, but
> > I can think of one possible reason: the desire to have the
> > same rules for data members and functions, and the fact that
> > you definitly don't want to instantiate a function unless it
> > is used. (Consider cases like std::list<>::sort.)

> I understand that the compiler should not instantiate
> functions or static variables that are not used, but you seem
> to agree above with my expectation that the side-effect should
> still occur.

If the code, as written, requires the instantiation of the
function of variable, then any "side-effects" of that
instantiation must occur.

But I'm wondering what you really mean. This is the first time
I've heard the expression "side-effects" applied to an
instantiation. Side-effects are normally what happens at
run-time; instantiation is a compile time phenomena.

> Functions, of course, cannot have side-effects if they are not
> used, so it seems to me that the rules *ought* to be different
> (if indeed having the same rules was the committee's
> motivation for the behavior in question) because of the
> different nature of variables and functions.

I like orthogonality. It's hard enough explaining when
something is intantiated, and when it isn't, without having two
distinct sets of rules.

It's very important to realize that this is not an optimization
issue. A function is either instantiated, or it isn't. If it
is instantiated, all template functions and/or classes it uses
also get instantiated, and all functions, types and objects that
it uses must exist, or it is an error, and the code won't
compile. For this reason, it is very important that functions
like std::list<>::sort not be instantiated unless they are used;
instantiating std::list<>::sort will result in a compiler error
if the type doesn't support '<', and many types that you put in
lists won't support '<'.

It's less obvious, but the same logic applies to static
variables. Suppose the initialization expression of the static
variable uses '<' on the instantiation type. Instantiating a
template may trigger other instantiations and introduce
constraints on the instantiation that are not wanted in every
case.

> > > 2. Can anyone suggest a standard conformant way to force
> > > instantiation of an inherited class like A<float> without
> > > listing each instantiation explicitly? (Sneaky backdoors
> > > accepted.)

> > Well, you're going to have to list the targetted types
> > somewhere; there are potentially an infinit number of types
> > over which the template can be instantiated, and the
> > compiler cannot instantiate them all. So some subset must
> > be specified, and the only way I know of for specifying such
> > a subset is to list all of its elements.

> Right, but I would like to list the targeted types only in my
> inheritance specifications, as in the OP.

So create a "side effect" in your inheritance specification
which triggers the instantiation of the static variable.
Alberto's suggestion of refering to the static variable in the
destructor, for example, will ensure instantiation of the static
variable any time the destructor is instantiated. And if the
class is used as a base class, the destructor *will* be
instantiated (since it is implicitly called by the destructor of
the derived class).

--
James Kanze GABI Software
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

mlimber

unread,
Nov 21, 2005, 8:21:20 PM11/21/05
to

Clarified. Thanks. The revised title for this post should be
"Suboptimal non-instantiation", but it just doesn't have the same ring.
:-)

> > In short, side-effects of implicit instantiations (like my
> > instantiation in the inheritance specification) may be
> > ignored.
>
> No. All "side-effects" of an instantiation, implicit or
> explicit, must occur. The problem here is that there is no
> instantiation, so no side-effects.

I guess the knot in my thinking has been that I attempt to instantiate
the class A<> (cf. the OP), but since A<>::i is not instantiated as a
result unless it is also explicitly (if trivially) used somewhere, the
side-effect associated with the initialization of A<>::i does not take
place. I had assumed it would.

> > [snip]
> > > > 1. Why? Can anyone explain the rationale for the behavior
> > > > described in 14.7.1/1 with respect to side-effects? It's
> > > > not clear to me why the specified behavior is preferable
> > > > to the one I expected.
>
> > > I'm not sure of what the committee members had in mind, but
> > > I can think of one possible reason: the desire to have the
> > > same rules for data members and functions, and the fact that
> > > you definitly don't want to instantiate a function unless it
> > > is used. (Consider cases like std::list<>::sort.)
>
> > I understand that the compiler should not instantiate
> > functions or static variables that are not used, but you seem
> > to agree above with my expectation that the side-effect should
> > still occur.
>
> If the code, as written, requires the instantiation of the
> function of variable, then any "side-effects" of that
> instantiation must occur.
>
> But I'm wondering what you really mean. This is the first time
> I've heard the expression "side-effects" applied to an
> instantiation. Side-effects are normally what happens at
> run-time; instantiation is a compile time phenomena.

I mean that each compile-time instantiation will have a run-time
side-effect. In fact, that is precisely what my real code tried to
achieve: registering some class with an object factory (a la _Modern
C++ Design_) at run-time as a side-effect of instantiating a static
data member. (See the new code at the bottom of this post.)

> > Functions, of course, cannot have side-effects if they are not
> > used, so it seems to me that the rules *ought* to be different
> > (if indeed having the same rules was the committee's
> > motivation for the behavior in question) because of the
> > different nature of variables and functions.
>
> I like orthogonality. It's hard enough explaining when
> something is intantiated, and when it isn't, without having two
> distinct sets of rules.

Agreed, but still, the rules should be as simple as possible but no
simpler.

> It's very important to realize that this is not an optimization
> issue. A function is either instantiated, or it isn't. If it
> is instantiated, all template functions and/or classes it uses
> also get instantiated, and all functions, types and objects that
> it uses must exist, or it is an error, and the code won't
> compile. For this reason, it is very important that functions
> like std::list<>::sort not be instantiated unless they are used;
> instantiating std::list<>::sort will result in a compiler error
> if the type doesn't support '<', and many types that you put in
> lists won't support '<'.
>
> It's less obvious, but the same logic applies to static
> variables. Suppose the initialization expression of the static
> variable uses '<' on the instantiation type. Instantiating a
> template may trigger other instantiations and introduce
> constraints on the instantiation that are not wanted in every
> case.

Ah ha. This seems to be what I was looking for as far as a rationale
for the rule, and now it makes sense. The only obstacle left is to get
around it as portably as possible.. ;-)

> > > > 2. Can anyone suggest a standard conformant way to force
> > > > instantiation of an inherited class like A<float> without
> > > > listing each instantiation explicitly? (Sneaky backdoors
> > > > accepted.)
>
> > > Well, you're going to have to list the targetted types
> > > somewhere; there are potentially an infinit number of types
> > > over which the template can be instantiated, and the
> > > compiler cannot instantiate them all. So some subset must
> > > be specified, and the only way I know of for specifying such
> > > a subset is to list all of its elements.
>
> > Right, but I would like to list the targeted types only in my
> > inheritance specifications, as in the OP.
>
> So create a "side effect" in your inheritance specification
> which triggers the instantiation of the static variable.
> Alberto's suggestion of refering to the static variable in the
> destructor, for example, will ensure instantiation of the static
> variable any time the destructor is instantiated. And if the
> class is used as a base class, the destructor *will* be
> instantiated (since it is implicitly called by the destructor of
> the derived class).

I have tried this and it works on VC++ 6 with sp6 and g++ 3.4.4, but
not with one essential element in my chain, viz. Texas Instruments C++
v.5.1.0, which I believe uses the EDG front-end. Is this a
bug/non-conformancy in their compiler? Here's the code that I used to
test it:

#include <cassert>
#include <memory> // For auto_ptr

/////////////////////////////////////////////////////////////
// Factory class simplified from Loki::Factory
template<class Base, class Derived>
class Factory
{
typedef Base* (*CreationFn)();
CreationFn creationFn_;
Factory() : creationFn_(0) {}

public:
static Factory& Instance() { static Factory f; return f; }

bool Register( const CreationFn creationFn )
{
creationFn_ = creationFn;
return true;
}

Base* Create() const
{
assert( creationFn_ != 0 );
return creationFn_();
}
};

/////////////////////////////////////////////////////////////
// This policy class automatically registers a Base/Derived
// pair with the appropriate factory.
template<class Base, class Derived>
class FactoryRegistrar
{
typedef Factory<Base,Derived> MyFactory;
static const bool isRegistered_;

protected:
virtual ~FactoryRegistrar()
{
assert( &isRegistered_ && isRegistered_ );
}
};

/////////////////////////////////////////////////////////////
// Initialize static member variable, whose real purpose
// is to register a with the factory as a side-effect
template<class Base, class Derived>
const bool FactoryRegistrar<Base,Derived>::isRegistered_
= FactoryRegistrar<Base,Derived>::MyFactory
::Instance().Register( Derived::Create );

/////////////////////////////////////////////////////////////
// Base class
struct B
{
virtual ~B() {}
virtual int GetInt() const { return -1; }
};

/////////////////////////////////////////////////////////////
// Derived class
struct D
: public B
, public FactoryRegistrar<B,D>
{
static B* Create() { return new D; }
int GetInt() const { return 42; }
};

/////////////////////////////////////////////////////////////
int main()
{
std::auto_ptr<B> pb( Factory<B,D>::Instance().Create() );
return pb->GetInt();
}


On the first two compilers mentioned, this works fine. On the last, it
asserts in Factory::Create().

Cheers! --M

Greg Herlihy

unread,
Nov 22, 2005, 6:29:24 AM11/22/05
to
mlimber wrote:
> kanze wrote:
> > mlimber wrote:
> > > kanze wrote:
> > > > mlimber wrote:
...

Remove the assert from FactoryRegistrar's destructor and run the
program again. The assertion in Factory::Create will fail because the
compiler no longer has a reason to instantiate FactoryRegistrar. In
other words it is only the presence of the assert that ensures
FactoryRegistrar's instantiation - an inherently fragile technique and
one suited only for debug builds at that. A much more reliable solution
would be to instruct the compiler to instantiate FactoryRegistrar
explicitly (see below).

> /////////////////////////////////////////////////////////////
> // Initialize static member variable, whose real purpose
> // is to register a with the factory as a side-effect
> template<class Base, class Derived>
> const bool FactoryRegistrar<Base,Derived>::isRegistered_
> = FactoryRegistrar<Base,Derived>::MyFactory
> ::Instance().Register( Derived::Create );

It is important to note that the above line does not instantiate
FactoryRegistrar<Base, Derived>::isRegistered. It merely instructs the
compiler how to initialize isRegistered should it be instantiated.
Clearly if the compiler has no cause to instantiate isRegistered, then
no call will be made FactoryRegistrar::Register() method in order to
initialize it.

A program that has a different set of expectations for these methods
and members will have to have a different set of declarations. For
example, if Register() must be called then it should be called to
initialize a non-template data member or global variable - either one
of which would be certain to exist. Alternately, if both Register must
be called and isRegistered must be instantiated, then inserting an
explicit template instantiation at this point would be in order:

template class FactoryRegistrar<Base, Derived>;

With this declaration FactoryRegistar<Base, Derived>- including all of
its methods and members - will be instantiated. There is no need to use
compiler-specific, obscure and error-prone methods to accomplish a task
that the language already specifically accomodates.

There is however an inherent contradiction in an explicit
instantiation. The entire reason for placing code in a template is to
make that code's presence in a program, conditional. It becomes part of
the program only if code already in the program needs it. Otherwise it
will be left out. An explicit instantiation adds the code to the
program whether or not it is needed by the rest of the program. So one
question raised is why package this code in a template if its presence
is required, or, alternately, why add the code to the program absent
any need for it?

Greg

kanze

unread,
Nov 22, 2005, 2:30:22 PM11/22/05
to
mlimber wrote:

>> kanze wrote:


>>> > No. All "side-effects" of an instantiation, implicit or
>>> > explicit, must occur. The problem here is that there is no
>>> > instantiation, so no side-effects.


>> I guess the knot in my thinking has been that I attempt to
>> instantiate the class A<> (cf. the OP), but since A<>::i is
>> not instantiated as a result unless it is also explicitly (if
>> trivially) used somewhere, the side-effect associated with the
>> initialization of A<>::i does not take place. I had assumed it
>> would.


Understandable. I think that always instantiating the entire
class would be more intuitive. But it has the problem I
mentionned below -- in the case of smart pointers and iterators,
it is almost a killer problem, since it means that you have to
choose between allowing instantiation on non-class types and
supporting -> when instantiated over a class type.


>>>> > > [snip]


>>> > So create a "side effect" in your inheritance specification
>>> > which triggers the instantiation of the static variable.
>>> > Alberto's suggestion of refering to the static variable in
>>> > the destructor, for example, will ensure instantiation of
>>> > the static variable any time the destructor is instantiated.
>>> > And if the class is used as a base class, the destructor
>>> > *will* be instantiated (since it is implicitly called by the
>>> > destructor of the derived class).


>> I have tried this and it works on VC++ 6 with sp6 and g++
>> 3.4.4, but not with one essential element in my chain, viz.
>> Texas Instruments C++ v.5.1.0, which I believe uses the EDG
>> front-end. Is this a bug/non-conformancy in their compiler?


Possibly. It's also possible that the compiler has decided to
use differed initialization. It would be the first compiler
I've heard of to do this, however, if this were the case.


>> Here's the code that I used to test it:


[I've clipped all but the relevant parts...]


>> template<class Base, class Derived>
>> class Factory
>> {
>> typedef Base* (*CreationFn)();
>> CreationFn creationFn_;
>> Factory() : creationFn_(0) {}


>> public:
>> static Factory& Instance() { static Factory f; return f; }
>>
>> bool Register( const CreationFn creationFn )
>> {
>> creationFn_ = creationFn;
>> return true;
>> }


>> Base* Create() const
>> {
>> assert( creationFn_ != 0 );
>> return creationFn_();
>> }
>> };


>> /////////////////////////////////////////////////////////////
>> // This policy class automatically registers a Base/Derived
>> // pair with the appropriate factory.
>> template<class Base, class Derived>
>> class FactoryRegistrar
>> {
>> typedef Factory<Base,Derived> MyFactory;
>> static const bool isRegistered_;


>> protected:
>> virtual ~FactoryRegistrar()
>> {
>> assert( &isRegistered_ && isRegistered_ );


First, I would definitly move the references outside the assert.
Asserts occasionally get stripped in production builds.

Second, if this is a policy class, then there probably aren't
any instances of it. And if there aren't any instances to be
destructed, the destructor won't be instantiated. The problem
is, of course, that you're code


>> }
>> };


>> /////////////////////////////////////////////////////////////
>> // Initialize static member variable, whose real purpose
>> // is to register a with the factory as a side-effect
>> template<class Base, class Derived>
>> const bool FactoryRegistrar<Base,Derived>::isRegistered_
>> = FactoryRegistrar<Base,Derived>::MyFactory
>> ::Instance().Register( Derived::Create );


>> /////////////////////////////////////////////////////////////
>> // Base class
>> struct B
>> {
>> virtual ~B() {}
>> virtual int GetInt() const { return -1; }
>> };


>> /////////////////////////////////////////////////////////////
>> // Derived class
>> struct D
>> : public B
>> , public FactoryRegistrar<B,D>


That should do it, I think. If you explicitly declare an empty,
inline destructor, I'm sure it will. Since you don't, I think
you do need something which invokes the constructor, since it is
the constructor which will require the destructor, thus
triggering its instantiation.

Note that here, we have a case where the compiler instantiates a
non-template -- the implicitly declared destructor. I don't
think the rules governing this aren't 100% the same as those for
templates class member functions, but there is still the rule
that it won't happen unless necessary. For the same reason: we
want the rules to be orthogonal accross all implicitly defined
special member functions, and we don't want to instantation the
copy constructor or the assignment operator, provoking an error
if they aren't instantiable (e.g. because the class inherited
from boost::noncopiable).

In this case, Derived::Create seems to be the actual function.
It certainly has a new D in it, so it must call the (implicitly
defined default) constructor. And this constructor must
instantiate the (implicit) destructor, since it must create a
vtbl with its address.

I *think* that this is a requirement of the standard, and if the
compiler is failing to do it, it is an error. Given the
reputation of EDG, I'd feel better if they'd confirm it,
however.

As a work around, you might try making the function Create
non-inline.


>> {
>> static B* Create() { return new D; }
>> int GetInt() const { return 42; }
>> };


>> /////////////////////////////////////////////////////////////
>> int main()
>> {
>> std::auto_ptr<B> pb( Factory<B,D>::Instance().Create() );
>> return pb->GetInt();
>> }


>> On the first two compilers mentioned, this works fine. On the
>> last, it asserts in Factory::Create().


As I said, it *looks* to me like an error. Supposing that the
compiler isn't doing differed instantiation (but if everything
is in one translation unit, as in your example, that shouldn't
change anything). I'd send in a bug report, and see what
happens. But don't expect too much, too fast. I can imagine
that your code would confuse most support engineers. And your
interlocuter is Texas, and not EDG. (EDG doesn't have support
engineers, because they don't sell to end users. If we're
lucky, however, someone from EDG will read this, and respond.)

--
James Kanze GABI Software
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

kanze

unread,
Nov 22, 2005, 2:35:59 PM11/22/05
to
Greg Herlihy wrote:

>> mlimber wrote:
>
>>> > kanze wrote:
>>
>>>> > > mlimber wrote:
>>>
>>>>> > > > kanze wrote:
>>>>
>>>>>> > > > > mlimber wrote:
>
>> ...

>>> > template<class Base, class Derived>
>>> > class FactoryRegistrar
>>> > {
>>> > typedef Factory<Base,Derived> MyFactory;
>>> > static const bool isRegistered_;


>>> > protected:
>>> > virtual ~FactoryRegistrar()
>>> > {
>>> > assert( &isRegistered_ && isRegistered_ );
>>> > }
>>> > };


>> Remove the assert from FactoryRegistrar's destructor and run
>> the program again. The assertion in Factory::Create will fail
>> because the compiler no longer has a reason to instantiate
>> FactoryRegistrar.


A reference to isRegistered_ is certainly needed, even if
assertions are turned off. But I doubt that that is his problem
for the moment.


>> In other words it is only the presence of the assert that
>> ensures FactoryRegistrar's instantiation - an inherently
>> fragile technique and one suited only for debug builds at
>> that. A much more reliable solution would be to instruct the
>> compiler to instantiate FactoryRegistrar explicitly (see
>> below).


It shouldn't be necessary, and it is what he is trying to avoid.

D derives from FactoryRegistrar<B,D>. Which means that D's
destructor will invoke FactoryRegistrar's destructor, which will
trigger the necessary instantiations (provided that assertions
have not been turned off).

His problem is that D's destructor is implicit. Which means
that it, like template member functions, will only be
instantiated if it is actually needed. In his code, the only
place it is needed is in fact the constructor of D, which must
put its address in the vtbl. Not dynamically, of course, but if
you need the address, the function must be instantiated. Except
that all of the constructors are also implicit, and their
instantion needs triggering as well. There is, however, a new D
in a member function of D. Since D is NOT a template, its
explicitly declared and defined member functions *are*
instantiated. Unconditionnally. And that should trigger the
chain of implicit instantiations necessary to provoke the
instantiation of isRegistered_, and thus its initialization.


>>> > /////////////////////////////////////////////////////////////
>>> > // Initialize static member variable, whose real purpose
>>> > // is to register a with the factory as a side-effect
>>> > template<class Base, class Derived>
>>> > const bool FactoryRegistrar<Base,Derived>::isRegistered_
>>> > = FactoryRegistrar<Base,Derived>::MyFactory
>>> > ::Instance().Register( Derived::Create );


>> It is important to note that the above line does not
>> instantiate FactoryRegistrar<Base, Derived>::isRegistered.


I think he knows that.


>> It merely instructs the compiler how to initialize
>> isRegistered should it be instantiated. Clearly if the
>> compiler has no cause to instantiate isRegistered, then no
>> call will be made FactoryRegistrar::Register() method in order
>> to initialize it.


It's not quite that simple. There are very strict rules as to
when something is instantiated, and when it is not. It's not up
to a whim of the compiler.


>> A program that has a different set of expectations for these
>> methods and members will have to have a different set of
>> declarations. For example, if Register() must be called then
>> it should be called to initialize a non-template data member
>> or global variable - either one of which would be certain to
>> exist. Alternately, if both Register must be called and
>> isRegistered must be instantiated, then inserting an explicit
>> template instantiation at this point would be in order:


>> template class FactoryRegistrar<Base, Derived>;


>> With this declaration FactoryRegistar<Base, Derived>-
>> including all of its methods and members - will be
>> instantiated. There is no need to use compiler-specific,
>> obscure and error-prone methods to accomplish a task that the
>> language already specifically accomodates.


The problem is that this must be done for every derived class.
I think that the goal is to use some tricky code once, somewhere
deep down and hidden away, so that client code is freed from the
responsibility, and doesn't have to do anything.


>> There is however an inherent contradiction in an explicit
>> instantiation. The entire reason for placing code in a
>> template is to make that code's presence in a program,
>> conditional.


Not really. Typically, that is the reason for placing code in a
library, rather than linking in the object file directly. The
reason for using templates is typically to factorize code that
is common except for a few specific parameters (particularly
types).


>> It becomes part of the program only if code already in the
>> program needs it.


I don't think that that was even part of the original templates.
That feature is basically there to allow classes like std::list
to support operations like sort (which require operator <) while
still being instantiable over types that didn't define operator
< (provided sort was not called).


>> Otherwise it will be left out. An explicit instantiation adds
>> the code to the program whether or not it is needed by the
>> rest of the program.


An explicit instantiation adds *all* of the code of the class to
the program, even if all that is wanted is a single function or
static variable. In this case, it doesn't matter, but in other
cases, it could add constraints that aren't wanted.


>> So one question raised is why package this code in a template
>> if its presence is required, or, alternately, why add the code
>> to the program absent any need for it?


It's a classical problem. Even without templates: the question
as to why some static initialization hasn't taken place when the
only references to code in the translation unit is through
virtual functions and the translation unit was put in a library
is almost a FAQ. This is the first time I've seen the problem
depend on a chain of implicit template instantiations (and
instantiations of automatically provided special member
fucntions), though.

--
James Kanze GABI Software
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

mlimber

unread,
Nov 23, 2005, 8:45:58 AM11/23/05
to
Greg Herlihy wrote:
> mlimber wrote:

Using assert is inherently fragile, but it was just for test purposes.
If you prefer, replace the asserts with calls to this function:

inline void CondTerminate( const bool b )
{
if( !b ) exit( -1 );
}

I get the same results as above on my three compilers.

> > /////////////////////////////////////////////////////////////
> > // Initialize static member variable, whose real purpose
> > // is to register a with the factory as a side-effect
> > template<class Base, class Derived>
> > const bool FactoryRegistrar<Base,Derived>::isRegistered_
> > = FactoryRegistrar<Base,Derived>::MyFactory
> > ::Instance().Register( Derived::Create );
>
> It is important to note that the above line does not instantiate
> FactoryRegistrar<Base, Derived>::isRegistered. It merely instructs the
> compiler how to initialize isRegistered should it be instantiated.
> Clearly if the compiler has no cause to instantiate isRegistered, then
> no call will be made FactoryRegistrar::Register() method in order to
> initialize it.

Right, as already discussed above. (See for instance the "knot in my
thinking" in the post you responded to.)

> A program that has a different set of expectations for these methods
> and members will have to have a different set of declarations. For
> example, if Register() must be called then it should be called to
> initialize a non-template data member or global variable - either one
> of which would be certain to exist. Alternately, if both Register must
> be called and isRegistered must be instantiated, then inserting an
> explicit template instantiation at this point would be in order:
>
> template class FactoryRegistrar<Base, Derived>;
>
> With this declaration FactoryRegistar<Base, Derived>- including all of
> its methods and members - will be instantiated. There is no need to use
> compiler-specific, obscure and error-prone methods to accomplish a task
> that the language already specifically accomodates.

This is precisely what I am trying to avoid doing. To reiterate, my
goal is to register with the factory by inheriting from
FactoryRegistrar<> but not explicitly referencing each instantiation
(except in the inheritance specification). It was suggested by others
that I simply refer to FactoryRegistrar<>::isRegistered in
FactoryRegistrar<>::~FactoryRegistrar() (e.g., "(void)&isRegistered;"),
which should portably force instantiation. However, this did not work
on one of my platforms, and I wonder if it's a bug/non-conformity to
the Standard or if the method itself is flawed.

> There is however an inherent contradiction in an explicit
> instantiation. The entire reason for placing code in a template is to
> make that code's presence in a program, conditional. It becomes part of
> the program only if code already in the program needs it. Otherwise it
> will be left out. An explicit instantiation adds the code to the
> program whether or not it is needed by the rest of the program.

Though, practically speaking, the linker will strip any unreferenced
code, at least in an executable.

> So one
> question raised is why package this code in a template if its presence
> is required, or, alternately, why add the code to the program absent
> any need for it?

The code *is* needed in order to register with the factory and create
objects, but the compiler doesn't recognize it as such without a second
(and putatively redundant) explicit instantiation. I am seeking a
portable way to get around this "redundant" instantiation.

Cheers! --M

mlimber

unread,
Dec 1, 2005, 7:32:39 PM12/1/05
to
kanze wrote:
> mlimber wrote:
> >> kanze wrote:
[snip]

Right. That was just for my test program, but just to be safe, I
replaced it with the code given in my response to Greg Herlihy.

> Second, if this is a policy class, then there probably aren't
> any instances of it. And if there aren't any instances to be
> destructed, the destructor won't be instantiated. The problem
> is, of course, that you're code

It appears that you were cut off here. Would you care to finish the
sentence?

FYI, I received the following response from Texas Instruments customer
support (punctuation errors in original):

"The behavior reported by you looks like a bug and I have filled a bug
report for the issue Bug No. is SDSCM00002617
Concerned engineer will look into the issue and this will be fixed in
future compiler releases"

Thanks to everyone for the help.

Cheers! --M

kanze

unread,
Dec 2, 2005, 12:08:04 PM12/2/05
to
mlimber wrote:

>> kanze wrote:
>
>>> > mlimber wrote:
>>
>>>>> > >> kanze wrote:
>
>> [snip]

>>> > Second, if this is a policy class, then there probably
>>> > aren't any instances of it. And if there aren't any
>>> > instances to be destructed, the destructor won't be
>>> > instantiated. The problem is, of course, that you're code


>> It appears that you were cut off here. Would you care to
>> finish the sentence?


I does, doesn't it. It's been a while, so I don't really
remember what I was saying. (It doesn't sound right, either. I
couldn't have been saying that you are code.) Probably
something along the lines of "your code won't be instantiated
unless the function which uses it is instantiated", or something
similar.

[...]

>>> > As I said, it *looks* to me like an error. Supposing that
>>> > the compiler isn't doing differed instantiation (but if
>>> > everything is in one translation unit, as in your example,
>>> > that shouldn't change anything). I'd send in a bug report,
>>> > and see what happens. But don't expect too much, too fast.
>>> > I can imagine that your code would confuse most support
>>> > engineers. And your interlocuter is Texas, and not EDG.
>>> > (EDG doesn't have support engineers, because they don't sell
>>> > to end users. If we're lucky, however, someone from EDG
>>> > will read this, and respond.)


>> FYI, I received the following response from Texas Instruments
>> customer support (punctuation errors in original):


>> "The behavior reported by you looks like a bug and I have
>> filled a bug report for the issue Bug No. is SDSCM00002617
>> Concerned engineer will look into the issue and this will be
>> fixed in future compiler releases"


Well, that's good news of a sort. If you can afford to wait for
the future release.

--
James Kanze GABI Software
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

0 new messages