Design and implementation issues (templates, inheritance)

Discussion in 'C++' started by scorp007, Dec 5, 2006.

  1. scorp007

    scorp007 Guest

    Hi,

    My problem is as follows. I am trying to have each of my classes in the
    hierarchy have different behaviours which are set at compile time. I
    also want to be able to treat the classes in the hierarchy uniformly
    using polymorphism and this is where I run into problems. I'm not sure
    how to go about declaring a pointer to the base type so that this
    becomes possible.

    I dynamically create objects that fall into the types A,B or C. The
    types might be instantiated something like:
    A<TypeOne> a;
    B<TypeTwo> b;
    C<TypeOne> c;

    What then should the Base pointer type look like? Perhaps my design
    isn't all that good but I couldn't really see another nice way to do
    what I want. Inheritance wouldn't be very manageable although fairly
    easy to implement.

    The class hierarchy looks something like this:

    template<class Type>
    class Base
    {
    void function()
    {
    Type aType;
    aType.doSomething()
    }
    };

    template<class Type>
    class A: public Base<Type>
    {
    };

    template<class Type>
    class B: public Base<Type>
    {
    };

    template<class Type>
    class C: public Base<Type>
    {
    };

    class TypeOne
    {
    public:
    void doSomething()
    {
    cout << "this is type one\n";
    }
    };

    class TypeTwo
    {
    void doSomething()
    {
    cout << "this is type two\n";
    }
    };


    Thanks for any help.
    scorp007, Dec 5, 2006
    #1
    1. Advertising

  2. scorp007

    Ondra Holub Guest

    scorp007 napsal:
    > Hi,
    >
    > My problem is as follows. I am trying to have each of my classes in the
    > hierarchy have different behaviours which are set at compile time. I
    > also want to be able to treat the classes in the hierarchy uniformly
    > using polymorphism and this is where I run into problems. I'm not sure
    > how to go about declaring a pointer to the base type so that this
    > becomes possible.
    >
    > I dynamically create objects that fall into the types A,B or C. The
    > types might be instantiated something like:
    > A<TypeOne> a;
    > B<TypeTwo> b;
    > C<TypeOne> c;
    >
    > What then should the Base pointer type look like? Perhaps my design
    > isn't all that good but I couldn't really see another nice way to do
    > what I want. Inheritance wouldn't be very manageable although fairly
    > easy to implement.
    >
    > The class hierarchy looks something like this:
    >
    > template<class Type>
    > class Base
    > {
    > void function()
    > {
    > Type aType;
    > aType.doSomething()
    > }
    > };
    >
    > template<class Type>
    > class A: public Base<Type>
    > {
    > };
    >
    > template<class Type>
    > class B: public Base<Type>
    > {
    > };
    >
    > template<class Type>
    > class C: public Base<Type>
    > {
    > };
    >
    > class TypeOne
    > {
    > public:
    > void doSomething()
    > {
    > cout << "this is type one\n";
    > }
    > };
    >
    > class TypeTwo
    > {
    > void doSomething()
    > {
    > cout << "this is type two\n";
    > }
    > };
    >
    >
    > Thanks for any help.


    Your classes do NOT have any common base type, because Base<int> is
    different from Base<double>. You need inherit class Base<something>
    from some common base type:

    class ReallyBaseClass
    {
    // Some code will be here
    };

    template<class Type>
    class Base: public ReallyBaseClass
    {
    // etc.
    Ondra Holub, Dec 5, 2006
    #2
    1. Advertising

  3. scorp007

    scorp007 Guest

    On Dec 5, 9:12 pm, "Ondra Holub" <> wrote:
    > Your classes do NOT have any common base type, because Base<int> is
    > different from Base<double>. You need inherit class Base<something>
    > from some common base type:
    >
    > class ReallyBaseClass
    > {
    > // Some code will be here
    >
    > };
    > template<class Type>
    > class Base: public ReallyBaseClass
    > {
    > // etc.


    I see. How then do I implement
    void function()
    {
    Type aType;
    aType.doSomething()
    }

    In such a base class? If I remove the template argument from my current
    base class, I would then have to implement the function() in each of
    the subclasses?
    scorp007, Dec 5, 2006
    #3
  4. scorp007

    Ondra Holub Guest

    scorp007 napsal:
    > On Dec 5, 9:12 pm, "Ondra Holub" <> wrote:
    > > Your classes do NOT have any common base type, because Base<int> is
    > > different from Base<double>. You need inherit class Base<something>
    > > from some common base type:
    > >
    > > class ReallyBaseClass
    > > {
    > > // Some code will be here
    > >
    > > };
    > > template<class Type>
    > > class Base: public ReallyBaseClass
    > > {
    > > // etc.

    >
    > I see. How then do I implement
    > void function()
    > {
    > Type aType;
    > aType.doSomething()
    > }
    >
    > In such a base class? If I remove the template argument from my current
    > base class, I would then have to implement the function() in each of
    > the subclasses?


    class ReallyBaseClass
    {
    public:
    // Constructors and other stuff not here for simplicity

    virtual function() = 0;
    };

    And in templated class Base you can let it as it was:
    template<class Type>
    class Base: public ReallyBaseClass
    {
    virtual void function()
    {
    Type aType;
    aType.doSomething()
    }

    };
    Ondra Holub, Dec 5, 2006
    #4
  5. scorp007

    scorp007 Guest

    On Dec 5, 9:47 pm, "Ondra Holub" <> wrote:
    > class ReallyBaseClass
    > {
    > public:
    > // Constructors and other stuff not here for simplicity
    >
    > virtual function() = 0;
    >
    > };And in templated class Base you can let it as it was:
    > template<class Type>
    > class Base: public ReallyBaseClass
    > {
    > virtual void function()
    > {
    > Type aType;
    > aType.doSomething()
    > }
    >
    > };


    I now have an issue with calling functions that are defined only in
    BaseClass and not ReallyBaseClass. If I have pointers to
    ReallyBaseClass I still have to perform a cast to BaseClass to perform
    those function calls. How then do I cast to have the correct Type
    associated with it?
    scorp007, Dec 5, 2006
    #5
  6. scorp007

    Ondra Holub Guest

    > I now have an issue with calling functions that are defined only in
    > BaseClass and not ReallyBaseClass. If I have pointers to
    > ReallyBaseClass I still have to perform a cast to BaseClass to perform
    > those function calls. How then do I cast to have the correct Type
    > associated with it?


    If you want to call method (let's say named method()), you can either
    (a) define such function as virtual in ReallyBaseClass
    (b) if you know, that certain pointer to ReallyBaseClass points to
    instance of Base<SomeType>, you can typecast it this way:

    ReallyBaseClass* p = // get it from somewhere
    Base<SomeType>* pp = dynamic_cast<Base<SomeType>*>(p);
    if (pp == 0)
    {
    // p was not pointer to instance of Base<SomeType> (or class
    derived from it)
    }
    else
    {
    // call method of Base<SomeType>
    pp->method();
    }

    I prefer variant (a), because I do not need to perform ugly typecasts.
    In ReallyBaseClass I usualy define method() to always fail to be able
    to detect, that it was called for class, where it is nonsense:

    class ReallyBaseClass
    {
    public:
    // ... constructors and more methods

    // If you are using exceptions
    virtual void method()
    {
    throw SomeGeneralException;
    }

    // If you do not use exceptions
    virtual void method()
    {
    std::cerr << "ReallyBaseClass::method() called\n";
    exit(1);
    }
    };
    Ondra Holub, Dec 5, 2006
    #6
  7. scorp007

    scorp007 Guest

    On Dec 5, 11:09 pm, "Ondra Holub" <> wrote:
    > > I now have an issue with calling functions that are defined only in
    > > BaseClass and not ReallyBaseClass. If I have pointers to
    > > ReallyBaseClass I still have to perform a cast to BaseClass to perform
    > > those function calls. How then do I cast to have the correct Type
    > > associated with it?If you want to call method (let's say named method()), you can either

    > (a) define such function as virtual in ReallyBaseClass
    > (b) if you know, that certain pointer to ReallyBaseClass points to
    > instance of Base<SomeType>, you can typecast it this way:
    >
    > ReallyBaseClass* p = // get it from somewhere
    > Base<SomeType>* pp = dynamic_cast<Base<SomeType>*>(p);
    > if (pp == 0)
    > {
    > // p was not pointer to instance of Base<SomeType> (or class
    > derived from it)}else
    > {
    > // call method of Base<SomeType>
    > pp->method();
    >
    > }I prefer variant (a), because I do not need to perform ugly typecasts.
    > In ReallyBaseClass I usualy define method() to always fail to be able
    > to detect, that it was called for class, where it is nonsense:
    >


    I think my problem is how do I actually know what the correct SomeType
    is?
    All I have are pointers to already created objects which I then want to
    cast to the BaseClass so I can call some methods.
    scorp007, Dec 5, 2006
    #7
  8. scorp007

    Ondra Holub Guest

    scorp007 napsal:
    > On Dec 5, 11:09 pm, "Ondra Holub" <> wrote:
    > > > I now have an issue with calling functions that are defined only in
    > > > BaseClass and not ReallyBaseClass. If I have pointers to
    > > > ReallyBaseClass I still have to perform a cast to BaseClass to perform
    > > > those function calls. How then do I cast to have the correct Type
    > > > associated with it?If you want to call method (let's say named method()), you can either

    > > (a) define such function as virtual in ReallyBaseClass
    > > (b) if you know, that certain pointer to ReallyBaseClass points to
    > > instance of Base<SomeType>, you can typecast it this way:
    > >
    > > ReallyBaseClass* p = // get it from somewhere
    > > Base<SomeType>* pp = dynamic_cast<Base<SomeType>*>(p);
    > > if (pp == 0)
    > > {
    > > // p was not pointer to instance of Base<SomeType> (or class
    > > derived from it)}else
    > > {
    > > // call method of Base<SomeType>
    > > pp->method();
    > >
    > > }I prefer variant (a), because I do not need to perform ugly typecasts.
    > > In ReallyBaseClass I usualy define method() to always fail to be able
    > > to detect, that it was called for class, where it is nonsense:
    > >

    >
    > I think my problem is how do I actually know what the correct SomeType
    > is?
    > All I have are pointers to already created objects which I then want to
    > cast to the BaseClass so I can call some methods.


    If you cannot know the real type of instance, you should write the
    method as virtual method acording to (a) mentioned above. It is most
    simple and costs almost no work (less than typecasting).

    Otherwise you would have to provide your own RTTI
    (run-time-type-identification) mechanism. Something like:

    class ReallyBaseClass
    {
    public:
    ReallyBaseClass(int type): type_(type) { }

    int GetType() const { return type_; }

    static int GenerateID()
    {
    static int id = 0;
    return ++id;
    }

    private:
    int type_;
    };

    class Derived: public ReallyBaseClass
    {
    public:
    Derived(): ReallyBaseClass(ReallyBaseClass::ID) { }

    static const int ID; // This must be different for every type
    };

    const int Derived::ID = ReallyBaseClass::GenerateID();

    // Then if you have instance of ReallyBaseClass, you can do:

    ReallyBaseClass* p = // Somehow get it
    switch (p->GetType())
    {
    case Derived::ID:
    // Do something

    default:
    };

    But this is really very ugly and I do not recommend it. Variant (a)
    with virtual method is better and simpler.
    Ondra Holub, Dec 5, 2006
    #8
  9. scorp007

    scorp007 Guest

    On Dec 5, 11:32 pm, "Ondra Holub" <> wrote:
    > If you cannot know the real type of instance, you should write the
    > method as virtual method acording to (a) mentioned above. It is most
    > simple and costs almost no work (less than typecasting).
    >
    > Otherwise you would have to provide your own RTTI
    > (run-time-type-identification) mechanism. Something like:
    >
    > class ReallyBaseClass
    > {
    > public:
    > ReallyBaseClass(int type): type_(type) { }
    >
    > int GetType() const { return type_; }
    >
    > static int GenerateID()
    > {
    > static int id = 0;
    > return ++id;
    > }
    >
    > private:
    > int type_;
    >
    > };class Derived: public ReallyBaseClass
    > {
    > public:
    > Derived(): ReallyBaseClass(ReallyBaseClass::ID) { }
    >
    > static const int ID; // This must be different for every type
    >
    > };const int Derived::ID = ReallyBaseClass::GenerateID();
    >
    > // Then if you have instance of ReallyBaseClass, you can do:
    >
    > ReallyBaseClass* p = // Somehow get it
    > switch (p->GetType())
    > {
    > case Derived::ID:
    > // Do something
    >
    > default:
    >
    > };But this is really very ugly and I do not recommend it. Variant (a)
    > with virtual method is better and simpler.


    Is there really no other way? Both those methods don't look very
    appealing to me. On one hand I would have to include functionality in
    classes that has no real reason to be there, on the other I would have
    to do a lot of work which shouldn't need to be done with
    object-oriented code. I'm quite willing to rethink my design if it is
    seriously bad but I need some pointers as to the correct way to lay
    things out.

    Thanks for the help so far.
    scorp007, Dec 5, 2006
    #9
  10. scorp007

    Bo Yang Guest

    scorp007 :
    > On Dec 5, 11:32 pm, "Ondra Holub" <> wrote:
    >> If you cannot know the real type of instance, you should write the
    >> method as virtual method acording to (a) mentioned above. It is most
    >> simple and costs almost no work (less than typecasting).
    >>
    >> Otherwise you would have to provide your own RTTI
    >> (run-time-type-identification) mechanism. Something like:
    >>
    >> class ReallyBaseClass
    >> {
    >> public:
    >> ReallyBaseClass(int type): type_(type) { }
    >>
    >> int GetType() const { return type_; }
    >>
    >> static int GenerateID()
    >> {
    >> static int id = 0;
    >> return ++id;
    >> }
    >>
    >> private:
    >> int type_;
    >>
    >> };class Derived: public ReallyBaseClass
    >> {
    >> public:
    >> Derived(): ReallyBaseClass(ReallyBaseClass::ID) { }
    >>
    >> static const int ID; // This must be different for every type
    >>
    >> };const int Derived::ID = ReallyBaseClass::GenerateID();
    >>
    >> // Then if you have instance of ReallyBaseClass, you can do:
    >>
    >> ReallyBaseClass* p = // Somehow get it
    >> switch (p->GetType())
    >> {
    >> case Derived::ID:
    >> // Do something
    >>
    >> default:
    >>
    >> };But this is really very ugly and I do not recommend it. Variant (a)
    >> with virtual method is better and simpler.

    >
    > Is there really no other way? Both those methods don't look very
    > appealing to me. On one hand I would have to include functionality in
    > classes that has no real reason to be there, on the other I would have
    > to do a lot of work which shouldn't need to be done with
    > object-oriented code. I'm quite willing to rethink my design if it is
    > seriously bad but I need some pointers as to the correct way to lay
    > things out.
    >
    > Thanks for the help so far.
    >

    But, doesn't Ondra's method work?
    Bo Yang, Dec 9, 2006
    #10
    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. maxw_cc
    Replies:
    1
    Views:
    3,112
    Martijn van Steenbergen
    Dec 21, 2003
  2. Markus Seeger
    Replies:
    1
    Views:
    325
    Markus Seeger
    Feb 20, 2004
  3. JKop
    Replies:
    3
    Views:
    441
  4. recover
    Replies:
    2
    Views:
    779
    recover
    Jul 25, 2006
  5. Daniel Pitts
    Replies:
    27
    Views:
    1,864
    Mike Schilling
    Feb 27, 2008
Loading...

Share This Page