Template instantiation question

Discussion in 'C++' started by mlimber, Nov 3, 2005.

  1. mlimber

    mlimber Guest

    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
    mlimber, Nov 3, 2005
    #1
    1. Advertising

  2. mlimber

    mlimber Guest

    mlimber wrote:
    > 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
    mlimber, Nov 4, 2005
    #2
    1. Advertising

  3. mlimber wrote:
    > 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 <iostream>

    > // 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
    Jonathan Mcdougall, Nov 4, 2005
    #3
  4. mlimber

    Kaz Kylheku Guest

    mlimber wrote:

    > 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





    > Cheers! --Mon
    Kaz Kylheku, Nov 5, 2005
    #4
  5. mlimber

    Kaz Kylheku Guest

    Jonathan Mcdougall wrote:
    > 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.
    Kaz Kylheku, Nov 5, 2005
    #5
  6. mlimber

    mlimber Guest

    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.)

    > > 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.


    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
    mlimber, Nov 7, 2005
    #6
  7. mlimber

    Kaz Kylheku Guest

    mlimber wrote:
    > 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.

    > > 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!)

    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.
    Kaz Kylheku, Nov 7, 2005
    #7
  8. mlimber

    mlimber Guest

    Kaz Kylheku wrote:
    > mlimber wrote:
    > > 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
    mlimber, Nov 7, 2005
    #8
  9. mlimber wrote:
    > Kaz Kylheku wrote:
    > > mlimber wrote:
    > > > 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
    Jonathan Mcdougall, Nov 7, 2005
    #9
  10. mlimber

    mlimber Guest

    Jonathan Mcdougall wrote:
    > 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.

    > > 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.


    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
    mlimber, Nov 7, 2005
    #10
  11. mlimber wrote:
    > Jonathan Mcdougall wrote:
    > > 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.
    >
    > > > 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.

    >
    > 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
    Jonathan Mcdougall, Nov 7, 2005
    #11
  12. Kaz Kylheku wrote:
    > mlimber wrote:
    > > Right: the non-trivial initializer is the sticking point. Is this
    > > behavior regulated by the Standard? (Jonathan suggests not in his
    > > previous response.)


    It is not.

    > > > 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!)


    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
    Jonathan Mcdougall, Nov 7, 2005
    #12
  13. mlimber

    mlimber Guest

    Jonathan Mcdougall wrote:
    > 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
    mlimber, Nov 8, 2005
    #13
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Fernando Cuenca
    Replies:
    4
    Views:
    2,515
    Gianni Mariani
    Sep 6, 2004
  2. Thomas Maier-Komor
    Replies:
    6
    Views:
    607
    Thomas Maier-Komor
    May 19, 2005
  3. Replies:
    1
    Views:
    562
    Salt_Peter
    Dec 25, 2006
  4. Ed
    Replies:
    1
    Views:
    340
  5. Noah Roberts
    Replies:
    6
    Views:
    1,136
    Johannes Schaub (litb)
    Feb 2, 2011
Loading...

Share This Page