Mirroring template instantiations

Discussion in 'C++' started by Imre, Oct 23, 2006.

  1. Imre

    Imre Guest

    Hi

    I'm looking for a way to make sure that whenever a new instance of a
    class template A is created, then an instance of class template B is
    also created, with the same template parameters. Of course, I could do
    it by referencing B<T> from A<T>, but I'd like to do it without
    modifying A at all.
    I'm afraid it's not possile with separate translation units, but it
    might still worth asking.

    Anyway, here's an example of where this could be useful.
    Let's suppose that, based on a custom reflection system, we have an
    object editor. It's a window, with a cell for each registered member
    variables of the edited object. When constructing the editor, we create
    an EditorCell<T> for each member of type T.
    Let's further assume that we have multiple implementations of this
    object editor, using differenet GUI libraries. We do this by
    subclassing EditorCell. Say, we have EditorCellGui1<T>, and
    EditorCellGui2<T>, both derived from EditorCell<T>. (We can also have
    specializations of either the base or the derived classes, but that
    doesn't really matter now.) We also have some config variable defining
    the currently selected implementation to be used. Using this config
    var, and our reflection system, we can find the corresponding subclass
    of EditorCell<T>, and create a new instance of it.
    So when creating a new editor cell for a member of type T, we call
    EditorCell<T>::Create() (a static function), which uses the reflection
    system to find a subclass (say, EditorCellGui1<T>), and calls the
    subclass' Create() function, which returns a new instance.

    The problem is that all these classes are templates, so they are only
    instantiated if directly referenced. But EditorCellGui1<T> is never
    really referenced, so it won't be instantiated, and won't be registered
    into the reflection system, won't be found by EditorCell<T>::Create(),
    etc.

    A nice solution would be to somehow instruct the compiler to
    automatically create an EditorCellGui1<T> instance for every
    EditorCell<T> instance. Doing this by referencing EditorCellGui1<T>
    from EditorCell<T> is quite ugly, since it needs modifying EditorCell
    whenever a new gui implementation is added (and it also complicates
    dependencies). So it would be better to do it in / at the subclass
    (EditorCellGui1).

    So, my question is, how do other people do stuff like this? Any
    workarounds, alternative designs, suggestions, whatever?

    Thanks,

    Imre
    Imre, Oct 23, 2006
    #1
    1. Advertising

  2. Imre

    Jens Theisen Guest

    "Imre" <> writes:

    > So, my question is, how do other people do stuff like this? Any
    > workarounds, alternative designs, suggestions, whatever?


    Unfortunately I'm not understanding what you're getting it, which is
    in part due to confused terminology I think. Instantiation of a
    template happens, for example, if I have the expression
    EditorCell<T>::Create somewhere rather than just EditorCell<T>. This
    can't cause any registration though, since it won't instantiate any
    members unless they are also referenced (eg by taking their
    address). So what exactly do you mean here? What does the
    registration?

    --
    Cheers, Jens
    Jens Theisen, Oct 23, 2006
    #2
    1. Advertising

  3. Imre

    Imre Guest

    Jens Theisen wrote:
    > "Imre" <> writes:
    >
    > > So, my question is, how do other people do stuff like this? Any
    > > workarounds, alternative designs, suggestions, whatever?

    >
    > Unfortunately I'm not understanding what you're getting it, which is
    > in part due to confused terminology I think. Instantiation of a
    > template happens, for example, if I have the expression
    > EditorCell<T>::Create somewhere rather than just EditorCell<T>. This
    > can't cause any registration though, since it won't instantiate any
    > members unless they are also referenced (eg by taking their
    > address). So what exactly do you mean here? What does the
    > registration?


    Well, I was under the (false) impression that if any part of a class
    template is referenced / instantiated, then all static members of that
    class template will also be instantiated. This is not so.

    Anyway, let me rephrase the question (sorry if it won't be much
    clearer, english is not my native language).

    In the end, I'd like to find an appropriate subclass of EditorCell<T>,
    based on some runtime value. If it weren't a template, it would be
    easy; all subclasses could register themselves at startup (from the
    ctor of a static member), as subclasses of EditorCell. Later I could
    easily pick one of them.

    If these are class templates, then this is not so easy, as no one
    references those subclasses directly, so they are not instantiated at
    all. Only the base, EditorCell<T> is referenced, and its Create()
    should return a new instance of the right subclass, which is then used
    through virtual function calls. (As far as I know, virtual functions
    are always explicitly instantiated. I'm sure it works this way on many
    compilers, but I don't know if it's standard or compiler-dependent
    behavior.)

    Somehow I'd like to tell the compiler that whenever EditorCell<T> is
    used (say, when its ctor is instantiated) for a certain T, then a
    static member of EditorCellGui1<T> should also be instantiated
    automatically. The ctor of that static member could register the
    subclass at the base.

    So, if there's an EditorCell<int> (ctor) reference, then generate an
    EditorCellGui1<int>::staticInitializer instance as well. If there's
    EditorCell<float>, generate EditorCellGui1<float>::staticInitializer,
    etc.

    And I'd like to do this without modifying the base class at all (after
    all, that's the whole point of the registration stuff). So I'd like to
    do something at the site of the subclass implementation, that results
    in this behavior.

    Is it any clearer now?

    Imre
    Imre, Oct 24, 2006
    #3
  4. Imre wrote:
    > [..]
    > Somehow I'd like to tell the compiler that whenever EditorCell<T> is
    > used (say, when its ctor is instantiated) for a certain T, then a
    > static member of EditorCellGui1<T> should also be instantiated
    > automatically. The ctor of that static member could register the
    > subclass at the base. [..]


    You could simply take the address of the static member of EditorCellGui1
    in a constructor of EditorCell which will cause the static member to be
    instantiated. You don't have to retain the address you take (if you do
    not need it).

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Oct 24, 2006
    #4
  5. Imre

    Imre Guest

    Victor Bazarov wrote:
    > Imre wrote:
    > > [..]
    > > Somehow I'd like to tell the compiler that whenever EditorCell<T> is
    > > used (say, when its ctor is instantiated) for a certain T, then a
    > > static member of EditorCellGui1<T> should also be instantiated
    > > automatically. The ctor of that static member could register the
    > > subclass at the base. [..]

    >
    > You could simply take the address of the static member of EditorCellGui1
    > in a constructor of EditorCell which will cause the static member to be
    > instantiated. You don't have to retain the address you take (if you do
    > not need it).


    This is quite similar to my idea (calling a dummy function of the
    static member). The problem is still that I need to modify the base
    class whenever adding a new subclass. I'd like to avoid that if
    possible.

    Imre
    Imre, Oct 25, 2006
    #5
  6. Imre wrote:
    > Victor Bazarov wrote:
    >> Imre wrote:
    >>> [..]
    >>> Somehow I'd like to tell the compiler that whenever EditorCell<T> is
    >>> used (say, when its ctor is instantiated) for a certain T, then a
    >>> static member of EditorCellGui1<T> should also be instantiated
    >>> automatically. The ctor of that static member could register the
    >>> subclass at the base. [..]

    >>
    >> You could simply take the address of the static member of
    >> EditorCellGui1 in a constructor of EditorCell which will cause the
    >> static member to be instantiated. You don't have to retain the
    >> address you take (if you do not need it).

    >
    > This is quite similar to my idea (calling a dummy function of the
    > static member). The problem is still that I need to modify the base
    > class whenever adding a new subclass. I'd like to avoid that if
    > possible.


    Well, I've re-read your original post and can't claim full understanding
    of the design intent, sorry. Perhaps a complete program that does what
    you describe (even if it fails to compile or link) would be in order.

    You modify something when adding a subclass, you might as well modify
    something else... For example, the "config variable" that your class
    uses to find the proper "implemenation" to be instantiated. If you are
    adding another implementation, then the 'config' has to know about it,
    no? So, you need to make your "config" thing aware of the new derived
    class, right? Wouldn't that be the place where the derived class static
    object is instantiated?

    If the "config" is not the compile-time mechanism, then you cannot use
    compile-time polymorphism with it. You need to opt for the run-time one
    with virtual functions and so on.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Oct 25, 2006
    #6
  7. Imre

    Jens Theisen Guest

    "Imre" <> writes:

    > This is quite similar to my idea (calling a dummy function of the
    > static member). The problem is still that I need to modify the base
    > class whenever adding a new subclass. I'd like to avoid that if
    > possible.


    You can reference it anywhere you like, for example along the
    definition of EditorCellGui1 at global scope:

    InitialiserType const* const dummy = &EditorCellGui1< T >::initialiser;


    --
    Cheers, Jens
    Jens Theisen, Oct 26, 2006
    #7
  8. Jens Theisen wrote:
    > "Imre" <> writes:
    >
    >> This is quite similar to my idea (calling a dummy function of the
    >> static member). The problem is still that I need to modify the base
    >> class whenever adding a new subclass. I'd like to avoid that if
    >> possible.

    >
    > You can reference it anywhere you like, for example along the
    > definition of EditorCellGui1 at global scope:
    >
    > InitialiserType const* const dummy = &EditorCellGui1< T
    > >::initialiser;


    I think you're confused. What's "T" here?

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Oct 26, 2006
    #8
  9. Imre

    Imre Guest

    Victor Bazarov wrote:
    > Well, I've re-read your original post and can't claim full understanding
    > of the design intent, sorry. Perhaps a complete program that does what
    > you describe (even if it fails to compile or link) would be in order.


    All right, here's a complete (a bit long) example:

    // --- Base.h ---

    #ifndef Base_H
    #define Base_H

    #include <map>
    #include <iostream>

    template <typename T>
    class Base
    {
    public:
    typedef Base* (*PCreatorFunction)();

    static void RegisterCreator(int id, PCreatorFunction f)
    {
    creators[id] = f;
    }
    static Base* Create(int id)
    {
    Derived1<T>::Dummy();
    Derived2<T>::Dummy();

    PCreatorFunction cf = creators[id];
    return cf();
    }

    virtual void F() { std::cout << "Base::F()"; }

    protected:
    typedef std::map<int, PCreatorFunction> CreatorMap;
    static CreatorMap creators;
    };

    template <typename T>
    typename Base<T>::CreatorMap Base<T>::creators;


    template <typename D, typename T, int id>
    struct DerivedInitializer
    {
    DerivedInitializer()
    {
    Base<T>::RegisterCreator(id, &D::Create);
    }
    void Dummy() {}
    };

    #endif

    // --- Derived1.h ---

    #ifndef Derived1_H
    #define Derived1_H

    #include "Base.h"

    template <typename T>
    class Derived1:
    public Base<T>
    {
    public:
    static void Dummy() { initializer.Dummy(); }
    static Base<T>* Create() { return new Derived1<T>; }

    virtual void F() { std::cout << "Derived1::F()"; }

    protected:
    static DerivedInitializer<Derived1<T>, T, 1> initializer;
    };

    template <typename T>
    DerivedInitializer<Derived1<T>, T, 1> Derived1<T>::initializer;

    #endif

    // --- Derived2.h ---

    #ifndef Derived2_H
    #define Derived2_H

    #include "Base.h"

    template <typename T>
    class Derived2:
    public Base<T>
    {
    public:
    static void Dummy() { initializer.Dummy(); }
    static Base<T>* Create() { return new Derived2<T>; }

    virtual void F() { std::cout << "Derived2::F()"; }

    protected:
    static DerivedInitializer<Derived2<T>, T, 2> initializer;
    };

    template <typename T>
    DerivedInitializer<Derived2<T>, T, 2> Derived2<T>::initializer;

    #endif

    // --- main.cpp ---

    #include "Base.h"
    #include "Derived1.h"
    #include "Derived2.h"

    int main(int argc, char *argv[])
    {
    int id;

    id = 1;

    Base<int>* bi1 = Base<int>::Create(id);
    bi1->F(); // Derived1
    Base<float>* bf1 = Base<float>::Create(id);
    bf1->F(); // Derived1

    id = 2;

    Base<int>* bi2 = Base<int>::Create(id);
    bi2->F(); // Derived2

    return 0;
    }

    // ---

    This does work, but the Dummy() calls in Base::Create are ugly, because
    that list needs to be extended whenever a new subclass (say,
    Derived3<T>: public Base<T>) is added. The system cannot be cleanly
    extended without modifying existing code.
    If these were normal, non-template classes, then this would not be
    necessary, new subclasses could be added without modifying Base.

    As Jens suggested, I could place those Dummy() calls (or any other
    references that force the instantiation of the derived initializer)
    somewhere else, but that wouldn't really solve the problem. Actually,
    if those are not in Base, then things are even worse, as instead of
    referencing DerivedX<T>, I need to reference DerivedX<int>,
    DerivedX<float>, etc. separately. And if a new Derived is added, I need
    to revisit all the places where such references are written, and extend
    the list.
    (Note that Base is used with implicit instantiation. Having the client
    to explicitly instantiate the subclasses is.. at least strange.)

    Also note that the information about what instances of, say,
    Derived1::initializer should be created, are almost there. The compiler
    knows what instances of Base::Create are created (<int> and <float> in
    the above example). I'd like to tell it to create the same instances of
    the subclass initializers. Actually that's what the Dummy() calls do,
    it's just that they are at a very wrong place.

    Maybe I'm worrying too much over this; after all, adding a new line to
    Base::Create for each subclass isn't that much. But still, modifying
    existing code because of adding a new subclass somehow feels wrong.
    Also, if this pattern works for non-templates, it should work for
    templates as well.

    > You modify something when adding a subclass, you might as well modify
    > something else... For example, the "config variable" that your class
    > uses to find the proper "implemenation" to be instantiated. If you are
    > adding another implementation, then the 'config' has to know about it,
    > no?


    Not necessarily, see the example. The config var (called id in the
    example) is a simpe integer, and all subclasses are assigned an integer
    identifying them. When using the Base as a factory, we just give it a
    subclass id, and it can find the right one -- as long as subclasses are
    properly registered at startup.

    Anyway, thanks for all the answers.

    Imre
    Imre, Oct 26, 2006
    #9
    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. mpichini
    Replies:
    0
    Views:
    385
    mpichini
    Jul 1, 2003
  2. Replies:
    1
    Views:
    262
    Victor Bazarov
    Oct 19, 2005
  3. sks
    Replies:
    3
    Views:
    402
  4. sunil
    Replies:
    2
    Views:
    392
    Ian Collins
    Aug 13, 2008
  5. sunil
    Replies:
    0
    Views:
    313
    sunil
    Aug 13, 2008
Loading...

Share This Page