Template instantiation question

M

mlimber

This is a repost (with slight modifications) from
comp.lang.c++.moderated in an effort to get some response.

I am using Loki's Factory as presented in _Modern C++ Design_ for
message passing in an embedded environment with multiple processors. I
created a policy for classes, which, I had hoped, would automatically
register the class with the appropriate factory:

// In some header file...
#include <cassert>
#include "Loki/Singleton.h"
#include "Loki/Factory.h"
#include "Loki/TypeManip.h"
#include "Loki/static_check.h"

// Requirements: ConcreteClass must implement a static method GetID()
template<class AbstractClass, class ConcreteClass>
class DefaultCreationPolicy
{
public:
static AbstractClass* Create()
{
// Verify that the given types are actually super and sub classes
STATIC_CHECK(
SUPERSUBCLASS( AbstractClass, ConcreteClass ),
types_are_not_super_and_subclass
);

assert( m_registered );

return new ConcreteClass;
}

static bool IsRegistered() { return m_registered; }

protected:
DefaultCreationPolicy() {}
~DefaultCreationPolicy() {}

private:
typedef Loki::SingletonHolder<Loki::Factory< AbstractClass, int>
theFactory;

static const bool m_registered; // One per template instantiation
};

// Register all our subclasses with their respective factories.
// (Note that these static members appear in header file because
// they are themselves templates. The compiler will ensure that
// there is only one instance of each globally.)

template<class AbstractClass, class ConcreteClass>
const bool
DefaultCreationPolicy<AbstractClass,ConcreteClass>::m_registered
= DefaultCreationPolicy<AbstractClass,ConcreteClass>::
theFactory::Instance().Register(
ConcreteClass::GetID(),
ConcreteClass::Create
);

I use this code like this:

template<int id>
struct AssignID
{
static int GetID() { return id; }
};

struct MyBase
{
virtual ~MyBase() {}
};

enum { DERIVED_1, DERIVED_2 };

struct Derived1
: MyBase
, AssignID< DERIVED_1 >
, DefaultCreationPolicy<MyBase,Derived1>
{};

struct Derived2
: MyBase
, AssignID< DERIVED_2 >
, DefaultCreationPolicy<MyBase,Derived2>
{};

In theory, this code should automatically register these two classes
with a MyBase factory so that I can create objects using the factory
like this:

// ...

typedef Loki::SingletonHolder< Loki::Factory<MyBase, int> >
theFactory;

void Foo()
{
try
{
typedef std::auto_ptr<MyBase> Ptr;
Ptr p1( theFactory::Instance().CreateObject( DERIVED_1 ) );
Ptr p2( theFactory::Instance().CreateObject( DERIVED_2 ) );
// ... use p1 and p2 ...
}
catch( const std::exception& e )
{
std::cerr << e.what() << std::endl;
}
}

The problem is that, with just the code above, the
DefaultCreationPolicy<> policy never gets instantiated for Derived1 or
Derived2, and thus either call to Factory<>::CreateObject() will throw
because the classes are not registered. If, however, I manually access
the creation policy somewhere like this:

void Bar()
{
if( Derived1::IsRegistered() && Derived2::IsRegistered() )
{
Foo();
}
}

then, Derived1 and Derived2 *do* get instantiated and registered
automatically as desired.

Can anyone explain why this happens and perhaps suggest a better way of
forcing the instantiation of these classes? I was trying to avoid
manually referencing each of the factory-creatable classes, since in my
real code, it's an annoyance to have to do so -- especially three years
from now when I or someone else will need to add some additional
subclasses to MyBase and will have to remember to manually reference
the creation policy.

Cheers! --M
 
M

mlimber

mlimber said:
This is a repost (with slight modifications) from
comp.lang.c++.moderated in an effort to get some response.

I am using Loki's Factory as presented in _Modern C++ Design_ for
message passing in an embedded environment with multiple processors.
[snip]

BTW, the code from Loki can be found here:

http://sourceforge.net/projects/loki-lib/

Cheers! --M
 
J

Jonathan Mcdougall

mlimber said:
This is a repost (with slight modifications) from
comp.lang.c++.moderated in an effort to get some response.

I am using Loki's Factory as presented in _Modern C++ Design_ for
message passing in an embedded environment with multiple processors. I
created a policy for classes, which, I had hoped, would automatically
register the class with the appropriate factory:

// In some header file...
#include <cassert>
#include "Loki/Singleton.h"
#include "Loki/Factory.h"
#include "Loki/TypeManip.h"
#include "Loki/static_check.h"

# include said:
// Requirements: ConcreteClass must implement a static method GetID()
template<class AbstractClass, class ConcreteClass>
class DefaultCreationPolicy
{
public:
static AbstractClass* Create()
{
// Verify that the given types are actually super and sub classes
STATIC_CHECK(
SUPERSUBCLASS( AbstractClass, ConcreteClass ),
types_are_not_super_and_subclass
);

assert( m_registered );

return new ConcreteClass;
}

static bool IsRegistered() { return m_registered; }

protected:
DefaultCreationPolicy() {}
~DefaultCreationPolicy() {}

private:
typedef Loki::SingletonHolder<Loki::Factory< AbstractClass, int>

Missing a > here.
theFactory;

static const bool m_registered; // One per template instantiation
};

// Register all our subclasses with their respective factories.
// (Note that these static members appear in header file because
// they are themselves templates. The compiler will ensure that
// there is only one instance of each globally.)

template<class AbstractClass, class ConcreteClass>
const bool
DefaultCreationPolicy<AbstractClass,ConcreteClass>::m_registered
= DefaultCreationPolicy<AbstractClass,ConcreteClass>::
theFactory::Instance().Register(
ConcreteClass::GetID(),
ConcreteClass::Create
);

I use this code like this:

template<int id>
struct AssignID
{
static int GetID() { return id; }
};

struct MyBase
{
virtual ~MyBase() {}
};

enum { DERIVED_1, DERIVED_2 };

struct Derived1
: MyBase
, AssignID< DERIVED_1 >
, DefaultCreationPolicy<MyBase,Derived1>
{};

struct Derived2
: MyBase
, AssignID< DERIVED_2 >
, DefaultCreationPolicy<MyBase,Derived2>
{};

In theory, this code should automatically register these two classes
with a MyBase factory so that I can create objects using the factory
like this:

// ...

typedef Loki::SingletonHolder< Loki::Factory<MyBase, int> >
theFactory;

void Foo()
{
try
{
typedef std::auto_ptr<MyBase> Ptr;
Ptr p1( theFactory::Instance().CreateObject( DERIVED_1 ) );
Ptr p2( theFactory::Instance().CreateObject( DERIVED_2 ) );
// ... use p1 and p2 ...
}
catch( const std::exception& e )
{
std::cerr << e.what() << std::endl;
}
}

The problem is that, with just the code above, the
DefaultCreationPolicy<> policy never gets instantiated for Derived1 or
Derived2, and thus either call to Factory<>::CreateObject() will throw
because the classes are not registered. If, however, I manually access
the creation policy somewhere like this:

void Bar()
{
if( Derived1::IsRegistered() && Derived2::IsRegistered() )
{
Foo();
}
}

then, Derived1 and Derived2 *do* get instantiated and registered
automatically as desired.

Can anyone explain why this happens

The compiler optmizes away a static data member (nor a function for
that matter) if it is not used. For example,

DefaultCreationPolicy<MyBase,Derived1>::m_registered

in the debugger results in an error, unless I use m_registered.
and perhaps suggest a better way of
forcing the instantiation of these classes?

You have to force the compiler to use that member. Since this is
optimization, I think this is implementation dependent. On Visual C++
7.0, this works:

template <class Abstract, class Derived, int id>
struct a_concrete_class
: Abstract,
AssignID<id>,
DefaultCreationPolicy<Abstract, Derived>
{
a_concrete_class()
{
DefaultCreationPolicy<Abstract, Derived>::IsRegistered();
}
};

struct Derived1
: a_concrete_class<MyBase, Derived1, DERIVED_1>
{
Derived1()
{
}
};

Note that
1) I factored everything in a_concrete_class (to make it easier)
2) Derived1 has a default constructor

That's the simplest solution I found.


Jonathan
 
K

Kaz Kylheku

mlimber said:
Can anyone explain why this happens and perhaps suggest a better way of

Only those parts of a template get generated that you use. This is a
nice thing because it reduces bloat. In some cases, certain features of
a template cannot be generated.

Suppose you have some template class TC parametrized on a type <T> and
it contains some function foo() whose body assumes that it can call
something T::bar(). Now you instantiate that template over a class T
which has no bar() function. That is okay if you don't use foo()! Since
foo() isn't called, template <class T> TC::foo(); is never generated,
and so the call to nonexistent bar() isn't an issue.

In your case there is a tricky problem, because a static member
variable is being optimized away, but that static member variable has a
non-trivial initializer, a function call. And you are depending on that
function call to do something important. Oops!
forcing the instantiation of these classes?

Maybe the explicit template instantiation syntax will do the trick for
you. Pick some translation unit where you want the template
instantiation to live and do:

template class X<A, B, ...>;

where X is the template class, and A, B, ... are the template parameter
types for which you want ot generate the instantiation.

The explicit instantiation request will generate the whole darn
template.

It's a good idea to split the template into two header files. One which
declares the template class and any inline functions, and one which
implements the noninline functions, and static members.

Pick a place where you want to instantiate it, include all the
parameter types there, and put in all the instantiation requests. To
reduce compile dependencies, you can introduce multiple modules for
instantiating the same template over different types. I.e. suppose you
wanted to instantiate a one-parameter template ten times for ten
different classes. You could include the declarations of all ten of
those classes and the definition of the template in one place, and
write ten instantiation requests there. But that module would have to
be recompiled whenever you touch any of those classes.
I was trying to avoid
manually referencing each of the factory-creatable classes, since in my
real code, it's an annoyance to have to do so -- especially three years
from now when I or someone else will need to add some additional
subclasses to MyBase and will have to remember to manually reference
the creation policy.

With this explicit instantiation stuff, you can set it up so that the
compilation will fail if someone adds new classes, but doesn't add the
instantiation requests for them.

How? Well, for instance your classes have constructors. You are
actually inheriting from these template classes, and instatiating the
derived objects.

If you give the template a non-inlined constructor, and the template
definition isn't included in the program and subject to some implicit
or explicit instantiation, then the constructor won't exist and the
program won't link.

Speaking of which, can't you put the reference to m_registered into the
template's constructor?

template<class AbstractClass, class ConcreteClass>
class DefaultCreationPolicy
{
public:
// How about this!!
DefaultCreationPolicy()
{
(void) IsRegistered(); // touch that static member
}

static AbstractClass* Create()
// ... etc ..

Since the constructor is called somewhere, it drags in the dependency
on IsRegistered.




{
// Verify that the given types are actually super and sub classes
 
K

Kaz Kylheku

Jonathan said:
You have to force the compiler to use that member. Since this is
optimization, I think this is implementation dependent. On Visual C++
7.0, this works:

template <class Abstract, class Derived, int id>
struct a_concrete_class
: Abstract,
AssignID<id>,
DefaultCreationPolicy<Abstract, Derived>
{
a_concrete_class()
{
DefaultCreationPolicy<Abstract, Derived>::IsRegistered();
}
};

Is there are reason why the DefaultCreationPolicy<> constructor cannot
do this IsRegistered() call, so that the user of the class doesn't have
to know about it? Why pass the responsibility to the derived class?

It's not actually important for the call to take place.

It's because the call exists that the compiler generates the the
IsRegistered() function, which creates a dependency on the m_registered
variable, that in turn is instantiated, which brings in the call to the
registration function.
 
M

mlimber

Kaz Kylheku wrote:
[snip]
In your case there is a tricky problem, because a static member
variable is being optimized away, but that static member variable has a
non-trivial initializer, a function call. And you are depending on that
function call to do something important. Oops!

Right: the non-trivial initializer is the sticking point. Is this
behavior regulated by the Standard? (Jonathan suggests not in his
previous response.)
Maybe the explicit template instantiation syntax will do the trick for
you. Pick some translation unit where you want the template
instantiation to live and do:

template class X<A, B, ...>;

where X is the template class, and A, B, ... are the template parameter
types for which you want ot generate the instantiation.

The explicit instantiation request will generate the whole darn
template.

It's a good idea to split the template into two header files. One which
declares the template class and any inline functions, and one which
implements the noninline functions, and static members.

Pick a place where you want to instantiate it, include all the
parameter types there, and put in all the instantiation requests. To
reduce compile dependencies, you can introduce multiple modules for
instantiating the same template over different types. I.e. suppose you
wanted to instantiate a one-parameter template ten times for ten
different classes. You could include the declarations of all ten of
those classes and the definition of the template in one place, and
write ten instantiation requests there. But that module would have to
be recompiled whenever you touch any of those classes.


With this explicit instantiation stuff, you can set it up so that the
compilation will fail if someone adds new classes, but doesn't add the
instantiation requests for them.

How? Well, for instance your classes have constructors. You are
actually inheriting from these template classes, and instatiating the
derived objects.

If you give the template a non-inlined constructor, and the template
definition isn't included in the program and subject to some implicit
or explicit instantiation, then the constructor won't exist and the
program won't link.

Jonathan's suggestion of using the client's ctor has the advantage of
locating the reference to the static variable in the client ctor rather
than at some unrelated or namespace scope but the disadvantage of still
being easily forgetable and thus a maintenance hassle.

In MC++D, all factory-creatable classes must be manually registered
somewhere, and my intent was to automate that process. Using the
DefaultCreationPolicy template class and your technique, I can
partially automate it and force the compiler (or linker?) to issue
errors if new classes forget to finish the job by referencing the
static variable. I was hoping to do one better by not having to
explicitly specify each instantiation at all (except in the inheritance
specification for my factory-creatable classes, of course). This may be
impossible, but it will depend on the answer to the Standard question
at the top of this post.
Speaking of which, can't you put the reference to m_registered into the
template's constructor?

template<class AbstractClass, class ConcreteClass>
class DefaultCreationPolicy
{
public:
// How about this!!
DefaultCreationPolicy()
{
(void) IsRegistered(); // touch that static member
}

static AbstractClass* Create()
// ... etc ..

Since the constructor is called somewhere, it drags in the dependency
on IsRegistered.

Unfortunately, It doesn't seem to work on VC++ 6 or gnu 3.4.1 whether
or not the constructor is inline.

Cheers! --M
 
K

Kaz Kylheku

mlimber said:
Right: the non-trivial initializer is the sticking point. Is this
behavior regulated by the Standard? (Jonathan suggests not in his
previous response.)

No idea.
Unfortunately, It doesn't seem to work on VC++ 6 or gnu 3.4.1 whether
or not the constructor is inline.

Interesting. It seems that they realize that IsRegistered() does
``nothing''. After all, it only accesses a non-volatile variable and
throws away the result. Hey, since nothing else uses that variable, why
not throw it away---initializer with side effects be damned! :)

(I wonder if declaring that static member volatile would change the
behavior, hmm!)

In any case, you can do your own initialization, instead of relying on
the static variable's initializer.

Just replace the (void) IsRegistered() with EnsureRegistration().
EnsureRegistration() actually does something like this:

if (!s_registered)
s_registered = <the call to do the registration>;

A bit of construction-time overhead to check the flag, but hopefully,
the compiler won't throw this away.
 
M

mlimber

Kaz said:
mlimber said:
Kaz Kylheku wrote: [snip]
Speaking of which, can't you put the reference to m_registered into the
template's constructor?

template<class AbstractClass, class ConcreteClass>
class DefaultCreationPolicy
{
public:
// How about this!!
DefaultCreationPolicy()
{
(void) IsRegistered(); // touch that static member
}

static AbstractClass* Create()
// ... etc ..

Since the constructor is called somewhere, it drags in the dependency
on IsRegistered.

Unfortunately, It doesn't seem to work on VC++ 6 or gnu 3.4.1 whether
or not the constructor is inline.

Interesting. It seems that they realize that IsRegistered() does
``nothing''. After all, it only accesses a non-volatile variable and
throws away the result. Hey, since nothing else uses that variable, why
not throw it away---initializer with side effects be damned! :)

(I wonder if declaring that static member volatile would change the
behavior, hmm!)

It appears that volatile has no effect on the same compilers.
In any case, you can do your own initialization, instead of relying on
the static variable's initializer.

Just replace the (void) IsRegistered() with EnsureRegistration().
EnsureRegistration() actually does something like this:

if (!s_registered)
s_registered = <the call to do the registration>;

A bit of construction-time overhead to check the flag, but hopefully,
the compiler won't throw this away.

But if I call DefaultCreationPolicy<>::IsRegistered(), then the
automatic registration is automatically invoked making the
construction-time registration unnecessary since start-up registration
is performed. Also, with EnsureRegistration(), I'm back to manually
registering each class (whether in the client constructor or
elsewhere).

I also tried eliminating m_registered altogether and having the
constructor perform registration to no avail:

DefaultCreationPolicy()
{
const bool success = theFactory::Instance().Register(
ConcreteClass::GetID(), ConcreteClass::Create );
assert( success );
}

Thanks for your efforts. Any other ideas?

Cheers! --M
 
J

Jonathan Mcdougall

mlimber said:
Kaz said:
mlimber said:
Kaz Kylheku wrote: [snip]
Speaking of which, can't you put the reference to m_registered into the
template's constructor?

template<class AbstractClass, class ConcreteClass>
class DefaultCreationPolicy
{
public:
// How about this!!
DefaultCreationPolicy()
{
(void) IsRegistered(); // touch that static member
}

static AbstractClass* Create()
// ... etc ..

Since the constructor is called somewhere, it drags in the dependency
on IsRegistered.

Unfortunately, It doesn't seem to work on VC++ 6 or gnu 3.4.1 whether
or not the constructor is inline.

Interesting. It seems that they realize that IsRegistered() does
``nothing''. After all, it only accesses a non-volatile variable and
throws away the result. Hey, since nothing else uses that variable, why
not throw it away---initializer with side effects be damned! :)

(I wonder if declaring that static member volatile would change the
behavior, hmm!)

It appears that volatile has no effect on the same compilers.
In any case, you can do your own initialization, instead of relying on
the static variable's initializer.

Just replace the (void) IsRegistered() with EnsureRegistration().
EnsureRegistration() actually does something like this:

if (!s_registered)
s_registered = <the call to do the registration>;

A bit of construction-time overhead to check the flag, but hopefully,
the compiler won't throw this away.

But if I call DefaultCreationPolicy<>::IsRegistered(), then the
automatic registration is automatically invoked making the
construction-time registration unnecessary since start-up registration
is performed. Also, with EnsureRegistration(), I'm back to manually
registering each class (whether in the client constructor or
elsewhere).

I also tried eliminating m_registered altogether and having the
constructor perform registration to no avail:

DefaultCreationPolicy()
{
const bool success = theFactory::Instance().Register(
ConcreteClass::GetID(), ConcreteClass::Create );
assert( success );
}

This won't work. DefaultCreationPolicy is never instantiated. All you
use is static member functions and data and static member typedefs.
Thanks for your efforts. Any other ideas?

Well, I don't think my suggestion was a "maintenance hassle", since you
only have to derive from a_concrete_class instead of three different
classes. It is also less typing.

template <class Abstract, class Derived, int id>
struct a_concrete_class
: Abstract,
AssignID<id>,
DefaultCreationPolicy<Abstract, Derived>
{
a_concrete_class()
{
DefaultCreationPolicy<Abstract, Derived>::IsRegistered();
}
};

struct Derived1
: a_concrete_class<MyBase, Derived1, DERIVED_1>
{
Derived1()
{
}
};

a_concrete_class never changes. I don't see where the hassle is.


Jonathan
 
M

mlimber

Jonathan said:
mlimber wrote: [snip]
I also tried eliminating m_registered altogether and having the
constructor perform registration to no avail:

DefaultCreationPolicy()
{
const bool success = theFactory::Instance().Register(
ConcreteClass::GetID(), ConcreteClass::Create );
assert( success );
}

This won't work. DefaultCreationPolicy is never instantiated. All you
use is static member functions and data and static member typedefs.

Right, just as I indicated it wouldn't.
Well, I don't think my suggestion was a "maintenance hassle", since you
only have to derive from a_concrete_class instead of three different
classes. It is also less typing.

template <class Abstract, class Derived, int id>
struct a_concrete_class
: Abstract,
AssignID<id>,
DefaultCreationPolicy<Abstract, Derived>
{
a_concrete_class()
{
DefaultCreationPolicy<Abstract, Derived>::IsRegistered();
}
};

struct Derived1
: a_concrete_class<MyBase, Derived1, DERIVED_1>
{
Derived1()
{
}
};

a_concrete_class never changes. I don't see where the hassle is.

Oh, sorry. I didn't look closely enough. Your suggestion isn't the
maintenance hassle I made it out to be, but unfortunately it doesn't
solve the problem on VC++ 6 (sp6) or g++ 3.4.1: Derived1 and Derived2
aren't registered with the factory, implying that the call to
IsRegistered() is still optimized away.

(FYI, the two compilers I need to get this to work on are VC++ 6 and
Texas Instrument's TMS320C6x C/C++ Compiler v5.1.0. I'm just using g++
because it's more convenient than TI's and more compliant that VC6.)

Cheers! --M
 
J

Jonathan Mcdougall

mlimber said:
Jonathan said:
mlimber wrote: [snip]
I also tried eliminating m_registered altogether and having the
constructor perform registration to no avail:

DefaultCreationPolicy()
{
const bool success = theFactory::Instance().Register(
ConcreteClass::GetID(), ConcreteClass::Create );
assert( success );
}

This won't work. DefaultCreationPolicy is never instantiated. All you
use is static member functions and data and static member typedefs.

Right, just as I indicated it wouldn't.
Well, I don't think my suggestion was a "maintenance hassle", since you
only have to derive from a_concrete_class instead of three different
classes. It is also less typing.

template <class Abstract, class Derived, int id>
struct a_concrete_class
: Abstract,
AssignID<id>,
DefaultCreationPolicy<Abstract, Derived>
{
a_concrete_class()
{
DefaultCreationPolicy<Abstract, Derived>::IsRegistered();
}
};

struct Derived1
: a_concrete_class<MyBase, Derived1, DERIVED_1>
{
Derived1()
{
}
};

a_concrete_class never changes. I don't see where the hassle is.

Oh, sorry. I didn't look closely enough. Your suggestion isn't the
maintenance hassle I made it out to be, but unfortunately it doesn't
solve the problem on VC++ 6 (sp6) or g++ 3.4.1: Derived1 and Derived2
aren't registered with the factory, implying that the call to
IsRegistered() is still optimized away.

Well, I don't have Visual C++ 6, so I cannot test. The goal here is to
"trick" the optimizer which shouldn't be that difficult with this
compiler

Perhaps something with pointers to member functions in
a_concrete_class: add a dummy member function which calls
IsRegistered(), get a pointer to it in the constructor, call
a_concrete_class::bar() which calls the member function through the
pointer, or something like that. Separating the class definition from
member function definitions could also help by making it more opaque to
the compiler. Maybe if Greg Comeau comes around this thread, he'll be
able to give you nifty tricks to baffle a compiler :)
(FYI, the two compilers I need to get this to work on are VC++ 6 and
Texas Instrument's TMS320C6x C/C++ Compiler v5.1.0. I'm just using g++
because it's more convenient than TI's and more compliant that VC6.)

[Un]fortunately (depending on the pov), I've been working professionaly
only on g++ and Visual C++ 7.0 and it's been a while since I used 6.0,
so I cannot help much more here.

I think this is the kind of situation where you are getting towards the
limits of the language. IMO, going farther will only make it less
portable. I think you're in for some "maintenance hassle".


Jonathan
 
J

Jonathan Mcdougall

It is not.
Interesting. It seems that they realize that IsRegistered() does
``nothing''. After all, it only accesses a non-volatile variable and
throws away the result. Hey, since nothing else uses that variable, why
not throw it away---initializer with side effects be damned! :)

(I wonder if declaring that static member volatile would change the
behavior, hmm!)

No, volatile is a runtime feature, not compile-time. Optimization still
takes place.
In any case, you can do your own initialization, instead of relying on
the static variable's initializer.

Just replace the (void) IsRegistered() with EnsureRegistration().
EnsureRegistration() actually does something like this:

if (!s_registered)
s_registered = <the call to do the registration>;

A bit of construction-time overhead to check the flag, but hopefully,
the compiler won't throw this away.

It will, very probably. Silly optimizers :)


Jonathan
 
M

mlimber

Jonathan said:
mlimber wrote: [snip]
Oh, sorry. I didn't look closely enough. Your suggestion isn't the
maintenance hassle I made it out to be, but unfortunately it doesn't
solve the problem on VC++ 6 (sp6) or g++ 3.4.1: Derived1 and Derived2
aren't registered with the factory, implying that the call to
IsRegistered() is still optimized away.

Well, I don't have Visual C++ 6, so I cannot test. The goal here is to
"trick" the optimizer which shouldn't be that difficult with this
compiler

Perhaps something with pointers to member functions in
a_concrete_class: add a dummy member function which calls
IsRegistered(), get a pointer to it in the constructor, call
a_concrete_class::bar() which calls the member function through the
pointer, or something like that. Separating the class definition from
member function definitions could also help by making it more opaque to
the compiler. Maybe if Greg Comeau comes around this thread, he'll be
able to give you nifty tricks to baffle a compiler :)

Well, after tinkering with it for far too long, it seems that the best
option for my compilers is simply to use a virtual function (in
particular, a virtual destructor):

virtual ~DefaultCreationPolicy()
{ if( !m_registered ) exit( -1 ); }

The TI compiler, but not VC6, is "smart" enough to figure out that
"(void) IsRegistered();" in the virtual destructor should still be
eliminated, and so I need some other non-trivial usage of m_registered
to prevent that. I would use an assert, but I don't want the behavior
to change if/when debugging mode is disabled. Hence, I hand-rolled an
equivalent. Unfortunately, this carries with it the baggage of a
virtual table, but in the real application that shouldn't be an issue.
I'm open to other suggestions, but that solves the problem for now.

Thanks for all your help, Jonathan and Kaz.

Cheers! --M
 

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,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top